Init HttpClient with each Api Request. Guarg agsint connectivity issues when making API calls.

This commit is contained in:
Kyle Spearrin 2016-07-01 18:54:00 -04:00
parent a4e6f49959
commit 7d62a89a51
15 changed files with 402 additions and 172 deletions

View File

@ -10,6 +10,8 @@ using Bit.App.Abstractions;
using XLabs.Ioc; using XLabs.Ioc;
using Plugin.Fingerprint.Abstractions; using Plugin.Fingerprint.Abstractions;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions;
using Acr.UserDialogs;
namespace Bit.Android namespace Bit.Android
{ {
@ -25,6 +27,8 @@ namespace Bit.Android
LoadApplication(new App.App( LoadApplication(new App.App(
Resolver.Resolve<IAuthService>(), Resolver.Resolve<IAuthService>(),
Resolver.Resolve<IConnectivity>(),
Resolver.Resolve<IUserDialogs>(),
Resolver.Resolve<IDatabaseService>(), Resolver.Resolve<IDatabaseService>(),
Resolver.Resolve<ISyncService>(), Resolver.Resolve<ISyncService>(),
Resolver.Resolve<IFingerprint>(), Resolver.Resolve<IFingerprint>(),

View File

@ -14,6 +14,6 @@ namespace Bit.App.Abstractions
Task<ApiResult<ListResponse<TResponse>>> GetAsync(); Task<ApiResult<ListResponse<TResponse>>> GetAsync();
Task<ApiResult<TResponse>> PostAsync(TRequest requestObj); Task<ApiResult<TResponse>> PostAsync(TRequest requestObj);
Task<ApiResult<TResponse>> PutAsync(TId id, TRequest requestObj); Task<ApiResult<TResponse>> PutAsync(TId id, TRequest requestObj);
Task<ApiResult<object>> DeleteAsync(TId id); Task<ApiResult> DeleteAsync(TId id);
} }
} }

View File

@ -10,12 +10,17 @@ using Plugin.Fingerprint.Abstractions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
using Bit.App.Controls; using Bit.App.Controls;
using Plugin.Connectivity.Abstractions;
using System.Net;
using Acr.UserDialogs;
namespace Bit.App namespace Bit.App
{ {
public class App : Application public class App : Application
{ {
private readonly IDatabaseService _databaseService; private readonly IDatabaseService _databaseService;
private readonly IConnectivity _connectivity;
private readonly IUserDialogs _userDialogs;
private readonly ISyncService _syncService; private readonly ISyncService _syncService;
private readonly IAuthService _authService; private readonly IAuthService _authService;
private readonly IFingerprint _fingerprint; private readonly IFingerprint _fingerprint;
@ -23,12 +28,16 @@ namespace Bit.App
public App( public App(
IAuthService authService, IAuthService authService,
IConnectivity connectivity,
IUserDialogs userDialogs,
IDatabaseService databaseService, IDatabaseService databaseService,
ISyncService syncService, ISyncService syncService,
IFingerprint fingerprint, IFingerprint fingerprint,
ISettings settings) ISettings settings)
{ {
_databaseService = databaseService; _databaseService = databaseService;
_connectivity = connectivity;
_userDialogs = userDialogs;
_syncService = syncService; _syncService = syncService;
_authService = authService; _authService = authService;
_fingerprint = fingerprint; _fingerprint = fingerprint;
@ -47,9 +56,36 @@ namespace Bit.App
MessagingCenter.Subscribe<Application, bool>(Current, "Resumed", async (sender, args) => MessagingCenter.Subscribe<Application, bool>(Current, "Resumed", async (sender, args) =>
{ {
var syncTask = _syncService.IncrementalSyncAsync();
await CheckLockAsync(args); await CheckLockAsync(args);
await syncTask; if(_connectivity.IsConnected)
{
var attempt = 0;
do
{
try
{
await _syncService.IncrementalSyncAsync();
break;
}
catch(WebException)
{
Debug.WriteLine("Failed to sync.");
if(attempt >= 1)
{
await _userDialogs.AlertAsync("Unable to automatically sync.");
}
else
{
await Task.Delay(1000);
}
attempt++;
}
} while(attempt <= 1);
}
else
{
Debug.WriteLine("Not connected.");
}
}); });
MessagingCenter.Subscribe<Application, bool>(Current, "Lock", async (sender, args) => MessagingCenter.Subscribe<Application, bool>(Current, "Lock", async (sender, args) =>
@ -58,12 +94,35 @@ namespace Bit.App
}); });
} }
protected override void OnStart() protected async override void OnStart()
{ {
// Handle when your app starts // Handle when your app starts
var lockTask = CheckLockAsync(false); await CheckLockAsync(false);
_databaseService.CreateTables(); _databaseService.CreateTables();
var syncTask = _syncService.FullSyncAsync(); if(_connectivity.IsConnected)
{
var attempt = 0;
do
{
try
{
await _syncService.FullSyncAsync();
break;
}
catch(WebException)
{
if(attempt >= 1)
{
await _userDialogs.AlertAsync("Unable to automatically sync. Manual sync required.");
}
else
{
await Task.Delay(1000);
}
attempt++;
}
} while(attempt <= 1);
}
Debug.WriteLine("OnStart"); Debug.WriteLine("OnStart");
} }
@ -79,14 +138,14 @@ namespace Bit.App
} }
} }
protected override void OnResume() protected async override void OnResume()
{ {
// Handle when your app resumes // Handle when your app resumes
Debug.WriteLine("OnResume"); Debug.WriteLine("OnResume");
if(Device.OS == TargetPlatform.Android) if(Device.OS == TargetPlatform.Android)
{ {
var task = CheckLockAsync(false); await CheckLockAsync(false);
} }
var lockPinPage = Current.MainPage.Navigation.ModalStack.LastOrDefault() as LockPinPage; var lockPinPage = Current.MainPage.Navigation.ModalStack.LastOrDefault() as LockPinPage;

View File

@ -157,6 +157,7 @@
<Compile Include="Pages\VaultListSitesPage.cs" /> <Compile Include="Pages\VaultListSitesPage.cs" />
<Compile Include="Utilities\Extentions.cs" /> <Compile Include="Utilities\Extentions.cs" />
<Compile Include="Utilities\ExtendedObservableCollection.cs" /> <Compile Include="Utilities\ExtendedObservableCollection.cs" />
<Compile Include="Utilities\ApiHttpClient.cs" />
<Compile Include="Utilities\TokenHttpRequestMessage.cs" /> <Compile Include="Utilities\TokenHttpRequestMessage.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -12,6 +12,7 @@ using XLabs.Ioc;
using Bit.App.Utilities; using Bit.App.Utilities;
using PushNotification.Plugin.Abstractions; using PushNotification.Plugin.Abstractions;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@ -20,6 +21,7 @@ namespace Bit.App.Pages
private readonly IFolderService _folderService; private readonly IFolderService _folderService;
private readonly ISiteService _siteService; private readonly ISiteService _siteService;
private readonly IUserDialogs _userDialogs; private readonly IUserDialogs _userDialogs;
private readonly IConnectivity _connectivity;
private readonly IClipboardService _clipboardService; private readonly IClipboardService _clipboardService;
private readonly IPushNotification _pushNotification; private readonly IPushNotification _pushNotification;
private readonly ISettings _settings; private readonly ISettings _settings;
@ -30,6 +32,7 @@ namespace Bit.App.Pages
_favorites = favorites; _favorites = favorites;
_folderService = Resolver.Resolve<IFolderService>(); _folderService = Resolver.Resolve<IFolderService>();
_siteService = Resolver.Resolve<ISiteService>(); _siteService = Resolver.Resolve<ISiteService>();
_connectivity = Resolver.Resolve<IConnectivity>();
_userDialogs = Resolver.Resolve<IUserDialogs>(); _userDialogs = Resolver.Resolve<IUserDialogs>();
_clipboardService = Resolver.Resolve<IClipboardService>(); _clipboardService = Resolver.Resolve<IClipboardService>();
_pushNotification = Resolver.Resolve<IPushNotification>(); _pushNotification = Resolver.Resolve<IPushNotification>();
@ -77,7 +80,7 @@ namespace Bit.App.Pages
base.OnAppearing(); base.OnAppearing();
LoadFoldersAsync().Wait(); LoadFoldersAsync().Wait();
if(Device.OS == TargetPlatform.iOS && !_favorites) if(_connectivity.IsConnected && Device.OS == TargetPlatform.iOS && !_favorites)
{ {
if(!_settings.GetValueOrDefault<bool>(Constants.PushPromptShown)) if(!_settings.GetValueOrDefault<bool>(Constants.PushPromptShown))
{ {

View File

@ -3,22 +3,34 @@ using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
public class AccountsApiRepository : BaseApiRepository, IAccountsApiRepository public class AccountsApiRepository : BaseApiRepository, IAccountsApiRepository
{ {
public AccountsApiRepository(IConnectivity connectivity)
: base(connectivity)
{ }
protected override string ApiRoute => "accounts"; protected override string ApiRoute => "accounts";
public virtual async Task<ApiResult> PostRegisterAsync(RegisterRequest requestObj) public virtual async Task<ApiResult> PostRegisterAsync(RegisterRequest requestObj)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage(requestObj) var requestMessage = new TokenHttpRequestMessage(requestObj)
{ {
Method = HttpMethod.Post, Method = HttpMethod.Post,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/register")), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/register")),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync(response); return await HandleErrorAsync(response);
@ -28,3 +40,4 @@ namespace Bit.App.Repositories
} }
} }
} }
}

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
@ -13,18 +14,26 @@ namespace Bit.App.Repositories
where TRequest : class where TRequest : class
where TResponse : class where TResponse : class
{ {
public ApiRepository() public ApiRepository(IConnectivity connectivity)
: base(connectivity)
{ } { }
public virtual async Task<ApiResult<TResponse>> GetByIdAsync(TId id) public virtual async Task<ApiResult<TResponse>> GetByIdAsync(TId id)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<TResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage() var requestMessage = new TokenHttpRequestMessage()
{ {
Method = HttpMethod.Get, Method = HttpMethod.Get,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/", id)), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/", id)),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<TResponse>(response); return await HandleErrorAsync<TResponse>(response);
@ -34,16 +43,24 @@ namespace Bit.App.Repositories
var responseObj = JsonConvert.DeserializeObject<TResponse>(responseContent); var responseObj = JsonConvert.DeserializeObject<TResponse>(responseContent);
return ApiResult<TResponse>.Success(responseObj, response.StatusCode); return ApiResult<TResponse>.Success(responseObj, response.StatusCode);
} }
}
public virtual async Task<ApiResult<ListResponse<TResponse>>> GetAsync() public virtual async Task<ApiResult<ListResponse<TResponse>>> GetAsync()
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<ListResponse<TResponse>>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage() var requestMessage = new TokenHttpRequestMessage()
{ {
Method = HttpMethod.Get, Method = HttpMethod.Get,
RequestUri = new Uri(Client.BaseAddress, ApiRoute), RequestUri = new Uri(client.BaseAddress, ApiRoute),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<ListResponse<TResponse>>(response); return await HandleErrorAsync<ListResponse<TResponse>>(response);
@ -53,16 +70,24 @@ namespace Bit.App.Repositories
var responseObj = JsonConvert.DeserializeObject<ListResponse<TResponse>>(responseContent); var responseObj = JsonConvert.DeserializeObject<ListResponse<TResponse>>(responseContent);
return ApiResult<ListResponse<TResponse>>.Success(responseObj, response.StatusCode); return ApiResult<ListResponse<TResponse>>.Success(responseObj, response.StatusCode);
} }
}
public virtual async Task<ApiResult<TResponse>> PostAsync(TRequest requestObj) public virtual async Task<ApiResult<TResponse>> PostAsync(TRequest requestObj)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<TResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage(requestObj) var requestMessage = new TokenHttpRequestMessage(requestObj)
{ {
Method = HttpMethod.Post, Method = HttpMethod.Post,
RequestUri = new Uri(Client.BaseAddress, ApiRoute), RequestUri = new Uri(client.BaseAddress, ApiRoute),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<TResponse>(response); return await HandleErrorAsync<TResponse>(response);
@ -72,16 +97,24 @@ namespace Bit.App.Repositories
var responseObj = JsonConvert.DeserializeObject<TResponse>(responseContent); var responseObj = JsonConvert.DeserializeObject<TResponse>(responseContent);
return ApiResult<TResponse>.Success(responseObj, response.StatusCode); return ApiResult<TResponse>.Success(responseObj, response.StatusCode);
} }
}
public virtual async Task<ApiResult<TResponse>> PutAsync(TId id, TRequest requestObj) public virtual async Task<ApiResult<TResponse>> PutAsync(TId id, TRequest requestObj)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<TResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage(requestObj) var requestMessage = new TokenHttpRequestMessage(requestObj)
{ {
Method = HttpMethod.Put, Method = HttpMethod.Put,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/", id)), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/", id)),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<TResponse>(response); return await HandleErrorAsync<TResponse>(response);
@ -91,22 +124,31 @@ namespace Bit.App.Repositories
var responseObj = JsonConvert.DeserializeObject<TResponse>(responseContent); var responseObj = JsonConvert.DeserializeObject<TResponse>(responseContent);
return ApiResult<TResponse>.Success(responseObj, response.StatusCode); return ApiResult<TResponse>.Success(responseObj, response.StatusCode);
} }
}
public virtual async Task<ApiResult<object>> DeleteAsync(TId id) public virtual async Task<ApiResult> DeleteAsync(TId id)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage() var requestMessage = new TokenHttpRequestMessage()
{ {
Method = HttpMethod.Delete, Method = HttpMethod.Delete,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/", id)), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/", id)),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<object>(response); return await HandleErrorAsync(response);
} }
return ApiResult<object>.Success(null, response.StatusCode); return ApiResult.Success(response.StatusCode);
}
} }
} }
} }

View File

@ -4,22 +4,34 @@ using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
public class AuthApiRepository : BaseApiRepository, IAuthApiRepository public class AuthApiRepository : BaseApiRepository, IAuthApiRepository
{ {
public AuthApiRepository(IConnectivity connectivity)
: base(connectivity)
{ }
protected override string ApiRoute => "auth"; protected override string ApiRoute => "auth";
public virtual async Task<ApiResult<TokenResponse>> PostTokenAsync(TokenRequest requestObj) public virtual async Task<ApiResult<TokenResponse>> PostTokenAsync(TokenRequest requestObj)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<TokenResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage(requestObj) var requestMessage = new TokenHttpRequestMessage(requestObj)
{ {
Method = HttpMethod.Post, Method = HttpMethod.Post,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/token")), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/token")),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<TokenResponse>(response); return await HandleErrorAsync<TokenResponse>(response);
@ -29,16 +41,24 @@ namespace Bit.App.Repositories
var responseObj = JsonConvert.DeserializeObject<TokenResponse>(responseContent); var responseObj = JsonConvert.DeserializeObject<TokenResponse>(responseContent);
return ApiResult<TokenResponse>.Success(responseObj, response.StatusCode); return ApiResult<TokenResponse>.Success(responseObj, response.StatusCode);
} }
}
public virtual async Task<ApiResult<TokenResponse>> PostTokenTwoFactorAsync(TokenTwoFactorRequest requestObj) public virtual async Task<ApiResult<TokenResponse>> PostTokenTwoFactorAsync(TokenTwoFactorRequest requestObj)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<TokenResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage(requestObj) var requestMessage = new TokenHttpRequestMessage(requestObj)
{ {
Method = HttpMethod.Post, Method = HttpMethod.Post,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/token/two-factor")), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/token/two-factor")),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<TokenResponse>(response); return await HandleErrorAsync<TokenResponse>(response);
@ -50,3 +70,4 @@ namespace Bit.App.Repositories
} }
} }
} }
}

View File

@ -1,27 +1,34 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using ModernHttpClient;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
public abstract class BaseApiRepository public abstract class BaseApiRepository
{ {
public BaseApiRepository() public BaseApiRepository(IConnectivity connectivity)
{ {
Client = new HttpClient(new NativeMessageHandler()); Connectivity = connectivity;
Client.BaseAddress = new Uri("https://api.bitwarden.com");
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
} }
protected virtual HttpClient Client { get; private set; } protected IConnectivity Connectivity { get; private set; }
protected abstract string ApiRoute { get; } protected abstract string ApiRoute { get; }
public async Task<ApiResult<T>> HandleErrorAsync<T>(HttpResponseMessage response) protected ApiResult HandledNotConnected()
{
return ApiResult.Failed(System.Net.HttpStatusCode.RequestTimeout, new ApiError { Message = "Not connected to the internet." });
}
protected ApiResult<T> HandledNotConnected<T>()
{
return ApiResult<T>.Failed(System.Net.HttpStatusCode.RequestTimeout, new ApiError { Message = "Not connected to the internet." });
}
protected async Task<ApiResult<T>> HandleErrorAsync<T>(HttpResponseMessage response)
{ {
try try
{ {
@ -34,7 +41,7 @@ namespace Bit.App.Repositories
return ApiResult<T>.Failed(response.StatusCode, new ApiError { Message = "An unknown error has occured." }); return ApiResult<T>.Failed(response.StatusCode, new ApiError { Message = "An unknown error has occured." });
} }
public async Task<ApiResult> HandleErrorAsync(HttpResponseMessage response) protected async Task<ApiResult> HandleErrorAsync(HttpResponseMessage response)
{ {
try try
{ {

View File

@ -5,22 +5,34 @@ using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
public class CipherApiRepository : BaseApiRepository, ICipherApiRepository public class CipherApiRepository : BaseApiRepository, ICipherApiRepository
{ {
public CipherApiRepository(IConnectivity connectivity)
: base(connectivity)
{ }
protected override string ApiRoute => "ciphers"; protected override string ApiRoute => "ciphers";
public virtual async Task<ApiResult<CipherResponse>> GetByIdAsync(string id) public virtual async Task<ApiResult<CipherResponse>> GetByIdAsync(string id)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<CipherResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage() var requestMessage = new TokenHttpRequestMessage()
{ {
Method = HttpMethod.Get, Method = HttpMethod.Get,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/", id)), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/", id)),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<CipherResponse>(response); return await HandleErrorAsync<CipherResponse>(response);
@ -30,16 +42,24 @@ namespace Bit.App.Repositories
var responseObj = JsonConvert.DeserializeObject<CipherResponse>(responseContent); var responseObj = JsonConvert.DeserializeObject<CipherResponse>(responseContent);
return ApiResult<CipherResponse>.Success(responseObj, response.StatusCode); return ApiResult<CipherResponse>.Success(responseObj, response.StatusCode);
} }
}
public virtual async Task<ApiResult<ListResponse<CipherResponse>>> GetAsync() public virtual async Task<ApiResult<ListResponse<CipherResponse>>> GetAsync()
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<ListResponse<CipherResponse>>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage() var requestMessage = new TokenHttpRequestMessage()
{ {
Method = HttpMethod.Get, Method = HttpMethod.Get,
RequestUri = new Uri(Client.BaseAddress, ApiRoute), RequestUri = new Uri(client.BaseAddress, ApiRoute),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<ListResponse<CipherResponse>>(response); return await HandleErrorAsync<ListResponse<CipherResponse>>(response);
@ -49,16 +69,24 @@ namespace Bit.App.Repositories
var responseObj = JsonConvert.DeserializeObject<ListResponse<CipherResponse>>(responseContent); var responseObj = JsonConvert.DeserializeObject<ListResponse<CipherResponse>>(responseContent);
return ApiResult<ListResponse<CipherResponse>>.Success(responseObj, response.StatusCode); return ApiResult<ListResponse<CipherResponse>>.Success(responseObj, response.StatusCode);
} }
}
public virtual async Task<ApiResult<CipherHistoryResponse>> GetByRevisionDateWithHistoryAsync(DateTime since) public virtual async Task<ApiResult<CipherHistoryResponse>> GetByRevisionDateWithHistoryAsync(DateTime since)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<CipherHistoryResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage() var requestMessage = new TokenHttpRequestMessage()
{ {
Method = HttpMethod.Get, Method = HttpMethod.Get,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/history", "?since=", since)), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/history", "?since=", since)),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<CipherHistoryResponse>(response); return await HandleErrorAsync<CipherHistoryResponse>(response);
@ -70,3 +98,4 @@ namespace Bit.App.Repositories
} }
} }
} }
}

View File

@ -4,22 +4,34 @@ using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
public class DeviceApiRepository : ApiRepository<DeviceRequest, DeviceResponse, string>, IDeviceApiRepository public class DeviceApiRepository : ApiRepository<DeviceRequest, DeviceResponse, string>, IDeviceApiRepository
{ {
public DeviceApiRepository(IConnectivity connectivity)
: base(connectivity)
{ }
protected override string ApiRoute => "devices"; protected override string ApiRoute => "devices";
public virtual async Task<ApiResult<DeviceResponse>> PutTokenAsync(string identifier, DeviceTokenRequest request) public virtual async Task<ApiResult<DeviceResponse>> PutTokenAsync(string identifier, DeviceTokenRequest request)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<DeviceResponse>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage(request) var requestMessage = new TokenHttpRequestMessage(request)
{ {
Method = HttpMethod.Put, Method = HttpMethod.Put,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/identifier/", identifier, "/token")), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/identifier/", identifier, "/token")),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<DeviceResponse>(response); return await HandleErrorAsync<DeviceResponse>(response);
@ -31,3 +43,4 @@ namespace Bit.App.Repositories
} }
} }
} }
}

View File

@ -5,22 +5,34 @@ using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
public class FolderApiRepository : ApiRepository<FolderRequest, FolderResponse, string>, IFolderApiRepository public class FolderApiRepository : ApiRepository<FolderRequest, FolderResponse, string>, IFolderApiRepository
{ {
public FolderApiRepository(IConnectivity connectivity)
: base(connectivity)
{ }
protected override string ApiRoute => "folders"; protected override string ApiRoute => "folders";
public virtual async Task<ApiResult<ListResponse<FolderResponse>>> GetByRevisionDateAsync(DateTime since) public virtual async Task<ApiResult<ListResponse<FolderResponse>>> GetByRevisionDateAsync(DateTime since)
{
if(!Connectivity.IsConnected)
{
return HandledNotConnected<ListResponse<FolderResponse>>();
}
using(var client = new ApiHttpClient())
{ {
var requestMessage = new TokenHttpRequestMessage() var requestMessage = new TokenHttpRequestMessage()
{ {
Method = HttpMethod.Get, Method = HttpMethod.Get,
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "?since=", since)), RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "?since=", since)),
}; };
var response = await Client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)
{ {
return await HandleErrorAsync<ListResponse<FolderResponse>>(response); return await HandleErrorAsync<ListResponse<FolderResponse>>(response);
@ -32,3 +44,4 @@ namespace Bit.App.Repositories
} }
} }
} }
}

View File

@ -5,11 +5,16 @@ using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Models.Api; using Bit.App.Models.Api;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Connectivity.Abstractions;
namespace Bit.App.Repositories namespace Bit.App.Repositories
{ {
public class SiteApiRepository : ApiRepository<SiteRequest, SiteResponse, string>, ISiteApiRepository public class SiteApiRepository : ApiRepository<SiteRequest, SiteResponse, string>, ISiteApiRepository
{ {
public SiteApiRepository(IConnectivity connectivity)
: base(connectivity)
{ }
protected override string ApiRoute => "sites"; protected override string ApiRoute => "sites";
} }
} }

View File

@ -0,0 +1,17 @@
using System.Net.Http;
using ModernHttpClient;
using System;
using System.Net.Http.Headers;
namespace Bit.App
{
public class ApiHttpClient : HttpClient
{
public ApiHttpClient()
: base(new NativeMessageHandler())
{
BaseAddress = new Uri("https://api.bitwarden.com");
DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
}

View File

@ -22,6 +22,7 @@ using Bit.App;
using Bit.iOS.Core.Services; using Bit.iOS.Core.Services;
using PushNotification.Plugin; using PushNotification.Plugin;
using Plugin.DeviceInfo; using Plugin.DeviceInfo;
using Plugin.Connectivity.Abstractions;
namespace Bit.iOS namespace Bit.iOS
{ {
@ -43,6 +44,8 @@ namespace Bit.iOS
LoadApplication(new App.App( LoadApplication(new App.App(
Resolver.Resolve<IAuthService>(), Resolver.Resolve<IAuthService>(),
Resolver.Resolve<IConnectivity>(),
Resolver.Resolve<IUserDialogs>(),
Resolver.Resolve<IDatabaseService>(), Resolver.Resolve<IDatabaseService>(),
Resolver.Resolve<ISyncService>(), Resolver.Resolve<ISyncService>(),
Resolver.Resolve<IFingerprint>(), Resolver.Resolve<IFingerprint>(),