From f67b700d7db17c0d36c028ee6853d9167c496bef Mon Sep 17 00:00:00 2001 From: Luka Grabarevic Date: Tue, 31 Jul 2018 11:49:00 +0200 Subject: [PATCH] Fixed settings issues with deleted build definitions (#31) * Server settings edit mode now closes when saving * Improved handling when selecting build definitions. * Imporved handling of deleted build definitions. * Improved logging and handling of unsuccessful requests * Fixed some issues with handling of edit mode in server settings * fix possible stackoverflow exception --- .../Base/ViewModel/ProviderViewModelBase.cs | 19 +++- .../SubViewModels/BuildDefinitionViewModel.cs | 11 ++ .../BuildsAppReborn.Access.UI.csproj | 1 + .../TfsBuildProviderViewModelBase.cs | 104 ++++++++++-------- .../TFS/Views/TfsBuildProviderViewBase.xaml | 13 ++- BuildsAppReborn.Access/BuildMonitor.cs | 2 +- .../TFS/TfsBuildProviderBase.cs | 88 +++++++-------- .../BuildTimeToElapsedTimeConverter.cs | 5 + .../Settings/ServerSettingsViewModel.cs | 14 +++ .../Views/ServerSettingsControl.xaml | 13 ++- .../IBuildProviderViewModel.cs | 7 +- .../AdvancedHttpRequestException.cs | 29 +++++ .../BuildsAppReborn.Infrastructure.csproj | 2 + .../HttpResponseMessageExtensions.cs | 22 ++++ 14 files changed, 226 insertions(+), 104 deletions(-) create mode 100644 BuildsAppReborn.Infrastructure/AdvancedHttpRequestException.cs create mode 100644 BuildsAppReborn.Infrastructure/HttpResponseMessageExtensions.cs diff --git a/BuildsAppReborn.Access.UI/Base/ViewModel/ProviderViewModelBase.cs b/BuildsAppReborn.Access.UI/Base/ViewModel/ProviderViewModelBase.cs index ddd87fe..2339c40 100644 --- a/BuildsAppReborn.Access.UI/Base/ViewModel/ProviderViewModelBase.cs +++ b/BuildsAppReborn.Access.UI/Base/ViewModel/ProviderViewModelBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.ComponentModel.Composition; using System.Linq; using BuildsAppReborn.Contracts; @@ -14,9 +15,19 @@ internal abstract class ProviderViewModelBase : ViewModelBase, IBuildProviderVie { public abstract String DisplayName { get; protected set; } + public Boolean IsInEditMode + { + get { return this.isInEditMode; } + set + { + this.isInEditMode = value; + OnPropertyChanged(); + } + } + public BuildMonitorSettings MonitorSettings { get; private set; } - public abstract IEnumerable SelectedBuildDefinitions { get; } + public abstract ObservableCollection SelectedBuildDefinitions { get; } public Boolean SupportsDefaultCredentials => BuildProviderMetaData.SupportedAuthenticationModes.HasFlag(AuthenticationModes.Default); @@ -57,6 +68,10 @@ public virtual void OnImportsSatisfied() } } + public virtual void Save() + { + } + protected IBuildProvider BuildProvider { get; private set; } protected IBuildProviderMetadata BuildProviderMetaData { get; private set; } @@ -69,6 +84,8 @@ protected virtual void OnInitialized() #pragma warning disable 649 private Lazy[] buildProviders; + private Boolean isInEditMode; + #pragma warning restore 649 } } \ No newline at end of file diff --git a/BuildsAppReborn.Access.UI/Base/ViewModel/SubViewModels/BuildDefinitionViewModel.cs b/BuildsAppReborn.Access.UI/Base/ViewModel/SubViewModels/BuildDefinitionViewModel.cs index 545868d..6b1213d 100644 --- a/BuildsAppReborn.Access.UI/Base/ViewModel/SubViewModels/BuildDefinitionViewModel.cs +++ b/BuildsAppReborn.Access.UI/Base/ViewModel/SubViewModels/BuildDefinitionViewModel.cs @@ -18,6 +18,16 @@ public BuildDefinitionViewModel(IBuildDefinition buildDefinition) public IBuildDefinition BuildDefinition { get; } + public Boolean IsDeleted + { + get { return this.isDeleted; } + set + { + this.isDeleted = value; + OnPropertyChanged(); + } + } + public Boolean IsSelected { get { return this.isSelected; } @@ -29,6 +39,7 @@ public Boolean IsSelected } public String Name => BuildDefinition.Name; + private Boolean isDeleted; private Boolean isSelected; } diff --git a/BuildsAppReborn.Access.UI/BuildsAppReborn.Access.UI.csproj b/BuildsAppReborn.Access.UI/BuildsAppReborn.Access.UI.csproj index 94ab16c..0f42f98 100644 --- a/BuildsAppReborn.Access.UI/BuildsAppReborn.Access.UI.csproj +++ b/BuildsAppReborn.Access.UI/BuildsAppReborn.Access.UI.csproj @@ -40,6 +40,7 @@ + 4.0 diff --git a/BuildsAppReborn.Access.UI/TFS/ViewModel/TfsBuildProviderViewModelBase.cs b/BuildsAppReborn.Access.UI/TFS/ViewModel/TfsBuildProviderViewModelBase.cs index 632e3a9..28063ce 100644 --- a/BuildsAppReborn.Access.UI/TFS/ViewModel/TfsBuildProviderViewModelBase.cs +++ b/BuildsAppReborn.Access.UI/TFS/ViewModel/TfsBuildProviderViewModelBase.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading.Tasks; using System.Windows; using BuildsAppReborn.Access.UI.ViewModel.SubViewModels; @@ -78,7 +78,7 @@ public String ProjectUrl } } - public override IEnumerable SelectedBuildDefinitions => new ObservableCollection(MonitorSettings.SelectedBuildDefinitions); + public override ObservableCollection SelectedBuildDefinitions => this.selectedBuildDefinitions; public Boolean ShowPersonalAccessTokenInput { @@ -102,6 +102,26 @@ public String StatusText public override String Url => ProjectUrl; + public override void Save() + { + if (IsInEditMode) + { + base.Save(); + + MonitorSettings.SelectedBuildDefinitions.Clear(); + var selected = BuildDefinitions.Where(a => a.IsSelected && !a.IsDeleted); + foreach (var viewModel in selected) + { + MonitorSettings.SelectedBuildDefinitions.Add(viewModel.BuildDefinition); + } + + IsInEditMode = false; + + UpdateSelectedCollection(); + BuildDefinitions.Clear(); + } + } + protected abstract String ProviderName { get; } protected override void OnInitialized() @@ -115,6 +135,8 @@ protected override void OnInitialized() SetDisplayName(MonitorSettings.SelectedBuildDefinitions); + UpdateSelectedCollection(); + // ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(nameof(ProjectUrl)); ConnectCommand?.RaiseCanExecuteChanged(); @@ -137,28 +159,26 @@ private void ApplyBuildDefinitions(IEnumerable buildDefinition { BuildDefinitions.AddRange(buildDefinitions.Select(buildDefinition => { - var selectedBuildDefinitions = MonitorSettings.SelectedBuildDefinitions; - var vm = new BuildDefinitionViewModel(buildDefinition); - var definition = selectedBuildDefinitions.SingleOrDefault(a => a.Id == vm.BuildDefinition.Id); + var definition = MonitorSettings.SelectedBuildDefinitions.SingleOrDefault(a => a.Id == vm.BuildDefinition.Id); if (definition != null && this.buildDefinitionEqualityComparer.Equals(definition, vm.BuildDefinition)) { vm.IsSelected = true; } - vm.PropertyChanged += BuildDefinitionPropertyChanged; - return vm; })); - } - private void BuildDefinitionPropertyChanged(Object sender, PropertyChangedEventArgs e) - { - var vm = sender as BuildDefinitionViewModel; - if (vm != null && e.PropertyName == nameof(vm.IsSelected)) + var obsoleteDefinitions = MonitorSettings.SelectedBuildDefinitions.Where(a => BuildDefinitions.All(x => x.BuildDefinition.Id != a.Id)).ToList(); + foreach (var obsoleteDefinition in obsoleteDefinitions) { - UpdateSelectedBuildDefinitionList(vm); + var vm = new BuildDefinitionViewModel(obsoleteDefinition) + { + IsDeleted = true, + IsSelected = true + }; + BuildDefinitions.Add(vm); } } @@ -178,17 +198,9 @@ private async Task OnConnectAsync() try { - Application.Current.Dispatcher.Invoke(() => - { - foreach (var buildDefinitionViewModel in BuildDefinitions) - { - buildDefinitionViewModel.PropertyChanged -= BuildDefinitionPropertyChanged; - } + Application.Current.Dispatcher.Invoke(() => { BuildDefinitions.Clear(); }); - BuildDefinitions.Clear(); - }); - - var buildDefinitions = await Task.Run(() => BuildProvider.GetBuildDefinitionsAsync(MonitorSettings)).ConfigureAwait(false); + var buildDefinitions = await BuildProvider.GetBuildDefinitionsAsync(MonitorSettings).ConfigureAwait(false); if (buildDefinitions.IsSuccessStatusCode) { @@ -198,17 +210,13 @@ private async Task OnConnectAsync() } else { - if (buildDefinitions.StatusCode == HttpStatusCode.Unauthorized) - { - ShowPersonalAccessTokenInput = true; - StatusText = $"Authorization failed, please provide personal access token!"; - } - else - { - StatusText = $"Error connecting. StatusCode: {buildDefinitions.StatusCode}"; - } + AccessTokenInputWhenUnauthorized(buildDefinitions.StatusCode); } } + catch (AdvancedHttpRequestException ex) + { + AccessTokenInputWhenUnauthorized(ex.StatusCode); + } catch (Exception ex) { StatusText = ex.Message; @@ -217,6 +225,19 @@ private async Task OnConnectAsync() IsConnecting = false; } + private void AccessTokenInputWhenUnauthorized(HttpStatusCode statusCode) + { + if (statusCode == HttpStatusCode.Unauthorized) + { + ShowPersonalAccessTokenInput = true; + StatusText = $"Authorization failed, please provide personal access token!"; + } + else + { + StatusText = $"Error connecting. StatusCode: {statusCode}"; + } + } + private void SetDisplayName(IEnumerable buildDefinitions) { // this is okay as they are from the same project @@ -232,22 +253,10 @@ private void SetDisplayName(IEnumerable buildDefinitions) } } - private void UpdateSelectedBuildDefinitionList(BuildDefinitionViewModel buildDefinitionViewModel) + private void UpdateSelectedCollection() { - if (buildDefinitionViewModel.IsSelected) - { - MonitorSettings.SelectedBuildDefinitions.Add(buildDefinitionViewModel.BuildDefinition); - } - else if (!buildDefinitionViewModel.IsSelected) - { - var currentItem = MonitorSettings.SelectedBuildDefinitions.SingleOrDefault( - definition => this.buildDefinitionEqualityComparer.Equals(definition, buildDefinitionViewModel.BuildDefinition)); - - MonitorSettings.SelectedBuildDefinitions.Remove(currentItem); - } - - // ReSharper disable once ExplicitCallerInfoArgument - OnPropertyChanged(nameof(SelectedBuildDefinitions)); + this.selectedBuildDefinitions.Clear(); + this.selectedBuildDefinitions.AddRange(MonitorSettings.SelectedBuildDefinitions); } private readonly IEqualityComparer buildDefinitionEqualityComparer; @@ -255,6 +264,7 @@ private void UpdateSelectedBuildDefinitionList(BuildDefinitionViewModel buildDef private String displayName; private Boolean isConnecting; + private readonly RangeObservableCollection selectedBuildDefinitions = new RangeObservableCollection(); private Boolean showPersonalAccessTokenInput; diff --git a/BuildsAppReborn.Access.UI/TFS/Views/TfsBuildProviderViewBase.xaml b/BuildsAppReborn.Access.UI/TFS/Views/TfsBuildProviderViewBase.xaml index b6e7bc9..11ce047 100644 --- a/BuildsAppReborn.Access.UI/TFS/Views/TfsBuildProviderViewBase.xaml +++ b/BuildsAppReborn.Access.UI/TFS/Views/TfsBuildProviderViewBase.xaml @@ -114,7 +114,18 @@ - + + + + + diff --git a/BuildsAppReborn.Access/BuildMonitor.cs b/BuildsAppReborn.Access/BuildMonitor.cs index e5c7305..2f1ebc3 100644 --- a/BuildsAppReborn.Access/BuildMonitor.cs +++ b/BuildsAppReborn.Access/BuildMonitor.cs @@ -43,7 +43,7 @@ public BuildMonitor(LazyContainer buildP public async Task BeginPollingBuildsAsync() { var builds = new List(); - foreach (var pair in this.providerSettingsGroup) + foreach (var pair in this.providerSettingsGroup.ToList()) { foreach (var setting in pair.Value) { diff --git a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs index 291a725..83f64d7 100644 --- a/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs +++ b/BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs @@ -34,19 +34,16 @@ public virtual async Task>> GetBuildD var requestUrl = $"{projectUrl}/_apis/build/definitions?api-version={ApiVersion}"; var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false); - if (requestResponse.IsSuccessStatusCode) + requestResponse.ThrowIfUnsuccessful(); + + var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); + foreach (var buildDefinition in data) { - var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); - foreach (var buildDefinition in data) - { - buildDefinition.BuildSettingsId = settings.UniqueId; - } - - return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; + buildDefinition.BuildSettingsId = settings.UniqueId; } - return new DataResponse> {Data = Enumerable.Empty(), StatusCode = requestResponse.StatusCode}; + return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; } throw new Exception("Error while processing method!"); @@ -70,16 +67,13 @@ public virtual async Task>> GetBuildsAsync(IEnu var buildDefinitionsCommaList = String.Join(",", buildDefinitionsList.Select(a => a.Id)); var requestUrl = $"{projectUrl}/_apis/build/builds?api-version={ApiVersion}&definitions={buildDefinitionsCommaList}&maxBuildsPerDefinition={maxBuilds}"; var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false); - if (requestResponse.IsSuccessStatusCode) - { - var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); - await ResolveAdditionalBuildDataAsync(settings, data, projectUrl).ConfigureAwait(false); + requestResponse.ThrowIfUnsuccessful(); - return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; - } + var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); + await ResolveAdditionalBuildDataAsync(settings, data, projectUrl).ConfigureAwait(false); - return new DataResponse> {Data = Enumerable.Empty(), StatusCode = requestResponse.StatusCode}; + return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; } throw new Exception("Error while processing method!"); @@ -91,39 +85,36 @@ public virtual async Task>> GetBuildsByPullRequ var requestUrl = $"{projectUrl}/_apis/git/pullrequests?api-version={ApiVersion}"; var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false); - if (requestResponse.IsSuccessStatusCode) - { - var dict = new Dictionary>(); + requestResponse.ThrowIfUnsuccessful(); - var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); + var dict = new Dictionary>(); + + var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); - foreach (var pullRequest in data) + foreach (var pullRequest in data) + { + var buildsResponse = await GetBuildsOfPullRequestAsync(pullRequest, settings).ConfigureAwait(false); + if (buildsResponse.IsSuccessStatusCode) { - var buildsResponse = await GetBuildsOfPullRequestAsync(pullRequest, settings).ConfigureAwait(false); - if (buildsResponse.IsSuccessStatusCode) - { - dict.Add(pullRequest, buildsResponse.Data); - } - else - { - throw new Exception("Error while processing method!"); - } + dict.Add(pullRequest, buildsResponse.Data); } - - // sets the relation of the PR to the build object - foreach (var keyValuePair in dict) + else { - foreach (var build in keyValuePair.Value) - { - build.PullRequest = keyValuePair.Key; - } + throw new Exception("Error while processing method!"); } + } - return new DataResponse> {Data = dict.Values.SelectMany(a => a).ToList(), StatusCode = requestResponse.StatusCode}; + // sets the relation of the PR to the build object + foreach (var keyValuePair in dict) + { + foreach (var build in keyValuePair.Value) + { + build.PullRequest = keyValuePair.Key; + } } - throw new Exception("Error while processing method!"); + return new DataResponse> {Data = dict.Values.SelectMany(a => a).ToList(), StatusCode = requestResponse.StatusCode}; } protected abstract String ApiVersion { get; } @@ -232,17 +223,14 @@ private async Task>> GetBuildsOfPullRequestAsyn var requestUrl = $"{projectUrl}/_apis/build/builds?api-version={ApiVersion}&branchName=refs%2Fpull%2F{pullRequest.Id}%2Fmerge&$top={maxBuilds}"; var requestResponse = await GetRequestResponseAsync(requestUrl, settings).ConfigureAwait(false); - if (requestResponse.IsSuccessStatusCode) - { - var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); + requestResponse.ThrowIfUnsuccessful(); - await ResolveAdditionalBuildDataAsync(settings, data, projectUrl).ConfigureAwait(false); + var result = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + var data = JsonConvert.DeserializeObject>(JObject.Parse(result)["value"].ToString()); - return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; - } + await ResolveAdditionalBuildDataAsync(settings, data, projectUrl).ConfigureAwait(false); - throw new Exception("Error while processing method!"); + return new DataResponse> {Data = data, StatusCode = requestResponse.StatusCode}; } private Tuple GetGitOwnerAndRepo(String gitHubRepoUrl) diff --git a/BuildsAppReborn.Client/Converter/BuildTimeToElapsedTimeConverter.cs b/BuildsAppReborn.Client/Converter/BuildTimeToElapsedTimeConverter.cs index f7cf4b8..7a986df 100644 --- a/BuildsAppReborn.Client/Converter/BuildTimeToElapsedTimeConverter.cs +++ b/BuildsAppReborn.Client/Converter/BuildTimeToElapsedTimeConverter.cs @@ -27,6 +27,11 @@ public Object ConvertBack(Object value, Type targetType, Object parameter, Cultu private String GetReadableTimespan(TimeSpan elapsedTimeSpan) { + if (elapsedTimeSpan.TotalSeconds < 1) + { + return "1s"; + } + var seconds = elapsedTimeSpan.Seconds; var minutes = elapsedTimeSpan.Minutes; var hours = elapsedTimeSpan.Hours; diff --git a/BuildsAppReborn.Client/ViewModels/Settings/ServerSettingsViewModel.cs b/BuildsAppReborn.Client/ViewModels/Settings/ServerSettingsViewModel.cs index 5be60b8..b47eb68 100644 --- a/BuildsAppReborn.Client/ViewModels/Settings/ServerSettingsViewModel.cs +++ b/BuildsAppReborn.Client/ViewModels/Settings/ServerSettingsViewModel.cs @@ -33,6 +33,7 @@ internal ServerSettingsViewModel(GlobalSettingsContainer globalSettingsContainer SaveCommand = new DelegateCommand(OnSave); AddProviderCommand = new DelegateCommand(OnAddProvider); RemoveProviderCommand = new DelegateCommand(OnRemoveView); + EditCommand = new DelegateCommand(OnEdit); } public DelegateCommand AddProviderCommand { get; } @@ -41,6 +42,8 @@ internal ServerSettingsViewModel(GlobalSettingsContainer globalSettingsContainer public ExportFactoryContainer BuildProviderViews { get; } + public DelegateCommand EditCommand { get; } + public DelegateCommand RemoveProviderCommand { get; } public DelegateCommand SaveCommand { get; } @@ -56,6 +59,12 @@ public void OnSave() { this.globalSettingsContainer.BuildMonitorSettingsContainer = this.buildMonitorSettingsContainer.Clone(); this.globalSettingsContainer.Save(); + + foreach (var viewModel in Views.Select(a => a.ViewModel)) + { + viewModel.Save(); + } + this.buildMonitor.Start(this.globalSettingsContainer.BuildMonitorSettingsContainer, this.globalSettingsContainer.GeneralSettings); this.buildMonitor.BeginPollingBuildsAsync(); } @@ -91,6 +100,11 @@ private void OnAddProvider(IIdentifierMetadata selectedProvider) AddView(providerid, buildMonitorSettings); } + private void OnEdit(IBuildProviderView view) + { + view.ViewModel.IsInEditMode = !view.ViewModel.IsInEditMode; + } + private void OnRemoveView(IBuildProviderView view) { if (view != null) diff --git a/BuildsAppReborn.Client/Views/ServerSettingsControl.xaml b/BuildsAppReborn.Client/Views/ServerSettingsControl.xaml index c7cdf5e..fb7e66d 100644 --- a/BuildsAppReborn.Client/Views/ServerSettingsControl.xaml +++ b/BuildsAppReborn.Client/Views/ServerSettingsControl.xaml @@ -56,7 +56,10 @@ - + @@ -66,7 +69,9 @@ @@ -103,7 +108,9 @@ diff --git a/BuildsAppReborn.Contracts.UI/IBuildProviderViewModel.cs b/BuildsAppReborn.Contracts.UI/IBuildProviderViewModel.cs index 2d5a2c3..ee5ed36 100644 --- a/BuildsAppReborn.Contracts.UI/IBuildProviderViewModel.cs +++ b/BuildsAppReborn.Contracts.UI/IBuildProviderViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using BuildsAppReborn.Contracts.Models; namespace BuildsAppReborn.Contracts.UI @@ -8,12 +9,16 @@ public interface IBuildProviderViewModel { String DisplayName { get; } + Boolean IsInEditMode { get; set; } + BuildMonitorSettings MonitorSettings { get; } - IEnumerable SelectedBuildDefinitions { get; } + ObservableCollection SelectedBuildDefinitions { get; } String Url { get; } void Initialize(BuildMonitorSettings settings); + + void Save(); } } \ No newline at end of file diff --git a/BuildsAppReborn.Infrastructure/AdvancedHttpRequestException.cs b/BuildsAppReborn.Infrastructure/AdvancedHttpRequestException.cs new file mode 100644 index 0000000..04ec23a --- /dev/null +++ b/BuildsAppReborn.Infrastructure/AdvancedHttpRequestException.cs @@ -0,0 +1,29 @@ +using System; +using System.Net; +using System.Net.Http; + +namespace BuildsAppReborn.Infrastructure +{ + [Serializable] + public class AdvancedHttpRequestException : HttpRequestException + { + public AdvancedHttpRequestException() : base((String) null, (Exception) null) + { + } + + public AdvancedHttpRequestException(HttpStatusCode statusCode) : this(statusCode, (String) null, (Exception) null) + { + } + + public AdvancedHttpRequestException(HttpStatusCode statusCode, String message) : this(statusCode, message, (Exception) null) + { + } + + public AdvancedHttpRequestException(HttpStatusCode statusCode, String message, Exception innerException) : base(message, innerException) + { + StatusCode = statusCode; + } + + public HttpStatusCode StatusCode { get; } + } +} \ No newline at end of file diff --git a/BuildsAppReborn.Infrastructure/BuildsAppReborn.Infrastructure.csproj b/BuildsAppReborn.Infrastructure/BuildsAppReborn.Infrastructure.csproj index 65ccc3e..f992d27 100644 --- a/BuildsAppReborn.Infrastructure/BuildsAppReborn.Infrastructure.csproj +++ b/BuildsAppReborn.Infrastructure/BuildsAppReborn.Infrastructure.csproj @@ -51,8 +51,10 @@ Properties\GlobalAssemblyInfo.cs + + diff --git a/BuildsAppReborn.Infrastructure/HttpResponseMessageExtensions.cs b/BuildsAppReborn.Infrastructure/HttpResponseMessageExtensions.cs new file mode 100644 index 0000000..b66ff1f --- /dev/null +++ b/BuildsAppReborn.Infrastructure/HttpResponseMessageExtensions.cs @@ -0,0 +1,22 @@ +using System.Net.Http; + +namespace BuildsAppReborn.Infrastructure +{ + public static class HttpResponseMessageExtensions + { + public static void ThrowIfUnsuccessful(this HttpResponseMessage responseMessage) + { + if (!responseMessage.IsSuccessStatusCode) + { + try + { + responseMessage.EnsureSuccessStatusCode(); + } + catch (HttpRequestException ex) + { + throw new AdvancedHttpRequestException(responseMessage.StatusCode, ex.Message); + } + } + } + } +} \ No newline at end of file