Skip to content

Commit da055ae

Browse files
author
Bart Koelman
committed
Resurrected original operations code
Reverted operations-related changes from #571
1 parent 816c58a commit da055ae

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2389
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using JsonApiDotNetCore.Controllers;
2+
using JsonApiDotNetCore.Services.Operations;
3+
using Microsoft.AspNetCore.Mvc;
4+
5+
namespace OperationsExample.Controllers
6+
{
7+
[Route("api/bulk")]
8+
public class OperationsController : JsonApiOperationsController
9+
{
10+
public OperationsController(IOperationsProcessor processor)
11+
: base(processor)
12+
{ }
13+
}
14+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
4+
<PreserveCompilationContext>true</PreserveCompilationContext>
5+
<AssemblyName>OperationsExample</AssemblyName>
6+
<OutputType>Exe</OutputType>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="../../JsonApiDotNetCore/JsonApiDotNetCore.csproj" />
11+
<ProjectReference Include="../JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="Microsoft.AspNetCore" Version="$(AspNetCoreVersion)" />
16+
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(AspNetCoreVersion)" />
17+
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftConfigurationVersion)" />
18+
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftConfigurationVersion)" />
19+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftConfigurationVersion)" />
20+
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftConfigurationVersion)" />
21+
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftLoggingVersion)" />
22+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftLoggingVersion)" />
23+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftLoggingVersion)" />
24+
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftOptionsVersion)" />
25+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="$(EFCoreToolsVersion)" />
26+
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="$(EFCoreToolsVersion)" />
27+
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(NpgsqlPostgreSQLVersion)" />
28+
</ItemGroup>
29+
30+
</Project>

Diff for: src/Examples/OperationsExample/Program.cs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.AspNetCore;
2+
using Microsoft.AspNetCore.Hosting;
3+
4+
namespace OperationsExample
5+
{
6+
public class Program
7+
{
8+
public static void Main(string[] args) => BuildWebHost(args).Run();
9+
10+
public static IWebHost BuildWebHost(string[] args) =>
11+
WebHost.CreateDefaultBuilder(args)
12+
.UseStartup<Startup>()
13+
.Build();
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:53656/",
7+
"sslPort": 0
8+
}
9+
},
10+
"profiles": {
11+
"IIS Express": {
12+
"commandName": "IISExpress",
13+
"launchBrowser": true,
14+
"environmentVariables": {
15+
"ASPNETCORE_ENVIRONMENT": "Development"
16+
}
17+
},
18+
"OperationsExample": {
19+
"commandName": "Project",
20+
"launchBrowser": true,
21+
"environmentVariables": {
22+
"ASPNETCORE_ENVIRONMENT": "Development"
23+
},
24+
"applicationUrl": "http://localhost:53657/"
25+
}
26+
}
27+
}

Diff for: src/Examples/OperationsExample/Startup.cs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using JsonApiDotNetCore.Extensions;
3+
using JsonApiDotNetCoreExample.Data;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.Extensions.Configuration;
8+
using Microsoft.Extensions.DependencyInjection;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace OperationsExample
12+
{
13+
public class Startup
14+
{
15+
public readonly IConfiguration Config;
16+
17+
public Startup(IHostingEnvironment env)
18+
{
19+
var builder = new ConfigurationBuilder()
20+
.SetBasePath(env.ContentRootPath)
21+
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
22+
.AddEnvironmentVariables();
23+
24+
Config = builder.Build();
25+
}
26+
27+
public virtual IServiceProvider ConfigureServices(IServiceCollection services)
28+
{
29+
var loggerFactory = new LoggerFactory();
30+
loggerFactory.AddConsole(LogLevel.Warning);
31+
32+
services.AddSingleton<ILoggerFactory>(loggerFactory);
33+
34+
services.AddDbContext<AppDbContext>(options => options.UseNpgsql(GetDbConnectionString()), ServiceLifetime.Scoped);
35+
36+
services.AddJsonApi<AppDbContext>(opt => opt.EnableOperations = true);
37+
38+
return services.BuildServiceProvider();
39+
}
40+
41+
public virtual void Configure(
42+
IApplicationBuilder app,
43+
IHostingEnvironment env,
44+
ILoggerFactory loggerFactory,
45+
AppDbContext context)
46+
{
47+
context.Database.EnsureCreated();
48+
49+
loggerFactory.AddConsole(Config.GetSection("Logging"));
50+
app.UseJsonApi();
51+
}
52+
53+
public string GetDbConnectionString() => Config["Data:DefaultConnection"];
54+
}
55+
}

Diff for: src/Examples/OperationsExample/appsettings.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"Data": {
3+
"DefaultConnection": "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=postgres"
4+
},
5+
"Logging": {
6+
"IncludeScopes": false,
7+
"LogLevel": {
8+
"Default": "Warning",
9+
"System": "Warning",
10+
"Microsoft": "Warning"
11+
}
12+
}
13+
}

Diff for: src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ public interface IJsonApiOptions
172172
/// </summary>
173173
int? MaximumIncludeDepth { get; }
174174

175+
bool EnableOperations { get; set; }
176+
175177
/// <summary>
176178
/// Specifies the settings that are used by the <see cref="JsonSerializer"/>.
177179
/// Note that at some places a few settings are ignored, to ensure JSON:API spec compliance.

Diff for: src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs

+25
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ public void ConfigureServiceContainer(ICollection<Type> dbContextTypes)
132132
}
133133
}
134134

135+
if (jsonApiOptions.EnableOperations)
136+
{
137+
AddOperationServices(services);
138+
}
139+
135140
AddResourceLayer();
136141
AddRepositoryLayer();
137142
AddServiceLayer();
@@ -265,6 +270,26 @@ private void AddSerializationLayer()
265270
_services.AddScoped(typeof(ResponseSerializer<>));
266271
_services.AddScoped(sp => sp.GetRequiredService<IJsonApiSerializerFactory>().GetSerializer());
267272
_services.AddScoped<IResourceObjectBuilder, ResponseResourceObjectBuilder>();
273+
services.AddScoped<IOperationsDeserializer, OperationsDeserializer>();
274+
}
275+
276+
private static void AddOperationServices(IServiceCollection services)
277+
{
278+
services.AddScoped<IOperationsProcessor, OperationsProcessor>();
279+
280+
services.AddScoped(typeof(ICreateOpProcessor<>), typeof(CreateOpProcessor<>));
281+
services.AddScoped(typeof(ICreateOpProcessor<,>), typeof(CreateOpProcessor<,>));
282+
283+
services.AddScoped(typeof(IGetOpProcessor<>), typeof(GetOpProcessor<>));
284+
services.AddScoped(typeof(IGetOpProcessor<,>), typeof(GetOpProcessor<,>));
285+
286+
services.AddScoped(typeof(IRemoveOpProcessor<>), typeof(RemoveOpProcessor<>));
287+
services.AddScoped(typeof(IRemoveOpProcessor<,>), typeof(RemoveOpProcessor<,>));
288+
289+
services.AddScoped(typeof(IUpdateOpProcessor<>), typeof(UpdateOpProcessor<>));
290+
services.AddScoped(typeof(IUpdateOpProcessor<,>), typeof(UpdateOpProcessor<,>));
291+
292+
services.AddScoped<IOperationProcessorResolver, OperationProcessorResolver>();
268293
}
269294

270295
private void AddResourcesFromDbContext(DbContext dbContext, ResourceGraphBuilder builder)

Diff for: src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs

+11
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ public sealed class JsonApiOptions : IJsonApiOptions
6767
/// <inheritdoc />
6868
public int? MaximumIncludeDepth { get; set; }
6969

70+
/// <summary>
71+
/// Whether or not to allow json:api v1.1 operation requests.
72+
/// This is a beta feature and there may be breaking changes
73+
/// in subsequent releases. For now, ijt should be considered
74+
/// experimental.
75+
/// </summary>
76+
/// <remarks>
77+
/// This will be enabled by default in a subsequent patch JsonApiDotNetCore v2.2.x
78+
/// </remarks>
79+
public bool EnableOperations { get; set; }
80+
7081
/// <inheritdoc />
7182
public JsonSerializerSettings SerializerSettings { get; } = new JsonSerializerSettings
7283
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
/// <summary>
9+
/// A controller to be used for bulk operations as defined in the json:api 1.1 specification
10+
/// </summary>
11+
public class JsonApiOperationsController : ControllerBase
12+
{
13+
private readonly IOperationsProcessor _operationsProcessor;
14+
15+
/// <param name="operationsProcessor">
16+
/// The processor to handle bulk operations.
17+
/// </param>
18+
public JsonApiOperationsController(IOperationsProcessor operationsProcessor)
19+
{
20+
_operationsProcessor = operationsProcessor;
21+
}
22+
23+
/// <summary>
24+
/// Bulk endpoint for json:api operations
25+
/// </summary>
26+
/// <param name="doc">
27+
/// A json:api operations request document
28+
/// </param>
29+
/// <example>
30+
/// <code>
31+
/// PATCH /api/bulk HTTP/1.1
32+
/// Content-Type: application/vnd.api+json
33+
///
34+
/// {
35+
/// "operations": [{
36+
/// "op": "add",
37+
/// "ref": {
38+
/// "type": "authors"
39+
/// },
40+
/// "data": {
41+
/// "type": "authors",
42+
/// "attributes": {
43+
/// "name": "jaredcnance"
44+
/// }
45+
/// }
46+
/// }]
47+
/// }
48+
/// </code>
49+
/// </example>
50+
[HttpPatch]
51+
public virtual async Task<IActionResult> PatchAsync([FromBody] OperationsDocument doc)
52+
{
53+
if (doc == null) return new StatusCodeResult(422);
54+
55+
var results = await _operationsProcessor.ProcessAsync(doc.Operations);
56+
57+
return Ok(new OperationsDocument(results));
58+
}
59+
}
60+
}

Diff for: src/JsonApiDotNetCore/Middleware/IJsonApiRequest.cs

+2
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,7 @@ public interface IJsonApiRequest
5757
/// Indicates whether this request targets only fetching of data (such as resources and relationships).
5858
/// </summary>
5959
bool IsReadOnly { get; }
60+
61+
bool IsBulkRequest { get; set; }
6062
}
6163
}

Diff for: src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public async Task Invoke(HttpContext httpContext,
5555
return;
5656
}
5757

58+
_currentRequest.IsBulkRequest = PathIsBulk();
59+
5860
SetupRequest((JsonApiRequest)request, primaryResourceContext, routeValues, options, resourceContextProvider, httpContext.Request);
5961

6062
httpContext.RegisterJsonApiRequest();
@@ -63,6 +65,12 @@ public async Task Invoke(HttpContext httpContext,
6365
await _next(httpContext);
6466
}
6567

68+
private bool PathIsBulk()
69+
{
70+
var actionName = (string)_httpContext.GetRouteData().Values["action"];
71+
return actionName.ToLower().Contains("bulk");
72+
}
73+
6674
private static ResourceContext CreatePrimaryResourceContext(RouteValueDictionary routeValues,
6775
IControllerResourceMapping controllerResourceMapping, IResourceContextProvider resourceContextProvider)
6876
{

Diff for: src/JsonApiDotNetCore/Middleware/JsonApiRequest.cs

+3
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@ public sealed class JsonApiRequest : IJsonApiRequest
2929

3030
/// <inheritdoc />
3131
public bool IsReadOnly { get; set; }
32+
33+
/// <inheritdoc />
34+
public bool IsBulkRequest { get; set; }
3235
}
3336
}

0 commit comments

Comments
 (0)