Skip to content

Commit 8b1c537

Browse files
authored
Merge pull request #59 from Research-Institute/develop
fixes around id types
2 parents 214f7d0 + 3d51f1c commit 8b1c537

34 files changed

+588
-449
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
\.vs/

Build.ps1

+11-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,14 @@ $revision = "{0:D4}" -f [convert]::ToInt32($revision, 10)
44
dotnet restore .\src\JsonApiDotNetCore\JsonApiDotNetCore.csproj
55
dotnet build .\src\JsonApiDotNetCore -c Release
66

7-
If($env:APPVEYOR_REPO_TAG) { dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts }
8-
Else { dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=$revision }
7+
echo "APPVEYOR_REPO_TAG: $env:APPVEYOR_REPO_TAG"
8+
echo "VERSION-SUFFIX: alpha1-$revision"
9+
10+
If($env:APPVEYOR_REPO_TAG -eq $true) {
11+
echo "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts "
12+
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts
13+
}
14+
Else {
15+
echo "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision"
16+
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision
17+
}

JsonApiDotnetCore.sln

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.26228.4
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{C0EC9E70-EB2E-436F-9D94-FA16FA774123}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCoreExample", "src\JsonApiDotNetCoreExample\JsonApiDotNetCoreExample.csproj", "{97EE048B-16C0-43F6-BDA9-4E762B2F579F}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}"
11+
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}"
13+
EndProject
14+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCoreExampleTests", "test\JsonApiDotNetCoreExampleTests\JsonApiDotNetCoreExampleTests.csproj", "{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}"
15+
EndProject
16+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C5B4D998-CECB-454D-9F32-085A897577BE}"
17+
ProjectSection(SolutionItems) = preProject
18+
.gitignore = .gitignore
19+
EndProjectSection
20+
EndProject
21+
Global
22+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
23+
Debug|Any CPU = Debug|Any CPU
24+
Release|Any CPU = Release|Any CPU
25+
EndGlobalSection
26+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
27+
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28+
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Debug|Any CPU.Build.0 = Debug|Any CPU
29+
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Release|Any CPU.ActiveCfg = Release|Any CPU
30+
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Release|Any CPU.Build.0 = Release|Any CPU
31+
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32+
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Debug|Any CPU.Build.0 = Debug|Any CPU
33+
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Release|Any CPU.ActiveCfg = Release|Any CPU
34+
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Release|Any CPU.Build.0 = Release|Any CPU
35+
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36+
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
37+
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Release|Any CPU.Build.0 = Release|Any CPU
39+
EndGlobalSection
40+
GlobalSection(SolutionProperties) = preSolution
41+
HideSolutionNode = FALSE
42+
EndGlobalSection
43+
GlobalSection(NestedProjects) = preSolution
44+
{C0EC9E70-EB2E-436F-9D94-FA16FA774123} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
45+
{97EE048B-16C0-43F6-BDA9-4E762B2F579F} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
46+
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
47+
EndGlobalSection
48+
EndGlobal

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ JsonApiDotnetCore provides a framework for building [json:api](http://jsonapi.or
2626
- [Filtering](#filtering)
2727
- [Sorting](#sorting)
2828
- [Meta](#meta)
29+
- [Client Generated Ids](#client-generated-ids)
2930
- [Tests](#tests)
3031

3132
## Comprehensive Demo
@@ -342,6 +343,20 @@ public class Person : Identifiable<int>, IHasMeta
342343
}
343344
```
344345

346+
### Client Generated Ids
347+
348+
By default, the server will respond with a `403 Forbidden` HTTP Status Code if a `POST` request is
349+
received with a client generated id. However, this can be allowed by setting the `AllowClientGeneratedIds`
350+
flag in the options:
351+
352+
```csharp
353+
services.AddJsonApi<AppDbContext>(opt =>
354+
{
355+
opt.AllowClientGeneratedIds = true;
356+
// ..
357+
});
358+
```
359+
345360
## Tests
346361

347362
I am using DotNetCoreDocs to generate sample requests and documentation.

build.sh

+1-12
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,8 @@
33
#exit if any command fails
44
set -e
55

6-
artifactsFolder="./artifacts"
7-
8-
if [ -d $artifactsFolder ]; then
9-
rm -R $artifactsFolder
10-
fi
11-
126
dotnet restore ./src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
137
dotnet restore ./src/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
148
dotnet restore ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj
159

16-
dotnet test ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj
17-
18-
revision=${TRAVIS_JOB_ID:=1}
19-
revision=$(printf "%04d" $revision)
20-
21-
dotnet pack ./src/JsonApiDotNetCore -c Release -o ./artifacts --version-suffix=$revision
10+
dotnet test ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj

src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ private DocumentData _getData(ContextEntity contextEntity, IIdentifiable entity)
9494
var data = new DocumentData
9595
{
9696
Type = contextEntity.EntityName,
97-
Id = entity.Id.ToString()
97+
Id = entity.StringId
9898
};
9999

100100
if (_jsonApiContext.IsRelationshipData)
@@ -124,8 +124,8 @@ private void _addRelationships(DocumentData data, ContextEntity contextEntity, I
124124
{
125125
Links = new Links
126126
{
127-
Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName),
128-
Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName)
127+
Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.StringId, r.InternalRelationshipName),
128+
Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.StringId, r.InternalRelationshipName)
129129
}
130130
};
131131

@@ -175,7 +175,7 @@ private DocumentData _getIncludedEntity(IIdentifiable entity)
175175
var data = new DocumentData
176176
{
177177
Type = contextEntity.EntityName,
178-
Id = entity.Id.ToString()
178+
Id = entity.StringId
179179
};
180180

181181
data.Attributes = new Dictionary<string, object>();
@@ -205,7 +205,7 @@ private List<Dictionary<string, string>> _getRelationships(IEnumerable<object> e
205205
{
206206
relationships.Add(new Dictionary<string, string> {
207207
{"type", typeName.EntityName.Dasherize() },
208-
{"id", ((IIdentifiable)entity).Id.ToString() }
208+
{"id", ((IIdentifiable)entity).StringId }
209209
});
210210
}
211211
return relationships;
@@ -218,7 +218,7 @@ private Dictionary<string, string> _getRelationship(object entity, string relati
218218

219219
return new Dictionary<string, string> {
220220
{"type", typeName.EntityName.Dasherize() },
221-
{"id", ((IIdentifiable)entity).Id.ToString() }
221+
{"id", ((IIdentifiable)entity).StringId }
222222
};
223223
}
224224
}

src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ public class JsonApiOptions
55
public string Namespace { get; set; }
66
public int DefaultPageSize { get; set; }
77
public bool IncludeTotalRecordCount { get; set; }
8+
public bool AllowClientGeneratedIds { get; set; }
89
}
910
}

src/JsonApiDotNetCore/Controllers/JsonApiController.cs

+14-15
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace JsonApiDotNetCore.Controllers
1515
{
16-
public class JsonApiController<T>
16+
public class JsonApiController<T>
1717
: JsonApiController<T, int> where T : class, IIdentifiable<int>
1818
{
1919
public JsonApiController(
@@ -24,7 +24,7 @@ public JsonApiController(
2424
{ }
2525
}
2626

27-
public class JsonApiController<T, TId>
27+
public class JsonApiController<T, TId>
2828
: JsonApiControllerMixin where T : class, IIdentifiable<TId>
2929
{
3030
private readonly IEntityRepository<T, TId> _entities;
@@ -77,7 +77,7 @@ public virtual async Task<IActionResult> GetAsync()
7777
public virtual async Task<IActionResult> GetAsync(TId id)
7878
{
7979
T entity;
80-
if(_jsonApiContext.QuerySet?.IncludedRelationships != null)
80+
if (_jsonApiContext.QuerySet?.IncludedRelationships != null)
8181
entity = await _getWithRelationshipsAsync(id);
8282
else
8383
entity = await _entities.GetAsync(id);
@@ -138,8 +138,7 @@ public virtual async Task<IActionResult> PostAsync([FromBody] T entity)
138138
return UnprocessableEntity();
139139
}
140140

141-
var stringId = entity.Id.ToString();
142-
if(stringId.Length > 0 && stringId != "0")
141+
if (!_jsonApiContext.Options.AllowClientGeneratedIds && !string.IsNullOrEmpty(entity.StringId))
143142
return Forbidden();
144143

145144
await _entities.CreateAsync(entity);
@@ -158,7 +157,7 @@ public virtual async Task<IActionResult> PatchAsync(TId id, [FromBody] T entity)
158157

159158
var updatedEntity = await _entities.UpdateAsync(id, entity);
160159

161-
if(updatedEntity == null) return NotFound();
160+
if (updatedEntity == null) return NotFound();
162161

163162
return Ok(updatedEntity);
164163
}
@@ -185,12 +184,12 @@ public virtual async Task<IActionResult> PatchRelationshipsAsync(TId id, string
185184
.Relationships
186185
.FirstOrDefault(r => r.InternalRelationshipName == relationshipName);
187186

188-
var relationshipIds = relationships.Select(r=>r.Id);
189-
187+
var relationshipIds = relationships.Select(r => r.Id);
188+
190189
await _entities.UpdateRelationshipsAsync(entity, relationship, relationshipIds);
191190

192191
return Ok();
193-
192+
194193
}
195194

196195
[HttpDelete("{id}")]
@@ -208,14 +207,14 @@ private IQueryable<T> ApplySortAndFilterQuery(IQueryable<T> entities)
208207
{
209208
var query = _jsonApiContext.QuerySet;
210209

211-
if(_jsonApiContext.QuerySet == null)
210+
if (_jsonApiContext.QuerySet == null)
212211
return entities;
213212

214-
if(query.Filters.Count > 0)
215-
foreach(var filter in query.Filters)
213+
if (query.Filters.Count > 0)
214+
foreach (var filter in query.Filters)
216215
entities = _entities.Filter(entities, filter);
217216

218-
if(query.SortParameters != null && query.SortParameters.Count > 0)
217+
if (query.SortParameters != null && query.SortParameters.Count > 0)
219218
entities = _entities.Sort(entities, query.SortParameters);
220219

221220
return entities;
@@ -224,7 +223,7 @@ private IQueryable<T> ApplySortAndFilterQuery(IQueryable<T> entities)
224223
private async Task<IEnumerable<T>> ApplyPageQueryAsync(IQueryable<T> entities)
225224
{
226225
var pageManager = _jsonApiContext.PageManager;
227-
if(!pageManager.IsPaginated)
226+
if (!pageManager.IsPaginated)
228227
return entities;
229228

230229
var query = _jsonApiContext.QuerySet?.PageQuery ?? new PageQuery();
@@ -238,7 +237,7 @@ private IQueryable<T> IncludeRelationships(IQueryable<T> entities, List<string>
238237
{
239238
_jsonApiContext.IncludedRelationships = relationships;
240239

241-
foreach(var r in relationships)
240+
foreach (var r in relationships)
242241
entities = _entities.Include(entities, r.ToProperCase());
243242

244243
return entities;

src/JsonApiDotNetCore/Formatters/JsonApiInputFormatter.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.Extensions.DependencyInjection;
88
using Microsoft.Extensions.Logging;
99
using Newtonsoft.Json;
10+
using Microsoft.EntityFrameworkCore;
1011

1112
namespace JsonApiDotNetCore.Formatters
1213
{
@@ -37,13 +38,15 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
3738
var loggerFactory = GetService<ILoggerFactory>(context);
3839
var logger = loggerFactory?.CreateLogger<JsonApiInputFormatter>();
3940

41+
var dbContext = GetService<DbContext>(context);
42+
4043
try
4144
{
4245
var body = GetRequestBody(context.HttpContext.Request.Body);
4346
var jsonApiContext = GetService<IJsonApiContext>(context);
4447
var model = jsonApiContext.IsRelationshipPath ?
4548
JsonApiDeSerializer.DeserializeRelationship(body, jsonApiContext) :
46-
JsonApiDeSerializer.Deserialize(body, jsonApiContext);
49+
JsonApiDeSerializer.Deserialize(body, jsonApiContext, dbContext);
4750

4851
if(model == null)
4952
logger?.LogError("An error occurred while de-serializing the payload");

src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
using System.Collections;
21
using System.Collections.Generic;
32
using System.Linq;
4-
using System.Reflection;
53
using System.Threading.Tasks;
64
using JsonApiDotNetCore.Extensions;
75
using JsonApiDotNetCore.Models;
@@ -18,21 +16,26 @@ public GenericProcessor(DbContext context)
1816
}
1917

2018
public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
19+
{
20+
SetRelationships(parent, relationship, relationshipIds);
21+
22+
await _context.SaveChangesAsync();
23+
}
24+
25+
public void SetRelationships(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
2126
{
2227
var relationshipType = relationship.Type;
2328

24-
if(relationship.IsHasMany)
29+
if (relationship.IsHasMany)
2530
{
26-
var entities = _context.GetDbSet<T>().Where(x => relationshipIds.Contains(x.Id.ToString())).ToList();
31+
var entities = _context.GetDbSet<T>().Where(x => relationshipIds.Contains(x.StringId)).ToList();
2732
relationship.SetValue(parent, entities);
2833
}
2934
else
3035
{
31-
var entity = _context.GetDbSet<T>().SingleOrDefault(x => relationshipIds.First() == x.Id.ToString());
36+
var entity = _context.GetDbSet<T>().SingleOrDefault(x => relationshipIds.First() == x.StringId);
3237
relationship.SetValue(parent, entity);
33-
}
34-
35-
await _context.SaveChangesAsync();
38+
}
3639
}
3740
}
3841
}

src/JsonApiDotNetCore/Internal/Generics/IGenericProcessor.cs

+1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ namespace JsonApiDotNetCore.Internal
77
public interface IGenericProcessor
88
{
99
Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
10+
void SetRelationships(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
1011
}
1112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Reflection;
3+
4+
namespace JsonApiDotNetCore.Internal
5+
{
6+
public static class TypeHelper
7+
{
8+
public static object ConvertType(object value, Type type)
9+
{
10+
if(value == null)
11+
return null;
12+
13+
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
14+
type = Nullable.GetUnderlyingType(type);
15+
16+
var stringValue = value.ToString();
17+
18+
if(type == typeof(Guid))
19+
return Guid.Parse(stringValue);
20+
21+
return Convert.ChangeType(stringValue, type);
22+
}
23+
}
24+
}

src/JsonApiDotNetCore/JsonApiDotNetCore.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<VersionPrefix>1.1.0</VersionPrefix>
4+
<VersionPrefix>1.1.1</VersionPrefix>
55
<TargetFramework>netcoreapp1.0</TargetFramework>
66
<AssemblyName>JsonApiDotNetCore</AssemblyName>
77
<PackageId>JsonApiDotNetCore</PackageId>

src/JsonApiDotNetCore/Models/IIdentifiable.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ namespace JsonApiDotNetCore.Models
22
{
33
public interface IIdentifiable
44
{
5-
object Id { get; set; }
5+
string StringId { get; set; }
66
}
77

88
public interface IIdentifiable<T> : IIdentifiable
99
{
10-
new T Id { get; set; }
10+
T Id { get; set; }
1111
}
1212
}

0 commit comments

Comments
 (0)