Skip to content

Commit 00cbc04

Browse files
committed
feat(*): add controller, input formatter, and services
1 parent 6dca5c0 commit 00cbc04

File tree

9 files changed

+98
-9
lines changed

9 files changed

+98
-9
lines changed

JsonApiDotnetCore.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{02
2828
EndProject
2929
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportsExample", "src\Examples\ReportsExample\ReportsExample.csproj", "{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}"
3030
EndProject
31+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OperationsExample", "src\Examples\OperationsExample\OperationsExample.csproj", "{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}"
32+
EndProject
3133
Global
3234
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3335
Debug|Any CPU = Debug|Any CPU
@@ -110,6 +112,18 @@ Global
110112
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x64.Build.0 = Release|x64
111113
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x86.ActiveCfg = Release|x86
112114
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x86.Build.0 = Release|x86
115+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
116+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|Any CPU.Build.0 = Debug|Any CPU
117+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x64.ActiveCfg = Debug|x64
118+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x64.Build.0 = Debug|x64
119+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x86.ActiveCfg = Debug|x86
120+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x86.Build.0 = Debug|x86
121+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|Any CPU.ActiveCfg = Release|Any CPU
122+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|Any CPU.Build.0 = Release|Any CPU
123+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x64.ActiveCfg = Release|x64
124+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x64.Build.0 = Release|x64
125+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x86.ActiveCfg = Release|x86
126+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x86.Build.0 = Release|x86
113127
EndGlobalSection
114128
GlobalSection(SolutionProperties) = preSolution
115129
HideSolutionNode = FALSE
@@ -123,5 +137,6 @@ Global
123137
{6D4BD85A-A262-44C6-8572-FE3A30410BF3} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
124138
{026FBC6C-AF76-4568-9B87-EC73457899FD} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
125139
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
140+
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
126141
EndGlobalSection
127142
EndGlobal

src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
23
using JsonApiDotNetCore.Builders;
34
using JsonApiDotNetCore.Internal;
5+
using JsonApiDotNetCore.Models;
46
using JsonApiDotNetCore.Serialization;
57
using Microsoft.EntityFrameworkCore;
68
using Newtonsoft.Json;
@@ -30,6 +32,7 @@ public IContractResolver JsonContractResolver
3032
ContractResolver = new DasherizedResolver()
3133
};
3234
internal IContextGraphBuilder ContextGraphBuilder { get; } = new ContextGraphBuilder();
35+
internal List<JsonApiExtension> EnabledExtensions { get; set; } = new List<JsonApiExtension>();
3336

3437
public void BuildContextGraph<TContext>(Action<IContextGraphBuilder> builder) where TContext : DbContext
3538
{
@@ -48,5 +51,10 @@ public void BuildContextGraph(Action<IContextGraphBuilder> builder)
4851

4952
ContextGraph = ContextGraphBuilder.Build();
5053
}
54+
55+
public void EnableExtension(JsonApiExtension extension)
56+
{
57+
EnabledExtensions.Add(extension);
58+
}
5159
}
5260
}

src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using JsonApiDotNetCore.Models;
55
using JsonApiDotNetCore.Services;
66
using Microsoft.AspNetCore.Mvc;
7-
using Microsoft.Extensions.Logging;
87

98
namespace JsonApiDotNetCore.Controllers
109
{
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Threading.Tasks;
2+
using JsonApiDotNetCore.Models.Operations;
3+
using JsonApiDotNetCore.Services.Operations;
4+
using Microsoft.AspNetCore.Mvc;
5+
6+
namespace JsonApiDotNetCore.Controllers
7+
{
8+
public class JsonApiOperationsController : Controller
9+
{
10+
private readonly IOperationsProcessor _operationsProcessor;
11+
12+
public JsonApiOperationsController(IOperationsProcessor operationsProcessor)
13+
{
14+
_operationsProcessor = operationsProcessor;
15+
}
16+
17+
[HttpPost("bulk")]
18+
public async Task<IActionResult> PatchAsync(OperationsDocument doc)
19+
{
20+
var results = await _operationsProcessor.ProcessAsync(doc.Operations);
21+
return Ok(results);
22+
}
23+
}
24+
}

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
using JsonApiDotNetCore.Internal;
77
using JsonApiDotNetCore.Internal.Generics;
88
using JsonApiDotNetCore.Middleware;
9+
using JsonApiDotNetCore.Models;
910
using JsonApiDotNetCore.Serialization;
1011
using JsonApiDotNetCore.Services;
12+
using JsonApiDotNetCore.Services.Operations;
1113
using Microsoft.AspNetCore.Http;
1214
using Microsoft.AspNetCore.Mvc;
1315
using Microsoft.EntityFrameworkCore;
@@ -91,6 +93,9 @@ public static void AddJsonApiInternals(
9193
services.AddSingleton<DbContextOptions>(new DbContextOptionsBuilder().Options);
9294
}
9395

96+
if (jsonApiOptions.EnabledExtensions.Contains(JsonApiExtension.Operations))
97+
AddOperationServices(services);
98+
9499
services.AddScoped<IDbContextResolver, DbContextResolver>();
95100
services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>));
96101
services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>));
@@ -114,8 +119,18 @@ public static void AddJsonApiInternals(
114119
services.AddScoped<IControllerContext, Services.ControllerContext>();
115120
}
116121

122+
private static void AddOperationServices(IServiceCollection services)
123+
{
124+
services.AddScoped<IOperationsProcessor, OperationsProcessor>();
125+
services.AddSingleton<IOperationProcessorResolver, OperationProcessorResolver>();
126+
services.AddSingleton<IGenericProcessorFactory, GenericProcessorFactory>();
127+
}
128+
117129
public static void SerializeAsJsonApi(this MvcOptions options, JsonApiOptions jsonApiOptions)
118130
{
131+
if (jsonApiOptions.EnabledExtensions.Contains(JsonApiExtension.Operations))
132+
options.InputFormatters.Insert(0, new JsonApiOperationsInputFormatter());
133+
119134
options.InputFormatters.Insert(0, new JsonApiInputFormatter());
120135

121136
options.OutputFormatters.Insert(0, new JsonApiOutputFormatter());

src/JsonApiDotNetCore/Formatters/JsonApiInputFormatter.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@ namespace JsonApiDotNetCore.Formatters
88
public class JsonApiInputFormatter : IInputFormatter
99
{
1010
public bool CanRead(InputFormatterContext context)
11-
{
11+
{
1212
if (context == null)
1313
throw new ArgumentNullException(nameof(context));
1414

1515
var contentTypeString = context.HttpContext.Request.ContentType;
1616

17-
return contentTypeString == "application/vnd.api+json";
17+
var canRead = contentTypeString == "application/vnd.api+json";
18+
19+
Console.WriteLine($">>> JsonApiInputFormatter Can Read {canRead}");
20+
21+
return canRead;
1822
}
1923

2024
public async Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
2125
{
26+
Console.WriteLine($">>> JsonApiInputFormatter ReadAsync");
2227
var reader = context.HttpContext.RequestServices.GetService<IJsonApiReader>();
2328
return await reader.ReadAsync(context);
2429
}

src/JsonApiDotNetCore/Formatters/JsonApiOperationsInputFormatter.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,43 @@
11
using System;
2+
using System.Text;
23
using System.Threading.Tasks;
34
using Microsoft.AspNetCore.Mvc.Formatters;
45
using Microsoft.Extensions.DependencyInjection;
56
using Microsoft.Extensions.Primitives;
7+
using Microsoft.Net.Http.Headers;
68

79
namespace JsonApiDotNetCore.Formatters
810
{
9-
public class JsonApiOperationsInputFormatter : IInputFormatter
11+
public class JsonApiOperationsInputFormatter : TextInputFormatter
1012
{
1113
const string PROFILE_EXTENSION = "<http://example.org/profiles/myjsonstuff>; rel=\"profile\"";
1214

13-
public bool CanRead(InputFormatterContext context)
15+
public JsonApiOperationsInputFormatter()
16+
{
17+
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/vnd.api+json"));
18+
SupportedEncodings.Add(Encoding.UTF8);
19+
SupportedEncodings.Add(Encoding.Unicode);
20+
}
21+
22+
public override bool CanRead(InputFormatterContext context)
1423
{
1524
if (context == null)
1625
throw new ArgumentNullException(nameof(context));
1726

1827
var contentTypeString = context.HttpContext.Request.ContentType;
1928

20-
return (
21-
contentTypeString == "application/vnd.api+json" &&
29+
var canRead = (
30+
contentTypeString == "application/vnd.api+json" &&
2231
context.HttpContext.Request.Headers.TryGetValue("Link", out StringValues profileExtension) &&
2332
profileExtension == PROFILE_EXTENSION
2433
);
34+
Console.WriteLine($">>> JsonApiOperationsInputFormatter Can Read {canRead}");
35+
return canRead;
2536
}
2637

27-
public async Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
38+
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
2839
{
40+
Console.WriteLine($">>> JsonApiOperationsInputFormatter ReadAsync");
2941
var reader = context.HttpContext.RequestServices.GetService<IJsonApiOperationsReader>();
3042
return await reader.ReadAsync(context);
3143
}

src/JsonApiDotNetCore/Formatters/JsonApiOperationsReader.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Threading.Tasks;
4+
using JsonApiDotNetCore.Internal;
45
using JsonApiDotNetCore.Models.Operations;
56
using Microsoft.AspNetCore.Mvc.Formatters;
67
using Newtonsoft.Json;
@@ -27,7 +28,10 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
2728

2829
var operations = JsonConvert.DeserializeObject<OperationsDocument>(body);
2930

30-
return InputFormatterResult.SuccessAsync(null);
31+
if(operations == null)
32+
throw new JsonApiException(400, "Failed to deserialize operations request.");
33+
34+
return InputFormatterResult.SuccessAsync(operations);
3135
}
3236

3337
private string GetRequestBody(Stream body)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace JsonApiDotNetCore.Models
2+
{
3+
public enum JsonApiExtension
4+
{
5+
Operations = 0
6+
}
7+
}

0 commit comments

Comments
 (0)