diff --git a/.vscode/launch.json b/.vscode/launch.json
index 4dfa71e085..b26b008078 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,41 +1,11 @@
{
"version": "0.2.0",
"configurations": [
- {
- "name": ".NET Core Launch (web)",
- "type": "coreclr",
- "request": "launch",
- "preLaunchTask": "build",
- "program": "${workspaceRoot}/src/JsonApiDotNetCoreExample/bin/Debug/netcoreapp1.0/JsonApiDotNetCoreExample.dll",
- "args": [],
- "cwd": "${workspaceRoot}/src/JsonApiDotNetCoreExample",
- "stopAtEntry": false,
- "launchBrowser": {
- "enabled": false,
- "args": "${auto-detect-url}",
- "windows": {
- "command": "cmd.exe",
- "args": "/C start ${auto-detect-url}"
- },
- "osx": {
- "command": "open"
- },
- "linux": {
- "command": "xdg-open"
- }
- },
- "env": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- },
- "sourceFileMap": {
- "/Views": "${workspaceRoot}/Views"
- }
- },
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
- "processId": "${command.pickProcess}"
+ "processId": "${command:pickProcess}"
}
]
}
\ No newline at end of file
diff --git a/README.md b/README.md
index f7b068f988..29b8bff318 100644
--- a/README.md
+++ b/README.md
@@ -44,14 +44,14 @@ Install-Package JsonApiDotnetCore
- project.json
```json
-"JsonApiDotNetCore": "1.1.0"
+"JsonApiDotNetCore": "1.2.0"
```
- *.csproj
```xml
-
+
```
@@ -326,6 +326,10 @@ Resources can be sorted by an attribute:
### Meta
+Meta objects can be assigned in two ways:
+ - Resource meta
+ - Request Meta
+
Resource meta can be defined by implementing `IHasMeta` on the model class:
```csharp
@@ -343,6 +347,9 @@ public class Person : Identifiable, IHasMeta
}
```
+Request Meta can be added by injecting a service that implements `IRequestMeta`.
+In the event of a key collision, the Request Meta will take precendence.
+
### Client Generated Ids
By default, the server will respond with a `403 Forbidden` HTTP Status Code if a `POST` request is
diff --git a/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs b/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs
index 2df9eaca4f..63829021d7 100644
--- a/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs
+++ b/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs
@@ -8,10 +8,11 @@
namespace JsonApiDotNetCore.Builders
{
- public class DocumentBuilder
+ public class DocumentBuilder : IDocumentBuilder
{
private IJsonApiContext _jsonApiContext;
private IContextGraph _contextGraph;
+ private readonly IRequestMeta _requestMeta;
public DocumentBuilder(IJsonApiContext jsonApiContext)
{
@@ -19,6 +20,13 @@ public DocumentBuilder(IJsonApiContext jsonApiContext)
_contextGraph = jsonApiContext.ContextGraph;
}
+ public DocumentBuilder(IJsonApiContext jsonApiContext, IRequestMeta requestMeta)
+ {
+ _jsonApiContext = jsonApiContext;
+ _contextGraph = jsonApiContext.ContextGraph;
+ _requestMeta = requestMeta;
+ }
+
public Document Build(IIdentifiable entity)
{
var contextEntity = _contextGraph.GetContextEntity(entity.GetType());
@@ -62,16 +70,19 @@ public Documents Build(IEnumerable entities)
private Dictionary _getMeta(IIdentifiable entity)
{
if (entity == null) return null;
-
- var meta = new Dictionary();
- var metaEntity = entity as IHasMeta;
- if(metaEntity != null)
- meta = metaEntity.GetMeta(_jsonApiContext);
+ var builder = _jsonApiContext.MetaBuilder;
+
+ if(entity is IHasMeta metaEntity)
+ builder.Add(metaEntity.GetMeta(_jsonApiContext));
if(_jsonApiContext.Options.IncludeTotalRecordCount)
- meta["total-records"] = _jsonApiContext.PageManager.TotalRecords;
+ builder.Add("total-records", _jsonApiContext.PageManager.TotalRecords);
+ if(_requestMeta != null)
+ builder.Add(_requestMeta.GetMeta());
+
+ var meta = builder.Build();
if(meta.Count > 0) return meta;
return null;
}
diff --git a/src/JsonApiDotNetCore/Builders/IDocumentBuilder.cs b/src/JsonApiDotNetCore/Builders/IDocumentBuilder.cs
new file mode 100644
index 0000000000..8fe5c65ae9
--- /dev/null
+++ b/src/JsonApiDotNetCore/Builders/IDocumentBuilder.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using JsonApiDotNetCore.Models;
+
+namespace JsonApiDotNetCore.Builders
+{
+ public interface IDocumentBuilder
+ {
+ Document Build(IIdentifiable entity);
+ Documents Build(IEnumerable entities);
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Builders/IMetaBuilder.cs b/src/JsonApiDotNetCore/Builders/IMetaBuilder.cs
new file mode 100644
index 0000000000..bf35b9d210
--- /dev/null
+++ b/src/JsonApiDotNetCore/Builders/IMetaBuilder.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace JsonApiDotNetCore.Builders
+{
+ public interface IMetaBuilder
+ {
+ void Add(string key, object value);
+ void Add(Dictionary values);
+ Dictionary Build();
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Builders/MetaBuilder.cs b/src/JsonApiDotNetCore/Builders/MetaBuilder.cs
new file mode 100644
index 0000000000..14b80321f6
--- /dev/null
+++ b/src/JsonApiDotNetCore/Builders/MetaBuilder.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace JsonApiDotNetCore.Builders
+{
+ public class MetaBuilder : IMetaBuilder
+ {
+ private Dictionary _meta = new Dictionary();
+
+ public void Add(string key, object value)
+ {
+ _meta[key] = value;
+ }
+
+ ///
+ /// Joins the new dictionary with the current one. In the event of a key collision,
+ /// the new value will override the old.
+ ///
+ public void Add(Dictionary values)
+ {
+ _meta = values.Keys.Union(_meta.Keys)
+ .ToDictionary(key => key,
+ key => values.ContainsKey(key) ? values[key] : _meta[key]);
+ }
+
+ public Dictionary Build()
+ {
+ return _meta;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs
index 5a44bb076c..4c98f8cec8 100644
--- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs
+++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs
@@ -32,6 +32,7 @@ public class DefaultEntityRepository
private readonly DbSet _dbSet;
private readonly ILogger _logger;
private readonly IJsonApiContext _jsonApiContext;
+ private readonly IGenericProcessorFactory _genericProcessorFactory;
public DefaultEntityRepository(
DbContext context,
@@ -42,6 +43,7 @@ public DefaultEntityRepository(
_dbSet = context.GetDbSet();
_jsonApiContext = jsonApiContext;
_logger = loggerFactory.CreateLogger>();
+ _genericProcessorFactory = _jsonApiContext.GenericProcessorFactory;
}
public virtual IQueryable Get()
@@ -110,7 +112,7 @@ public virtual async Task UpdateAsync(TId id, TEntity entity)
public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable relationshipIds)
{
- var genericProcessor = GenericProcessorFactory.GetProcessor(relationship.Type, _context);
+ var genericProcessor = _genericProcessorFactory.GetProcessor(relationship.Type);
await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds);
}
diff --git a/src/JsonApiDotNetCore/Extensions/ServiceProviderExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs
similarity index 79%
rename from src/JsonApiDotNetCore/Extensions/ServiceProviderExtensions.cs
rename to src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs
index 38face7307..1e646c085d 100644
--- a/src/JsonApiDotNetCore/Extensions/ServiceProviderExtensions.cs
+++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs
@@ -1,8 +1,10 @@
using System;
+using JsonApiDotNetCore.Builders;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Data;
using JsonApiDotNetCore.Formatters;
using JsonApiDotNetCore.Internal;
+using JsonApiDotNetCore.Serialization;
using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -11,7 +13,7 @@
namespace JsonApiDotNetCore.Extensions
{
- public static class ServiceProviderExtensions
+ public static class IServiceCollectionExtensions
{
public static void AddJsonApi(this IServiceCollection services)
where TContext : DbContext
@@ -54,6 +56,15 @@ public static void AddJsonApiInternals(this IServiceCollection service
services.AddSingleton();
services.AddScoped();
+
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped(typeof(GenericProcessor<>));
}
public static void SerializeAsJsonApi(this MvcOptions options, JsonApiOptions jsonApiOptions)
diff --git a/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs
new file mode 100644
index 0000000000..5b64cc42bf
--- /dev/null
+++ b/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs
@@ -0,0 +1,10 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Formatters;
+
+namespace JsonApiDotNetCore.Formatters
+{
+ public interface IJsonApiReader
+ {
+ Task ReadAsync(InputFormatterContext context);
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Formatters/IJsonApiWriter.cs b/src/JsonApiDotNetCore/Formatters/IJsonApiWriter.cs
new file mode 100644
index 0000000000..ce8b7da6a4
--- /dev/null
+++ b/src/JsonApiDotNetCore/Formatters/IJsonApiWriter.cs
@@ -0,0 +1,10 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Formatters;
+
+namespace JsonApiDotNetCore.Formatters
+{
+ public interface IJsonApiWriter
+ {
+ Task WriteAsync(OutputFormatterWriteContext context);
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiInputFormatter.cs b/src/JsonApiDotNetCore/Formatters/JsonApiInputFormatter.cs
index c4f1692eaa..12e57deadf 100644
--- a/src/JsonApiDotNetCore/Formatters/JsonApiInputFormatter.cs
+++ b/src/JsonApiDotNetCore/Formatters/JsonApiInputFormatter.cs
@@ -1,13 +1,7 @@
using System;
-using System.IO;
using System.Threading.Tasks;
-using JsonApiDotNetCore.Serialization;
-using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using Microsoft.EntityFrameworkCore;
namespace JsonApiDotNetCore.Formatters
{
@@ -23,55 +17,10 @@ public bool CanRead(InputFormatterContext context)
return contentTypeString == "application/vnd.api+json";
}
- public Task ReadAsync(InputFormatterContext context)
+ public async Task ReadAsync(InputFormatterContext context)
{
- if (context == null)
- throw new ArgumentNullException(nameof(context));
-
- var request = context.HttpContext.Request;
-
- if (request.ContentLength == 0)
- {
- return InputFormatterResult.SuccessAsync(null);
- }
-
- var loggerFactory = GetService(context);
- var logger = loggerFactory?.CreateLogger();
-
- var dbContext = GetService(context);
-
- try
- {
- var body = GetRequestBody(context.HttpContext.Request.Body);
- var jsonApiContext = GetService(context);
- var model = jsonApiContext.IsRelationshipPath ?
- JsonApiDeSerializer.DeserializeRelationship(body, jsonApiContext) :
- JsonApiDeSerializer.Deserialize(body, jsonApiContext, dbContext);
-
- if(model == null)
- logger?.LogError("An error occurred while de-serializing the payload");
-
- return InputFormatterResult.SuccessAsync(model);
- }
- catch (JsonSerializationException ex)
- {
- logger?.LogError(new EventId(), ex, "An error occurred while de-serializing the payload");
- context.HttpContext.Response.StatusCode = 422;
- return InputFormatterResult.FailureAsync();
- }
- }
-
- private string GetRequestBody(Stream body)
- {
- using (var reader = new StreamReader(body))
- {
- return reader.ReadToEnd();
- }
- }
-
- private TService GetService(InputFormatterContext context)
- {
- return context.HttpContext.RequestServices.GetService();
+ var reader = context.HttpContext.RequestServices.GetService();
+ return await reader.ReadAsync(context);
}
}
}
diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiOutputFormatter.cs b/src/JsonApiDotNetCore/Formatters/JsonApiOutputFormatter.cs
index 95e607fca5..2431055d1d 100644
--- a/src/JsonApiDotNetCore/Formatters/JsonApiOutputFormatter.cs
+++ b/src/JsonApiDotNetCore/Formatters/JsonApiOutputFormatter.cs
@@ -1,14 +1,7 @@
using System;
-using System.Text;
using System.Threading.Tasks;
-using JsonApiDotNetCore.Internal;
-using JsonApiDotNetCore.Models;
-using JsonApiDotNetCore.Serialization;
-using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
namespace JsonApiDotNetCore.Formatters
{
@@ -26,76 +19,8 @@ public bool CanWriteResult(OutputFormatterCanWriteContext context)
public async Task WriteAsync(OutputFormatterWriteContext context)
{
- if (context == null)
- throw new ArgumentNullException(nameof(context));
-
- var logger = GetService(context)?
- .CreateLogger();
-
- logger?.LogInformation("Formatting response as JSONAPI");
-
- var response = context.HttpContext.Response;
- using (var writer = context.WriterFactory(response.Body, Encoding.UTF8))
- {
- var jsonApiContext = GetService(context);
-
- response.ContentType = "application/vnd.api+json";
- string responseContent;
- try
- {
- responseContent = GetResponseBody(context.Object, jsonApiContext, logger);
- }
- catch (Exception e)
- {
- logger?.LogError(new EventId(), e, "An error ocurred while formatting the response");
- var errors = new ErrorCollection();
- errors.Add(new Error("400", e.Message));
- responseContent = errors.GetJson();
- response.StatusCode = 400;
- }
-
- await writer.WriteAsync(responseContent);
- await writer.FlushAsync();
- }
- }
-
- private T GetService(OutputFormatterWriteContext context)
- {
- return context.HttpContext.RequestServices.GetService();
- }
-
- private string GetResponseBody(object responseObject, IJsonApiContext jsonApiContext, ILogger logger)
- {
- if (responseObject == null)
- return GetNullDataResponse();
-
- if (responseObject.GetType() == typeof(Error) || jsonApiContext.RequestEntity == null)
- return GetErrorJson(responseObject, logger);
-
- return JsonApiSerializer.Serialize(responseObject, jsonApiContext);
- }
-
- private string GetNullDataResponse()
- {
- return JsonConvert.SerializeObject(new Document
- {
- Data = null
- });
- }
-
- private string GetErrorJson(object responseObject, ILogger logger)
- {
- if (responseObject.GetType() == typeof(Error))
- {
- var errors = new ErrorCollection();
- errors.Add((Error)responseObject);
- return errors.GetJson();
- }
- else
- {
- logger?.LogInformation("Response was not a JSONAPI entity. Serializing as plain JSON.");
- return JsonConvert.SerializeObject(responseObject);
- }
+ var writer = context.HttpContext.RequestServices.GetService();
+ await writer.WriteAsync(context);
}
}
}
diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs
new file mode 100644
index 0000000000..65a2382a73
--- /dev/null
+++ b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs
@@ -0,0 +1,64 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using JsonApiDotNetCore.Serialization;
+using JsonApiDotNetCore.Services;
+using Microsoft.AspNetCore.Mvc.Formatters;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+
+namespace JsonApiDotNetCore.Formatters
+{
+ public class JsonApiReader : IJsonApiReader
+ {
+ private readonly IJsonApiDeSerializer _deSerializer;
+ private readonly IJsonApiContext _jsonApiContext;
+ private readonly ILogger _logger;
+
+
+ public JsonApiReader(IJsonApiDeSerializer deSerializer, IJsonApiContext jsonApiContext, ILoggerFactory loggerFactory)
+ {
+ _deSerializer = deSerializer;
+ _jsonApiContext = jsonApiContext;
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ public Task ReadAsync(InputFormatterContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException(nameof(context));
+
+ var request = context.HttpContext.Request;
+ if (request.ContentLength == 0)
+ return InputFormatterResult.SuccessAsync(null);
+
+ try
+ {
+ var body = GetRequestBody(context.HttpContext.Request.Body);
+ var model = _jsonApiContext.IsRelationshipPath ?
+ _deSerializer.DeserializeRelationship(body) :
+ _deSerializer.Deserialize(body);
+
+ if(model == null)
+ _logger?.LogError("An error occurred while de-serializing the payload");
+
+ return InputFormatterResult.SuccessAsync(model);
+ }
+ catch (JsonSerializationException ex)
+ {
+ _logger?.LogError(new EventId(), ex, "An error occurred while de-serializing the payload");
+ context.HttpContext.Response.StatusCode = 422;
+ return InputFormatterResult.FailureAsync();
+ }
+ }
+
+ private string GetRequestBody(Stream body)
+ {
+ using (var reader = new StreamReader(body))
+ {
+ return reader.ReadToEnd();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs b/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
new file mode 100644
index 0000000000..8607026387
--- /dev/null
+++ b/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Text;
+using System.Threading.Tasks;
+using JsonApiDotNetCore.Internal;
+using JsonApiDotNetCore.Models;
+using JsonApiDotNetCore.Serialization;
+using JsonApiDotNetCore.Services;
+using Microsoft.AspNetCore.Mvc.Formatters;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+
+namespace JsonApiDotNetCore.Formatters
+{
+ public class JsonApiWriter : IJsonApiWriter
+ {
+ private readonly ILogger _logger;
+ private readonly IJsonApiContext _jsonApiContext;
+ private readonly IJsonApiSerializer _serializer;
+
+ public JsonApiWriter(IJsonApiContext jsonApiContext,
+ IJsonApiSerializer serializer,
+ ILoggerFactory loggerFactory)
+ {
+ _jsonApiContext = jsonApiContext;
+ _serializer = serializer;
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ public async Task WriteAsync(OutputFormatterWriteContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException(nameof(context));
+
+ _logger?.LogInformation("Formatting response as JSONAPI");
+
+ var response = context.HttpContext.Response;
+ using (var writer = context.WriterFactory(response.Body, Encoding.UTF8))
+ {
+ response.ContentType = "application/vnd.api+json";
+ string responseContent;
+ try
+ {
+ responseContent = GetResponseBody(context.Object);
+ }
+ catch (Exception e)
+ {
+ _logger?.LogError(new EventId(), e, "An error ocurred while formatting the response");
+ var errors = new ErrorCollection();
+ errors.Add(new Error("400", e.Message));
+ responseContent = errors.GetJson();
+ response.StatusCode = 400;
+ }
+
+ await writer.WriteAsync(responseContent);
+ await writer.FlushAsync();
+ }
+ }
+
+ private string GetResponseBody(object responseObject)
+ {
+ if (responseObject == null)
+ return GetNullDataResponse();
+
+ if (responseObject.GetType() == typeof(Error) || _jsonApiContext.RequestEntity == null)
+ return GetErrorJson(responseObject, _logger);
+
+ return _serializer.Serialize(responseObject);
+ }
+
+ private string GetNullDataResponse()
+ {
+ return JsonConvert.SerializeObject(new Document
+ {
+ Data = null
+ });
+ }
+
+ private string GetErrorJson(object responseObject, ILogger logger)
+ {
+ if (responseObject.GetType() == typeof(Error))
+ {
+ var errors = new ErrorCollection();
+ errors.Add((Error)responseObject);
+ return errors.GetJson();
+ }
+ else
+ {
+ logger?.LogInformation("Response was not a JSONAPI entity. Serializing as plain JSON.");
+ return JsonConvert.SerializeObject(responseObject);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs
index 24a963599a..7a647bf60f 100644
--- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs
+++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs
@@ -3,17 +3,22 @@
namespace JsonApiDotNetCore.Internal
{
- ///
- /// Used to generate a generic operations processor when the types
- /// are not know until runtime. The typical use case would be for
- /// accessing relationship data.
- ///
- public static class GenericProcessorFactory
+ public class GenericProcessorFactory : IGenericProcessorFactory
{
- public static IGenericProcessor GetProcessor(Type type, DbContext dbContext)
+ private readonly DbContext _dbContext;
+ private readonly IServiceProvider _serviceProvider;
+
+ public GenericProcessorFactory(DbContext dbContext,
+ IServiceProvider serviceProvider)
+ {
+ _dbContext = dbContext;
+ _serviceProvider = serviceProvider;
+ }
+
+ public IGenericProcessor GetProcessor(Type type)
{
- var repositoryType = typeof(GenericProcessor<>).MakeGenericType(type);
- return (IGenericProcessor)Activator.CreateInstance(repositoryType, dbContext);
+ var processorType = typeof(GenericProcessor<>).MakeGenericType(type);
+ return (IGenericProcessor)_serviceProvider.GetService(processorType);
}
}
}
diff --git a/src/JsonApiDotNetCore/Internal/Generics/IGenericProcessorFactory.cs b/src/JsonApiDotNetCore/Internal/Generics/IGenericProcessorFactory.cs
new file mode 100644
index 0000000000..ce959658c4
--- /dev/null
+++ b/src/JsonApiDotNetCore/Internal/Generics/IGenericProcessorFactory.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace JsonApiDotNetCore.Internal
+{
+ ///
+ /// Used to generate a generic operations processor when the types
+ /// are not know until runtime. The typical use case would be for
+ /// accessing relationship data.
+ ///
+ public interface IGenericProcessorFactory
+ {
+ IGenericProcessor GetProcessor(Type type);
+ }
+}
diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
index 9f0d438389..a222271081 100755
--- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
+++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
@@ -1,7 +1,7 @@
- 1.1.1
+ 1.2.0
netcoreapp1.0
JsonApiDotNetCore
JsonApiDotNetCore
diff --git a/src/JsonApiDotNetCore/Serialization/IJsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/IJsonApiDeSerializer.cs
new file mode 100644
index 0000000000..02f84a747a
--- /dev/null
+++ b/src/JsonApiDotNetCore/Serialization/IJsonApiDeSerializer.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace JsonApiDotNetCore.Serialization
+{
+ public interface IJsonApiDeSerializer
+ {
+ object Deserialize(string requestBody);
+ object DeserializeRelationship(string requestBody);
+ List DeserializeList(string requestBody);
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Serialization/IJsonApiSerializer.cs b/src/JsonApiDotNetCore/Serialization/IJsonApiSerializer.cs
new file mode 100644
index 0000000000..21eae09980
--- /dev/null
+++ b/src/JsonApiDotNetCore/Serialization/IJsonApiSerializer.cs
@@ -0,0 +1,7 @@
+namespace JsonApiDotNetCore.Serialization
+{
+ public interface IJsonApiSerializer
+ {
+ string Serialize(object entity);
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
index 59772d9a76..63187099ea 100644
--- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
+++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
@@ -8,23 +8,33 @@
using JsonApiDotNetCore.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-using System.Collections;
-using JsonApiDotNetCore.Data;
using Microsoft.EntityFrameworkCore;
namespace JsonApiDotNetCore.Serialization
{
- public static class JsonApiDeSerializer
+ public class JsonApiDeSerializer : IJsonApiDeSerializer
{
- public static object Deserialize(string requestBody, IJsonApiContext context,
- DbContext dbContext)
+ private readonly DbContext _dbContext;
+ private readonly IJsonApiContext _jsonApiContext;
+ private readonly IGenericProcessorFactory _genericProcessorFactor;
+
+ public JsonApiDeSerializer(DbContext dbContext,
+ IJsonApiContext jsonApiContext,
+ IGenericProcessorFactory genericProcessorFactory)
+ {
+ _dbContext = dbContext;
+ _jsonApiContext = jsonApiContext;
+ _genericProcessorFactor = genericProcessorFactory;
+ }
+
+ public object Deserialize(string requestBody)
{
var document = JsonConvert.DeserializeObject(requestBody);
- var entity = DataToObject(document.Data, context, dbContext);
+ var entity = DataToObject(document.Data);
return entity;
}
- public static object DeserializeRelationship(string requestBody, IJsonApiContext context)
+ public object DeserializeRelationship(string requestBody)
{
var data = JToken.Parse(requestBody)["data"];
@@ -35,34 +45,31 @@ public static object DeserializeRelationship(string requestBody, IJsonApiContext
}
- public static List DeserializeList(string requestBody, IJsonApiContext context,
- DbContext dbContext)
+ public List DeserializeList(string requestBody)
{
var documents = JsonConvert.DeserializeObject(requestBody);
var deserializedList = new List();
foreach (var data in documents.Data)
{
- var entity = DataToObject(data, context, dbContext);
+ var entity = DataToObject(data);
deserializedList.Add((TEntity)entity);
}
return deserializedList;
}
- private static object DataToObject(DocumentData data,
- IJsonApiContext context,
- DbContext dbContext)
+ private object DataToObject(DocumentData data)
{
var entityTypeName = data.Type.ToProperCase();
- var contextEntity = context.ContextGraph.GetContextEntity(entityTypeName);
- context.RequestEntity = contextEntity;
+ var contextEntity = _jsonApiContext.ContextGraph.GetContextEntity(entityTypeName);
+ _jsonApiContext.RequestEntity = contextEntity;
var entity = Activator.CreateInstance(contextEntity.EntityType);
entity = _setEntityAttributes(entity, contextEntity, data.Attributes);
- entity = _setRelationships(entity, contextEntity, data.Relationships, dbContext);
+ entity = _setRelationships(entity, contextEntity, data.Relationships);
var identifiableEntity = (IIdentifiable)entity;
@@ -72,7 +79,7 @@ private static object DataToObject(DocumentData data,
return identifiableEntity;
}
- private static object _setEntityAttributes(
+ private object _setEntityAttributes(
object entity, ContextEntity contextEntity, Dictionary attributeValues)
{
if (attributeValues == null || attributeValues.Count == 0)
@@ -98,11 +105,10 @@ private static object _setEntityAttributes(
return entity;
}
- private static object _setRelationships(
+ private object _setRelationships(
object entity,
ContextEntity contextEntity,
- Dictionary relationships,
- DbContext context)
+ Dictionary relationships)
{
if (relationships == null || relationships.Count == 0)
return entity;
@@ -114,13 +120,13 @@ private static object _setRelationships(
if (attr.IsHasOne)
entity = _setHasOneRelationship(entity, entityProperties, attr, contextEntity, relationships);
else
- entity = _setHasManyRelationship(entity, entityProperties, attr, contextEntity, relationships, context);
+ entity = _setHasManyRelationship(entity, entityProperties, attr, contextEntity, relationships);
}
return entity;
}
- private static object _setHasOneRelationship(object entity,
+ private object _setHasOneRelationship(object entity,
PropertyInfo[] entityProperties,
RelationshipAttribute attr,
ContextEntity contextEntity,
@@ -147,12 +153,11 @@ private static object _setHasOneRelationship(object entity,
return entity;
}
- private static object _setHasManyRelationship(object entity,
+ private object _setHasManyRelationship(object entity,
PropertyInfo[] entityProperties,
RelationshipAttribute attr,
ContextEntity contextEntity,
- Dictionary relationships,
- DbContext context)
+ Dictionary relationships)
{
var entityProperty = entityProperties.FirstOrDefault(p => p.Name == attr.InternalRelationshipName);
@@ -167,7 +172,7 @@ private static object _setHasManyRelationship(object entity,
if (data == null) return entity;
- var genericProcessor = GenericProcessorFactory.GetProcessor(attr.Type, context);
+ var genericProcessor = _genericProcessorFactor.GetProcessor(attr.Type);
var ids = relationshipData.ManyData.Select(r => r["id"]);
genericProcessor.SetRelationships(entity, attr, ids);
}
diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs
index 1fedd019a1..58bc9b19d0 100644
--- a/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs
+++ b/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs
@@ -1,37 +1,42 @@
using System.Collections.Generic;
using JsonApiDotNetCore.Builders;
using JsonApiDotNetCore.Models;
-using JsonApiDotNetCore.Services;
using Newtonsoft.Json;
namespace JsonApiDotNetCore.Serialization
{
- public static class JsonApiSerializer
+ public class JsonApiSerializer : IJsonApiSerializer
{
- public static string Serialize(object entity, IJsonApiContext jsonApiContext)
+ private readonly IDocumentBuilder _documentBuilder;
+
+ public JsonApiSerializer(IDocumentBuilder documentBuilder)
+ {
+ _documentBuilder = documentBuilder;
+ }
+
+ public string Serialize(object entity)
{
if (entity is IEnumerable)
- return _serializeDocuments(entity, jsonApiContext);
- return _serializeDocument(entity, jsonApiContext);
+ return _serializeDocuments(entity);
+
+ return _serializeDocument(entity);
}
- private static string _serializeDocuments(object entity, IJsonApiContext jsonApiContext)
+ private string _serializeDocuments(object entity)
{
- var documentBuilder = new DocumentBuilder(jsonApiContext);
var entities = entity as IEnumerable;
- var documents = documentBuilder.Build(entities);
+ var documents = _documentBuilder.Build(entities);
return _serialize(documents);
}
- private static string _serializeDocument(object entity, IJsonApiContext jsonApiContext)
+ private string _serializeDocument(object entity)
{
- var documentBuilder = new DocumentBuilder(jsonApiContext);
var identifiableEntity = entity as IIdentifiable;
- var document = documentBuilder.Build(identifiableEntity);
+ var document = _documentBuilder.Build(identifiableEntity);
return _serialize(document);
}
- private static string _serialize(object obj)
+ private string _serialize(object obj)
{
return JsonConvert.SerializeObject(obj, new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore
diff --git a/src/JsonApiDotNetCore/Services/IJsonApiContext.cs b/src/JsonApiDotNetCore/Services/IJsonApiContext.cs
index 1757109b71..f7d05dc4b3 100644
--- a/src/JsonApiDotNetCore/Services/IJsonApiContext.cs
+++ b/src/JsonApiDotNetCore/Services/IJsonApiContext.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using JsonApiDotNetCore.Builders;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Internal;
using JsonApiDotNetCore.Internal.Query;
@@ -17,6 +18,7 @@ public interface IJsonApiContext
List IncludedRelationships { get; set; }
bool IsRelationshipPath { get; }
PageManager PageManager { get; set; }
-
+ IMetaBuilder MetaBuilder { get; set; }
+ IGenericProcessorFactory GenericProcessorFactory { get; set; }
}
}
diff --git a/src/JsonApiDotNetCore/Services/IRequestMeta.cs b/src/JsonApiDotNetCore/Services/IRequestMeta.cs
new file mode 100644
index 0000000000..7dd5fdcada
--- /dev/null
+++ b/src/JsonApiDotNetCore/Services/IRequestMeta.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace JsonApiDotNetCore.Services
+{
+ public interface IRequestMeta
+ {
+ Dictionary GetMeta();
+ }
+}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Services/JsonApiContext.cs b/src/JsonApiDotNetCore/Services/JsonApiContext.cs
index 5ecd72872d..f9bd3f0b0f 100644
--- a/src/JsonApiDotNetCore/Services/JsonApiContext.cs
+++ b/src/JsonApiDotNetCore/Services/JsonApiContext.cs
@@ -14,11 +14,15 @@ public class JsonApiContext : IJsonApiContext
public JsonApiContext(
IContextGraph contextGraph,
IHttpContextAccessor httpContextAccessor,
- JsonApiOptions options)
+ JsonApiOptions options,
+ IMetaBuilder metaBuilder,
+ IGenericProcessorFactory genericProcessorFactory)
{
ContextGraph = contextGraph;
_httpContextAccessor = httpContextAccessor;
Options = options;
+ MetaBuilder = metaBuilder;
+ GenericProcessorFactory = genericProcessorFactory;
}
public JsonApiOptions Options { get; set; }
@@ -30,6 +34,8 @@ public JsonApiContext(
public bool IsRelationshipPath { get; private set; }
public List IncludedRelationships { get; set; }
public PageManager PageManager { get; set; }
+ public IMetaBuilder MetaBuilder { get; set; }
+ public IGenericProcessorFactory GenericProcessorFactory { get; set; }
public IJsonApiContext ApplyContext()
{
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/RepositoryOverrideTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/RepositoryOverrideTests.cs
index 7c7b145830..847d6eefff 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/RepositoryOverrideTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/RepositoryOverrideTests.cs
@@ -57,7 +57,7 @@ public async Task Total_Record_Count_Included()
// act
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(responseBody, jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(responseBody);
// assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/RequestMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/RequestMetaTests.cs
new file mode 100644
index 0000000000..a1b597f7d5
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Extensibility/RequestMetaTests.cs
@@ -0,0 +1,72 @@
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Xunit;
+using JsonApiDotNetCoreExample.Models;
+using DotNetCoreDocs;
+using JsonApiDotNetCoreExample;
+using DotNetCoreDocs.Writers;
+using Newtonsoft.Json;
+using JsonApiDotNetCore.Models;
+using System.Collections;
+using System.Diagnostics;
+using System.Threading;
+using JsonApiDotNetCoreExampleTests.Startups;
+
+namespace JsonApiDotNetCoreExampleTests.Acceptance.Extensibility
+{
+ [Collection("WebHostCollection")]
+ public class RequestMetaTests
+ {
+ private DocsFixture _fixture;
+
+ public RequestMetaTests(DocsFixture fixture)
+ {
+ _fixture = fixture;
+ }
+
+ [Fact]
+ public async Task Injecting_IRequestMeta_Adds_Meta_Data()
+ {
+ // arrange
+ var person = new Person();
+ var expectedMeta = person.GetMeta(null);
+ var builder = new WebHostBuilder()
+ .UseStartup();
+
+ var httpMethod = new HttpMethod("GET");
+ var route = $"/api/v1/people";
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+ var request = new HttpRequestMessage(httpMethod, route);
+
+ // act
+ var response = await client.SendAsync(request);
+ var documents = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
+
+ // assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.NotNull(documents.Meta);
+ Assert.NotNull(expectedMeta);
+ Assert.NotEmpty(expectedMeta);
+
+ foreach(var hash in expectedMeta)
+ {
+ if(hash.Value is IList)
+ {
+ var listValue = (IList)hash.Value;
+ for(var i=0; i < listValue.Count; i++)
+ Assert.Equal(listValue[i].ToString(), ((IList)documents.Meta[hash.Key])[i].ToString());
+ }
+ else
+ {
+ Assert.Equal(hash.Value, documents.Meta[hash.Key]);
+ }
+ }
+ Assert.Equal("request-meta-value", documents.Meta["request-meta"]);
+ }
+ }
+}
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs
index 25e2e3d6fe..52b399191f 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs
@@ -156,7 +156,7 @@ public async Task Can_Create_Entity_With_Client_Defined_Id_If_Configured()
// act
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItem)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, context);
+ var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body);
// assert
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
@@ -208,7 +208,7 @@ public async Task Can_Create_Guid_Identifiable_Entity_With_Client_Defined_Id_If_
// act
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItemCollection)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, context);
+ var deserializedBody = (TodoItemCollection)_fixture.GetService().Deserialize(body);
// assert
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
@@ -269,7 +269,7 @@ public async Task Can_Create_And_Set_HasMany_Relationships()
// act
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItemCollection)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, context);
+ var deserializedBody = (TodoItemCollection)_fixture.GetService().Deserialize(body);
var newId = deserializedBody.Id;
var contextCollection = context.TodoItemCollections
.Include(c => c.Owner)
@@ -312,7 +312,7 @@ public async Task ShouldReceiveLocationHeader_InResponse()
// act
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItem)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body);
// assert
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs
index e024786252..4fb7693d4c 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Meta.cs
@@ -10,11 +10,9 @@
using Xunit;
using JsonApiDotNetCore.Models;
using JsonApiDotNetCoreExample.Data;
-using System.Linq;
using JsonApiDotNetCoreExampleTests.Startups;
using JsonApiDotNetCoreExample.Models;
using System.Collections;
-using System;
namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec.DocumentTests
{
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs
index 7c1339f86c..075fc77129 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs
@@ -50,7 +50,7 @@ public async Task Request_ForEmptyCollection_Returns_EmptyDataCollection()
// act
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(body);
// assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs
index dfbd862232..ffb336e2c5 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs
@@ -1,8 +1,10 @@
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Threading;
using System.Threading.Tasks;
using Bogus;
using DotNetCoreDocs;
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs
index 0e9449ad86..84e398d4b9 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs
@@ -56,7 +56,7 @@ public async Task Can_Get_TodoItems()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -85,7 +85,7 @@ public async Task Can_Paginate_TodoItems()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -114,7 +114,7 @@ public async Task Can_Filter_TodoItems()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -144,7 +144,7 @@ public async Task Can_Filter_TodoItems_Using_Like_Operator()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -182,7 +182,7 @@ public async Task Can_Sort_TodoItems_By_Ordinal_Ascending()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -224,7 +224,7 @@ public async Task Can_Sort_TodoItems_By_Ordinal_Descending()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = JsonApiDeSerializer.DeserializeList(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = _fixture.GetService().DeserializeList(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -258,7 +258,7 @@ public async Task Can_Get_TodoItem_ById()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItem)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -288,7 +288,7 @@ public async Task Can_Get_TodoItem_WithOwner()
// Act
var response = await _fixture.MakeRequest(description, httpMethod, route);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItem)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@@ -343,7 +343,7 @@ public async Task Can_Post_TodoItem()
// Act
var response = await _fixture.MakeRequest(description, request);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItem)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body);
// Assert
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
@@ -390,7 +390,7 @@ public async Task Can_Patch_TodoItem()
// Act
var response = await _fixture.MakeRequest(description, request);
var body = await response.Content.ReadAsStringAsync();
- var deserializedBody = (TodoItem)JsonApiDeSerializer.Deserialize(body, _jsonApiContext, _fixture.GetService());
+ var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
diff --git a/test/JsonApiDotNetCoreExampleTests/Services/MetaService.cs b/test/JsonApiDotNetCoreExampleTests/Services/MetaService.cs
new file mode 100644
index 0000000000..91de8fda5e
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/Services/MetaService.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using JsonApiDotNetCore.Services;
+
+namespace JsonApiDotNetCoreExampleTests.Services
+{
+ public class MetaService : IRequestMeta
+ {
+ public Dictionary GetMeta()
+ {
+ return new Dictionary {
+ { "request-meta", "request-meta-value" }
+ };
+ }
+ }
+}
diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/MetaStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/MetaStartup.cs
index 0c84733632..b23fded9ba 100644
--- a/test/JsonApiDotNetCoreExampleTests/Startups/MetaStartup.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Startups/MetaStartup.cs
@@ -7,6 +7,8 @@
using DotNetCoreDocs.Configuration;
using System;
using JsonApiDotNetCoreExample;
+using JsonApiDotNetCore.Services;
+using JsonApiDotNetCoreExampleTests.Services;
namespace JsonApiDotNetCoreExampleTests.Startups
{
@@ -38,6 +40,7 @@ public override IServiceProvider ConfigureServices(IServiceCollection services)
});
services.AddDocumentationConfiguration(Config);
+ services.AddScoped();
return services.BuildServiceProvider();
}
diff --git a/test/JsonApiDotNetCoreExampleTests/Unit/Builders/MetaBuilderTests.cs b/test/JsonApiDotNetCoreExampleTests/Unit/Builders/MetaBuilderTests.cs
new file mode 100644
index 0000000000..5cd0b765de
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/Unit/Builders/MetaBuilderTests.cs
@@ -0,0 +1,73 @@
+using Xunit;
+using JsonApiDotNetCore.Builders;
+using System.Collections.Generic;
+
+namespace JsonApiDotNetCoreExampleTests.Unit.Builders
+{
+ public class MetaBuilderTests
+ {
+ [Fact]
+ public void Can_Add_Key_Value()
+ {
+ // arrange
+ var builder = new MetaBuilder();
+ var key = "test";
+ var value = "testValue";
+
+ // act
+ builder.Add(key, value);
+ var result = builder.Build();
+
+ // assert
+ Assert.NotEmpty(result);
+ Assert.Equal(value, result[key]);
+ }
+
+ [Fact]
+ public void Can_Add_Multiple_Values()
+ {
+ // arrange
+ var builder = new MetaBuilder();
+ var input = new Dictionary {
+ { "key1", "value1" },
+ { "key2", "value2" }
+ };
+
+ // act
+ builder.Add(input);
+ var result = builder.Build();
+
+ // assert
+ Assert.NotEmpty(result);
+ foreach (var entry in input)
+ Assert.Equal(input[entry.Key], result[entry.Key]);
+ }
+
+ [Fact]
+ public void When_Adding_Duplicate_Values_Keep_Newest()
+ {
+ // arrange
+ var builder = new MetaBuilder();
+
+ var key = "key";
+ var oldValue = "oldValue";
+ var newValue = "newValue";
+
+ builder.Add(key, oldValue);
+
+ var input = new Dictionary {
+ { key, newValue },
+ { "key2", "value2" }
+ };
+
+ // act
+ builder.Add(input);
+ var result = builder.Build();
+
+ // assert
+ Assert.NotEmpty(result);
+ Assert.Equal(input.Count, result.Count);
+ Assert.Equal(input[key], result[key]);
+ }
+ }
+}
diff --git a/test/JsonApiDotNetCoreExampleTests/Unit/Extensions/IServiceCollectionExtensionsTests.cs b/test/JsonApiDotNetCoreExampleTests/Unit/Extensions/IServiceCollectionExtensionsTests.cs
new file mode 100644
index 0000000000..71a952994a
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/Unit/Extensions/IServiceCollectionExtensionsTests.cs
@@ -0,0 +1,54 @@
+using Xunit;
+using JsonApiDotNetCore.Builders;
+using Microsoft.Extensions.DependencyInjection;
+using JsonApiDotNetCore.Extensions;
+using JsonApiDotNetCore.Configuration;
+using Microsoft.EntityFrameworkCore;
+using JsonApiDotNetCore.Data;
+using JsonApiDotNetCore.Internal;
+using Microsoft.AspNetCore.Http;
+using JsonApiDotNetCore.Services;
+using JsonApiDotNetCoreExample.Data;
+using Microsoft.Extensions.Caching.Memory;
+using JsonApiDotNetCoreExample.Models;
+using JsonApiDotNetCore.Serialization;
+using JsonApiDotNetCore.Formatters;
+
+namespace JsonApiDotNetCoreExampleTests.Unit.Extensions
+{
+ public class IServiceCollectionExtensionsTests
+ {
+ [Fact]
+ public void AddJsonApiInternals_Adds_All_Required_Services()
+ {
+ // arrange
+ var services = new ServiceCollection();
+ var jsonApiOptions = new JsonApiOptions();
+
+ services.AddDbContext(options =>
+ {
+ options.UseMemoryCache(new MemoryCache(new MemoryCacheOptions()));
+ }, ServiceLifetime.Transient);
+
+ // act
+ services.AddJsonApiInternals(jsonApiOptions);
+ var provider = services.BuildServiceProvider();
+
+ // assert
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService(typeof(IEntityRepository)));
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService());
+ Assert.NotNull(provider.GetService(typeof(GenericProcessor)));
+ }
+ }
+}