Skip to content

Commit cca63da

Browse files
committed
put operation enabling behind a feature flag on global options
1 parent 9614774 commit cca63da

File tree

5 files changed

+164
-76
lines changed

5 files changed

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

src/JsonApiDotNetCore/Builders/DocumentBuilderOptionsProvider.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
41
using JsonApiDotNetCore.Services;
52
using Microsoft.AspNetCore.Http;
63

src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,106 @@
1010

1111
namespace JsonApiDotNetCore.Configuration
1212
{
13+
/// <summary>
14+
/// Global options.
15+
/// https://json-api-dotnet.github.io/#/global-options
16+
/// </summary>
1317
public class JsonApiOptions
1418
{
19+
/// <summary>
20+
/// The base URL Namespace
21+
/// </summary>
22+
/// <example>
23+
/// <code>options.Namespace = "api/v1";</code>
24+
/// </example>
1525
public string Namespace { get; set; }
26+
27+
/// <summary>
28+
/// The default page size for all resources
29+
/// </summary>
30+
/// <example>
31+
/// <code>options.DefaultPageSize = 10;</code>
32+
/// </example>
1633
public int DefaultPageSize { get; set; }
34+
35+
/// <summary>
36+
/// Whether or not the total-record count should be included in all document
37+
/// level meta objects.
38+
/// Defaults to false.
39+
/// </summary>
40+
/// <example>
41+
/// <code>options.IncludeTotalRecordCount = true;</code>
42+
/// </example>
1743
public bool IncludeTotalRecordCount { get; set; }
44+
45+
/// <summary>
46+
/// Whether or not clients can provide ids when creating resources.
47+
/// Defaults to false. When disabled the application will respond
48+
/// with a 403 Forbidden respponse if a client attempts to create a
49+
/// resource with a defined id.
50+
/// </summary>
51+
/// <example>
52+
/// <code>options.AllowClientGeneratedIds = true;</code>
53+
/// </example>
1854
public bool AllowClientGeneratedIds { get; set; }
55+
56+
/// <summary>
57+
/// The graph of all resources exposed by this application.
58+
/// </summary>
1959
public IContextGraph ContextGraph { get; set; }
60+
61+
/// <summary>
62+
/// Use relative links for all resources.
63+
/// </summary>
64+
/// <example>
65+
/// <code>
66+
/// options.RelativeLinks = true;
67+
/// </code>
68+
/// <code>
69+
/// {
70+
/// "type": "articles",
71+
/// "id": "4309",
72+
/// "relationships": {
73+
/// "author": {
74+
/// "links": {
75+
/// "self": "/api/v1/articles/4309/relationships/author",
76+
/// "related": "/api/v1/articles/4309/author"
77+
/// }
78+
/// }
79+
/// }
80+
/// }
81+
/// </code>
82+
/// </example>
2083
public bool RelativeLinks { get; set; }
84+
85+
/// <summary>
86+
/// Whether or not to allow all custom query parameters.
87+
/// </summary>
88+
/// <example>
89+
/// <code>
90+
/// options.AllowCustomQueryParameters = true;
91+
/// </code>
92+
/// </example>
2193
public bool AllowCustomQueryParameters { get; set; }
94+
95+
/// <summary>
96+
/// The default behavior for serializing null attributes.
97+
/// </summary>
98+
/// <example>
99+
/// <code>
100+
/// options.NullAttributeResponseBehavior = new NullAttributeResponseBehavior {
101+
/// // ...
102+
///};
103+
/// </code>
104+
/// </example>
22105
public NullAttributeResponseBehavior NullAttributeResponseBehavior { get; set; }
23106

107+
/// <summary>
108+
/// Whether or not to allow json:api v1.1 operation requests
109+
/// This will be enabled by default in JsonApiDotNetCore v2.2.1
110+
/// </summary>
111+
public bool EnableOperations { get; set; }
112+
24113
[Obsolete("JsonContract resolver can now be set on SerializerSettings.")]
25114
public IContractResolver JsonContractResolver
26115
{
@@ -33,9 +122,6 @@ public IContractResolver JsonContractResolver
33122
ContractResolver = new DasherizedResolver()
34123
};
35124

36-
internal IContextGraphBuilder ContextGraphBuilder { get; } = new ContextGraphBuilder();
37-
internal List<JsonApiExtension> EnabledExtensions { get; set; } = new List<JsonApiExtension>();
38-
39125
public void BuildContextGraph<TContext>(Action<IContextGraphBuilder> builder) where TContext : DbContext
40126
{
41127
BuildContextGraph(builder);
@@ -54,11 +140,10 @@ public void BuildContextGraph(Action<IContextGraphBuilder> builder)
54140
ContextGraph = ContextGraphBuilder.Build();
55141
}
56142

57-
public void EnableExtension(JsonApiExtension extension)
58-
{
59-
EnabledExtensions.Add(extension);
60-
}
61-
}
143+
public void EnableExtension(JsonApiExtension extension)
144+
=> EnabledExtensions.Add(extension);
62145

63-
146+
internal IContextGraphBuilder ContextGraphBuilder { get; } = new ContextGraphBuilder();
147+
internal List<JsonApiExtension> EnabledExtensions { get; set; } = new List<JsonApiExtension>();
148+
}
64149
}

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public static void AddJsonApiInternals(
9595
services.AddSingleton<DbContextOptions>(new DbContextOptionsBuilder().Options);
9696
}
9797

98-
if (jsonApiOptions.EnabledExtensions.Contains(JsonApiExtension.Operations))
98+
if (jsonApiOptions.EnableOperations)
9999
AddOperationServices(services);
100100

101101
services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>));

src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ public object Deserialize(string requestBody)
2929
{
3030
try
3131
{
32-
// TODO: determine whether or not the token should be re-used rather than performing full
33-
// deserialization again from the string
3432
var bodyJToken = JToken.Parse(requestBody);
35-
if(bodyJToken.SelectToken("operations") != null)
33+
34+
if(RequestIsOperation(bodyJToken))
3635
{
36+
// TODO: determine whether or not the token should be re-used rather than performing full
37+
// deserialization again from the string
3738
var operations = JsonConvert.DeserializeObject<OperationsDocument>(requestBody);
3839
if (operations == null)
3940
throw new JsonApiException(400, "Failed to deserialize operations request.");
@@ -53,6 +54,10 @@ public object Deserialize(string requestBody)
5354
}
5455
}
5556

57+
private bool RequestIsOperation(JToken bodyJToken)
58+
=> _jsonApiContext.Options.EnableOperations
59+
&& (bodyJToken.SelectToken("operations") != null);
60+
5661
public TEntity Deserialize<TEntity>(string requestBody) => (TEntity)Deserialize(requestBody);
5762

5863
public object DeserializeRelationship(string requestBody)

0 commit comments

Comments
 (0)