Skip to content

fixes around id types #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Mar 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
41a793d
Merged branch master into develop
jaredcnance Mar 14, 2017
a0a4e51
fix(identifiable): use a string id instead of the object id
jaredcnance Mar 15, 2017
1a86c9a
refactor(Identifiable): rename property to StringId
jaredcnance Mar 15, 2017
fb4ebac
refactor(jsonapi-controller): use StringId to check User defined ids
jaredcnance Mar 15, 2017
e53fd07
refactor(example): make collections use Guid Ids
jaredcnance Mar 15, 2017
d217336
test(post): failing test when creating Guid id entities
jaredcnance Mar 15, 2017
cc53963
feat(generic-processor): add public method for setting relationships
jaredcnance Mar 15, 2017
cedea74
test(creating-data): can create new entities with hasMany relationships
jaredcnance Mar 15, 2017
624d0c2
fix(example): pluralize todo Items collection
jaredcnance Mar 15, 2017
2e013ac
fix(jsonapi-deserializer): had method for de-serializing hasMany
jaredcnance Mar 15, 2017
6c5bb94
fix(*): update references to the Deserializer
jaredcnance Mar 15, 2017
bf9a5f8
chore: add sln file
jaredcnance Mar 15, 2017
6d259d8
chor(csproj): bump version to pre-release
jaredcnance Mar 15, 2017
3658069
fix(ci): update to use new csproj version prefix method
jaredcnance Mar 15, 2017
171b9d2
debug(build.ps1): check what the value of APPVEYOR_REPO_TAG is
jaredcnance Mar 15, 2017
d2fff47
debug(build.ps1): continuing to figure out why appveyor is not packin…
jaredcnance Mar 15, 2017
26b04e3
debug(appveyor): log the command being executed
jaredcnance Mar 15, 2017
ce5d860
fix(build.ps1): fix if statement syntax
jaredcnance Mar 15, 2017
f7e3e1b
Merge pull request #55 from Research-Institute/fix/id-type-support
jaredcnance Mar 15, 2017
6cbc1cd
chore(csproj): bump package version
jaredcnance Mar 15, 2017
9c38749
feat(jsonapi-options): add option for client generated ids
jaredcnance Mar 16, 2017
8534691
test(post): can create entities with client generated ids
jaredcnance Mar 16, 2017
229b53c
feat(jsonapi-controller): allow client generated ids if configured
jaredcnance Mar 16, 2017
96bcc18
docs(readme): document client generated id option
jaredcnance Mar 16, 2017
3d51f1c
Merge pull request #58 from Research-Institute/feature/client-generat…
jaredcnance Mar 16, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
\.vs/
13 changes: 11 additions & 2 deletions Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@ $revision = "{0:D4}" -f [convert]::ToInt32($revision, 10)
dotnet restore .\src\JsonApiDotNetCore\JsonApiDotNetCore.csproj
dotnet build .\src\JsonApiDotNetCore -c Release

If($env:APPVEYOR_REPO_TAG) { dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts }
Else { dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=$revision }
echo "APPVEYOR_REPO_TAG: $env:APPVEYOR_REPO_TAG"
echo "VERSION-SUFFIX: alpha1-$revision"

If($env:APPVEYOR_REPO_TAG -eq $true) {
echo "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts "
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts
}
Else {
echo "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision"
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision
}
48 changes: 48 additions & 0 deletions JsonApiDotnetCore.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{C0EC9E70-EB2E-436F-9D94-FA16FA774123}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCoreExample", "src\JsonApiDotNetCoreExample\JsonApiDotNetCoreExample.csproj", "{97EE048B-16C0-43F6-BDA9-4E762B2F579F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCoreExampleTests", "test\JsonApiDotNetCoreExampleTests\JsonApiDotNetCoreExampleTests.csproj", "{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C5B4D998-CECB-454D-9F32-085A897577BE}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0EC9E70-EB2E-436F-9D94-FA16FA774123}.Release|Any CPU.Build.0 = Release|Any CPU
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97EE048B-16C0-43F6-BDA9-4E762B2F579F}.Release|Any CPU.Build.0 = Release|Any CPU
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C0EC9E70-EB2E-436F-9D94-FA16FA774123} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
{97EE048B-16C0-43F6-BDA9-4E762B2F579F} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
EndGlobalSection
EndGlobal
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ JsonApiDotnetCore provides a framework for building [json:api](http://jsonapi.or
- [Filtering](#filtering)
- [Sorting](#sorting)
- [Meta](#meta)
- [Client Generated Ids](#client-generated-ids)
- [Tests](#tests)

## Comprehensive Demo
Expand Down Expand Up @@ -342,6 +343,20 @@ public class Person : Identifiable<int>, IHasMeta
}
```

### Client Generated Ids

By default, the server will respond with a `403 Forbidden` HTTP Status Code if a `POST` request is
received with a client generated id. However, this can be allowed by setting the `AllowClientGeneratedIds`
flag in the options:

```csharp
services.AddJsonApi<AppDbContext>(opt =>
{
opt.AllowClientGeneratedIds = true;
// ..
});
```

## Tests

I am using DotNetCoreDocs to generate sample requests and documentation.
Expand Down
13 changes: 1 addition & 12 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,8 @@
#exit if any command fails
set -e

artifactsFolder="./artifacts"

if [ -d $artifactsFolder ]; then
rm -R $artifactsFolder
fi

dotnet restore ./src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
dotnet restore ./src/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
dotnet restore ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj

dotnet test ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj

revision=${TRAVIS_JOB_ID:=1}
revision=$(printf "%04d" $revision)

dotnet pack ./src/JsonApiDotNetCore -c Release -o ./artifacts --version-suffix=$revision
dotnet test ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj
12 changes: 6 additions & 6 deletions src/JsonApiDotNetCore/Builders/DocumentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private DocumentData _getData(ContextEntity contextEntity, IIdentifiable entity)
var data = new DocumentData
{
Type = contextEntity.EntityName,
Id = entity.Id.ToString()
Id = entity.StringId
};

if (_jsonApiContext.IsRelationshipData)
Expand Down Expand Up @@ -124,8 +124,8 @@ private void _addRelationships(DocumentData data, ContextEntity contextEntity, I
{
Links = new Links
{
Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName),
Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName)
Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.StringId, r.InternalRelationshipName),
Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.StringId, r.InternalRelationshipName)
}
};

Expand Down Expand Up @@ -175,7 +175,7 @@ private DocumentData _getIncludedEntity(IIdentifiable entity)
var data = new DocumentData
{
Type = contextEntity.EntityName,
Id = entity.Id.ToString()
Id = entity.StringId
};

data.Attributes = new Dictionary<string, object>();
Expand Down Expand Up @@ -205,7 +205,7 @@ private List<Dictionary<string, string>> _getRelationships(IEnumerable<object> e
{
relationships.Add(new Dictionary<string, string> {
{"type", typeName.EntityName.Dasherize() },
{"id", ((IIdentifiable)entity).Id.ToString() }
{"id", ((IIdentifiable)entity).StringId }
});
}
return relationships;
Expand All @@ -218,7 +218,7 @@ private Dictionary<string, string> _getRelationship(object entity, string relati

return new Dictionary<string, string> {
{"type", typeName.EntityName.Dasherize() },
{"id", ((IIdentifiable)entity).Id.ToString() }
{"id", ((IIdentifiable)entity).StringId }
};
}
}
Expand Down
1 change: 1 addition & 0 deletions src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ public class JsonApiOptions
public string Namespace { get; set; }
public int DefaultPageSize { get; set; }
public bool IncludeTotalRecordCount { get; set; }
public bool AllowClientGeneratedIds { get; set; }
}
}
29 changes: 14 additions & 15 deletions src/JsonApiDotNetCore/Controllers/JsonApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace JsonApiDotNetCore.Controllers
{
public class JsonApiController<T>
public class JsonApiController<T>
: JsonApiController<T, int> where T : class, IIdentifiable<int>
{
public JsonApiController(
Expand All @@ -24,7 +24,7 @@ public JsonApiController(
{ }
}

public class JsonApiController<T, TId>
public class JsonApiController<T, TId>
: JsonApiControllerMixin where T : class, IIdentifiable<TId>
{
private readonly IEntityRepository<T, TId> _entities;
Expand Down Expand Up @@ -77,7 +77,7 @@ public virtual async Task<IActionResult> GetAsync()
public virtual async Task<IActionResult> GetAsync(TId id)
{
T entity;
if(_jsonApiContext.QuerySet?.IncludedRelationships != null)
if (_jsonApiContext.QuerySet?.IncludedRelationships != null)
entity = await _getWithRelationshipsAsync(id);
else
entity = await _entities.GetAsync(id);
Expand Down Expand Up @@ -138,8 +138,7 @@ public virtual async Task<IActionResult> PostAsync([FromBody] T entity)
return UnprocessableEntity();
}

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

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

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

if(updatedEntity == null) return NotFound();
if (updatedEntity == null) return NotFound();

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

var relationshipIds = relationships.Select(r=>r.Id);
var relationshipIds = relationships.Select(r => r.Id);

await _entities.UpdateRelationshipsAsync(entity, relationship, relationshipIds);

return Ok();

}

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

if(_jsonApiContext.QuerySet == null)
if (_jsonApiContext.QuerySet == null)
return entities;

if(query.Filters.Count > 0)
foreach(var filter in query.Filters)
if (query.Filters.Count > 0)
foreach (var filter in query.Filters)
entities = _entities.Filter(entities, filter);

if(query.SortParameters != null && query.SortParameters.Count > 0)
if (query.SortParameters != null && query.SortParameters.Count > 0)
entities = _entities.Sort(entities, query.SortParameters);

return entities;
Expand All @@ -224,7 +223,7 @@ private IQueryable<T> ApplySortAndFilterQuery(IQueryable<T> entities)
private async Task<IEnumerable<T>> ApplyPageQueryAsync(IQueryable<T> entities)
{
var pageManager = _jsonApiContext.PageManager;
if(!pageManager.IsPaginated)
if (!pageManager.IsPaginated)
return entities;

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

foreach(var r in relationships)
foreach (var r in relationships)
entities = _entities.Include(entities, r.ToProperCase());

return entities;
Expand Down
5 changes: 4 additions & 1 deletion src/JsonApiDotNetCore/Formatters/JsonApiInputFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.EntityFrameworkCore;

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

var dbContext = GetService<DbContext>(context);

try
{
var body = GetRequestBody(context.HttpContext.Request.Body);
var jsonApiContext = GetService<IJsonApiContext>(context);
var model = jsonApiContext.IsRelationshipPath ?
JsonApiDeSerializer.DeserializeRelationship(body, jsonApiContext) :
JsonApiDeSerializer.Deserialize(body, jsonApiContext);
JsonApiDeSerializer.Deserialize(body, jsonApiContext, dbContext);

if(model == null)
logger?.LogError("An error occurred while de-serializing the payload");
Expand Down
19 changes: 11 additions & 8 deletions src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using JsonApiDotNetCore.Extensions;
using JsonApiDotNetCore.Models;
Expand All @@ -18,21 +16,26 @@ public GenericProcessor(DbContext context)
}

public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
{
SetRelationships(parent, relationship, relationshipIds);

await _context.SaveChangesAsync();
}

public void SetRelationships(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
{
var relationshipType = relationship.Type;

if(relationship.IsHasMany)
if (relationship.IsHasMany)
{
var entities = _context.GetDbSet<T>().Where(x => relationshipIds.Contains(x.Id.ToString())).ToList();
var entities = _context.GetDbSet<T>().Where(x => relationshipIds.Contains(x.StringId)).ToList();
relationship.SetValue(parent, entities);
}
else
{
var entity = _context.GetDbSet<T>().SingleOrDefault(x => relationshipIds.First() == x.Id.ToString());
var entity = _context.GetDbSet<T>().SingleOrDefault(x => relationshipIds.First() == x.StringId);
relationship.SetValue(parent, entity);
}

await _context.SaveChangesAsync();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ namespace JsonApiDotNetCore.Internal
public interface IGenericProcessor
{
Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
void SetRelationships(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
}
}
24 changes: 24 additions & 0 deletions src/JsonApiDotNetCore/Internal/TypeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Reflection;

namespace JsonApiDotNetCore.Internal
{
public static class TypeHelper
{
public static object ConvertType(object value, Type type)
{
if(value == null)
return null;

if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
type = Nullable.GetUnderlyingType(type);

var stringValue = value.ToString();

if(type == typeof(Guid))
return Guid.Parse(stringValue);

return Convert.ChangeType(stringValue, type);
}
}
}
2 changes: 1 addition & 1 deletion src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>1.1.0</VersionPrefix>
<VersionPrefix>1.1.1</VersionPrefix>
<TargetFramework>netcoreapp1.0</TargetFramework>
<AssemblyName>JsonApiDotNetCore</AssemblyName>
<PackageId>JsonApiDotNetCore</PackageId>
Expand Down
4 changes: 2 additions & 2 deletions src/JsonApiDotNetCore/Models/IIdentifiable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ namespace JsonApiDotNetCore.Models
{
public interface IIdentifiable
{
object Id { get; set; }
string StringId { get; set; }
}

public interface IIdentifiable<T> : IIdentifiable
{
new T Id { get; set; }
T Id { get; set; }
}
}
Loading