Skip to content

Commit

Permalink
Merge pull request #714 from bart-degreed/extensibility
Browse files Browse the repository at this point in the history
Error handling and logging
  • Loading branch information
nicolestandifer3 committed Apr 8, 2020
2 parents 3773cf9 + affb075 commit 5d2114b
Show file tree
Hide file tree
Showing 174 changed files with 4,000 additions and 1,778 deletions.
5 changes: 3 additions & 2 deletions benchmarks/Query/QueryParserBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging.Abstractions;

namespace Benchmarks.Query
{
Expand Down Expand Up @@ -44,7 +45,7 @@ public QueryParserBenchmarks()
sortService
};

return new QueryParameterParser(options, queryStringAccessor, queryServices);
return new QueryParameterParser(options, queryStringAccessor, queryServices, NullLoggerFactory.Instance);
}

private static QueryParameterParser CreateQueryParameterDiscoveryForAll(IResourceGraph resourceGraph,
Expand All @@ -65,7 +66,7 @@ public QueryParserBenchmarks()
omitNullService
};

return new QueryParameterParser(options, queryStringAccessor, queryServices);
return new QueryParameterParser(options, queryStringAccessor, queryServices, NullLoggerFactory.Instance);
}

[Benchmark]
Expand Down
3 changes: 2 additions & 1 deletion benchmarks/Serialization/JsonApiSerializerBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using BenchmarkDotNet.Attributes;
using JsonApiDotNetCore.Graph;
using JsonApiDotNetCore.Internal.Contracts;
using JsonApiDotNetCore.Managers;
using JsonApiDotNetCore.Query;
Expand Down Expand Up @@ -33,7 +34,7 @@ public JsonApiSerializerBenchmarks()
var resourceObjectBuilder = new ResourceObjectBuilder(resourceGraph, new ResourceObjectBuilderSettings());

_jsonApiSerializer = new ResponseSerializer<BenchmarkResource>(metaBuilderMock.Object, linkBuilderMock.Object,
includeBuilderMock.Object, fieldsToSerialize, resourceObjectBuilder);
includeBuilderMock.Object, fieldsToSerialize, resourceObjectBuilder, new CamelCaseFormatter());
}

private static FieldsToSerialize CreateFieldsToSerialize(IResourceGraph resourceGraph)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace JsonApiDotNetCoreExample.Controllers
{
[DisableQuery(StandardQueryStringParameters.Sort | StandardQueryStringParameters.Page)]
public sealed class ArticlesController : JsonApiController<Article>
{
public ArticlesController(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace JsonApiDotNetCoreExample.Controllers
{
[DisableQuery("skipCache")]
public sealed class TagsController : JsonApiController<Tag>
{
public TagsController(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
using JsonApiDotNetCoreExample.Models;
using Microsoft.Extensions.Logging;

namespace JsonApiDotNetCoreExample.Controllers
{
public sealed class ThrowingResourcesController : JsonApiController<ThrowingResource>
{
public ThrowingResourcesController(
IJsonApiOptions jsonApiOptions,
ILoggerFactory loggerFactory,
IResourceService<ThrowingResource> resourceService)
: base(jsonApiOptions, loggerFactory, resourceService)
{ }
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Exceptions;
using JsonApiDotNetCore.Models;
using JsonApiDotNetCore.Services;
using JsonApiDotNetCoreExample.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace JsonApiDotNetCoreExample.Controllers
{
[ApiController]
[DisableRoutingConvention, Route("custom/route/todoItems")]
public class TodoItemsCustomController : CustomJsonApiController<TodoItem>
{
public TodoItemsCustomController(
IJsonApiOptions options,
IResourceService<TodoItem> resourceService,
ILoggerFactory loggerFactory)
: base(options, resourceService, loggerFactory)
IResourceService<TodoItem> resourceService)
: base(options, resourceService)
{ }
}

Expand All @@ -26,8 +27,7 @@ public class CustomJsonApiController<T>
{
public CustomJsonApiController(
IJsonApiOptions options,
IResourceService<T, int> resourceService,
ILoggerFactory loggerFactory)
IResourceService<T, int> resourceService)
: base(options, resourceService)
{
}
Expand All @@ -41,7 +41,7 @@ public class CustomJsonApiController<T, TId>

private IActionResult Forbidden()
{
return new StatusCodeResult(403);
return new StatusCodeResult((int)HttpStatusCode.Forbidden);
}

public CustomJsonApiController(
Expand All @@ -68,22 +68,29 @@ public async Task<IActionResult> GetAsync()
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(TId id)
{
var entity = await _resourceService.GetAsync(id);

if (entity == null)
try
{
var entity = await _resourceService.GetAsync(id);
return Ok(entity);
}
catch (ResourceNotFoundException)
{
return NotFound();

return Ok(entity);
}
}

[HttpGet("{id}/relationships/{relationshipName}")]
public async Task<IActionResult> GetRelationshipsAsync(TId id, string relationshipName)
{
var relationship = _resourceService.GetRelationshipAsync(id, relationshipName);
if (relationship == null)
try
{
var relationship = await _resourceService.GetRelationshipsAsync(id, relationshipName);
return Ok(relationship);
}
catch (ResourceNotFoundException)
{
return NotFound();

return await GetRelationshipAsync(id, relationshipName);
}
}

[HttpGet("{id}/{relationshipName}")]
Expand Down Expand Up @@ -113,12 +120,15 @@ public async Task<IActionResult> PatchAsync(TId id, [FromBody] T entity)
if (entity == null)
return UnprocessableEntity();

var updatedEntity = await _resourceService.UpdateAsync(id, entity);

if (updatedEntity == null)
try
{
var updatedEntity = await _resourceService.UpdateAsync(id, entity);
return Ok(updatedEntity);
}
catch (ResourceNotFoundException)
{
return NotFound();

return Ok(updatedEntity);
}
}

[HttpPatch("{id}/relationships/{relationshipName}")]
Expand All @@ -131,11 +141,7 @@ public async Task<IActionResult> PatchRelationshipsAsync(TId id, string relation
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteAsync(TId id)
{
var wasDeleted = await _resourceService.DeleteAsync(id);

if (!wasDeleted)
return NotFound();

await _resourceService.DeleteAsync(id);
return NoContent();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System.Net;
using System.Threading.Tasks;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Models;
using JsonApiDotNetCore.Models.JsonApiDocuments;
using JsonApiDotNetCore.Services;
using JsonApiDotNetCoreExample.Models;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -9,7 +12,7 @@
namespace JsonApiDotNetCoreExample.Controllers
{
public abstract class AbstractTodoItemsController<T>
: JsonApiController<T> where T : class, IIdentifiable<int>
: BaseJsonApiController<T> where T : class, IIdentifiable<int>
{
protected AbstractTodoItemsController(
IJsonApiOptions jsonApiOptions,
Expand All @@ -19,6 +22,7 @@ public abstract class AbstractTodoItemsController<T>
{ }
}

[DisableRoutingConvention]
[Route("/abstract")]
public class TodoItemsTestController : AbstractTodoItemsController<TodoItem>
{
Expand All @@ -28,5 +32,49 @@ public class TodoItemsTestController : AbstractTodoItemsController<TodoItem>
IResourceService<TodoItem> service)
: base(jsonApiOptions, loggerFactory, service)
{ }

[HttpGet]
public override async Task<IActionResult> GetAsync() => await base.GetAsync();

[HttpGet("{id}")]
public override async Task<IActionResult> GetAsync(int id) => await base.GetAsync(id);

[HttpGet("{id}/relationships/{relationshipName}")]
public override async Task<IActionResult> GetRelationshipsAsync(int id, string relationshipName)
=> await base.GetRelationshipsAsync(id, relationshipName);

[HttpGet("{id}/{relationshipName}")]
public override async Task<IActionResult> GetRelationshipAsync(int id, string relationshipName)
=> await base.GetRelationshipAsync(id, relationshipName);

[HttpPost]
public override async Task<IActionResult> PostAsync(TodoItem entity)
{
await Task.Yield();

return NotFound(new Error(HttpStatusCode.NotFound)
{
Title = "NotFound ActionResult with explicit error object."
});
}

[HttpPatch("{id}")]
public override async Task<IActionResult> PatchAsync(int id, [FromBody] TodoItem entity)
{
return await base.PatchAsync(id, entity);
}

[HttpPatch("{id}/relationships/{relationshipName}")]
public override async Task<IActionResult> PatchRelationshipsAsync(
int id, string relationshipName, [FromBody] object relationships)
=> await base.PatchRelationshipsAsync(id, relationshipName, relationships);

[HttpDelete("{id}")]
public override async Task<IActionResult> DeleteAsync(int id)
{
await Task.Yield();

return NotFound();
}
}
}
1 change: 1 addition & 0 deletions src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed class AppDbContext : DbContext
public DbSet<ArticleTag> ArticleTags { get; set; }
public DbSet<IdentifiableArticleTag> IdentifiableArticleTags { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<ThrowingResource> ThrowingResources { get; set; }

public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

Expand Down
4 changes: 3 additions & 1 deletion src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.ComponentModel.DataAnnotations;
using JsonApiDotNetCore.Models;

namespace JsonApiDotNetCoreExample.Models
{
public class Tag : Identifiable
{
[Attr]
[RegularExpression(@"^\W$")]
public string Name { get; set; }
}
}
}
29 changes: 29 additions & 0 deletions src/Examples/JsonApiDotNetCoreExample/Models/ThrowingResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Diagnostics;
using System.Linq;
using JsonApiDotNetCore.Formatters;
using JsonApiDotNetCore.Models;

namespace JsonApiDotNetCoreExample.Models
{
public sealed class ThrowingResource : Identifiable
{
[Attr]
public string FailsOnSerialize
{
get
{
var isSerializingResponse = new StackTrace().GetFrames()
.Any(frame => frame.GetMethod().DeclaringType == typeof(JsonApiWriter));

if (isSerializingResponse)
{
throw new InvalidOperationException($"The value for the '{nameof(FailsOnSerialize)}' property is currently unavailable.");
}

return string.Empty;
}
set { }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"JsonApiDotNetCoreExample": {
"commandName": "Project",
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "http://localhost:5000/api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000/"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using System;
using JsonApiDotNetCore.Internal;
using System.Net;
using JsonApiDotNetCore.Exceptions;
using JsonApiDotNetCore.Models;
using JsonApiDotNetCore.Hooks;
using JsonApiDotNetCoreExample.Models;
using JsonApiDotNetCore.Internal.Contracts;
using JsonApiDotNetCore.Models.JsonApiDocuments;

namespace JsonApiDotNetCoreExample.Resources
{
Expand All @@ -17,9 +18,13 @@ public override IEnumerable<Article> OnReturn(HashSet<Article> entities, Resourc
{
if (pipeline == ResourcePipeline.GetSingle && entities.Single().Name == "Classified")
{
throw new JsonApiException(403, "You are not allowed to see this article!", new UnauthorizedAccessException());
throw new JsonApiException(new Error(HttpStatusCode.Forbidden)
{
Title = "You are not allowed to see this article."
});
}
return entities.Where(t => t.Name != "This should be not be included");

return entities.Where(t => t.Name != "This should not be included");
}
}
}
Expand Down

0 comments on commit 5d2114b

Please sign in to comment.