diff --git a/.gitignore b/.gitignore
index cd9f35a530..6630942ad5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,6 @@
*.user
.couscous/
docs/Template-Dark/
-.idea/
\ No newline at end of file
+.idea/
+**/_site/
+log.txt
diff --git a/Build.ps1 b/Build.ps1
index e51473adf2..bfbd989415 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -32,6 +32,9 @@ CheckLastExitCode
dotnet test ./test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj
CheckLastExitCode
+dotnet test ./test/OperationsExampleTests/OperationsExampleTests.csproj
+CheckLastExitCode
+
dotnet build .\src\JsonApiDotNetCore -c Release
CheckLastExitCode
@@ -57,4 +60,4 @@ Else {
Write-Output "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
CheckLastExitCode
-}
\ No newline at end of file
+}
diff --git a/Directory.Build.props b/Directory.Build.props
index cc81f65974..346835dd6c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,5 +1,5 @@
-
+
netcoreapp2.0
netstandard2.0
@@ -10,7 +10,6 @@
2.0.0
2.0.0
-
2.0.1
2.0.1
@@ -19,8 +18,8 @@
4.4.0
-
-
+
+
15.3.0-preview-20170427-09
1.1.2
@@ -28,5 +27,5 @@
15.0.3
4.7.99
-
+
diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln
index d62f8db6af..4b7cf5ea47 100644
--- a/JsonApiDotnetCore.sln
+++ b/JsonApiDotnetCore.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.27004.2009
+VisualStudioVersion = 15.0.27130.2010
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{C0EC9E70-EB2E-436F-9D94-FA16FA774123}"
EndProject
@@ -15,6 +15,11 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C5B4D998-CECB-454D-9F32-085A897577BE}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
+ .travis.yml = .travis.yml
+ appveyor.yml = appveyor.yml
+ Build.ps1 = Build.ps1
+ build.sh = build.sh
+ Directory.Build.props = Directory.Build.props
README.md = README.md
EndProjectSection
EndProject
@@ -32,9 +37,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "benchmarks\Benchmarks.csproj", "{1F604666-BB0F-413E-922D-9D37C6073285}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OperationsExample", "src\Examples\OperationsExample\OperationsExample.csproj", "{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExample", "src\Examples\OperationsExample\OperationsExample.csproj", "{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OperationsExampleTests", "test\OperationsExampleTests\OperationsExampleTests.csproj", "{9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExampleTests", "test\OperationsExampleTests\OperationsExampleTests.csproj", "{9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -114,34 +119,10 @@ Global
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Debug|x86.Build.0 = Debug|Any CPU
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|Any CPU.Build.0 = Release|Any CPU
- {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x64.ActiveCfg = Release|x64
- {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x64.Build.0 = Release|x64
- {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x86.ActiveCfg = Release|x86
- {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x86.Build.0 = Release|x86
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x64.ActiveCfg = Debug|x64
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x64.Build.0 = Debug|x64
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x86.ActiveCfg = Debug|x86
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x86.Build.0 = Debug|x86
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|Any CPU.Build.0 = Release|Any CPU
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x64.ActiveCfg = Release|x64
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x64.Build.0 = Release|x64
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x86.ActiveCfg = Release|x86
- {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x86.Build.0 = Release|x86
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x64.ActiveCfg = Debug|x64
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x64.Build.0 = Debug|x64
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x86.ActiveCfg = Debug|x86
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x86.Build.0 = Debug|x86
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|Any CPU.Build.0 = Release|Any CPU
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x64.ActiveCfg = Release|x64
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x64.Build.0 = Release|x64
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x86.ActiveCfg = Release|x86
- {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x86.Build.0 = Release|x86
+ {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x64.ActiveCfg = Release|Any CPU
+ {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x64.Build.0 = Release|Any CPU
+ {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x86.ActiveCfg = Release|Any CPU
+ {FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D}.Release|x86.Build.0 = Release|Any CPU
{1F604666-BB0F-413E-922D-9D37C6073285}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F604666-BB0F-413E-922D-9D37C6073285}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F604666-BB0F-413E-922D-9D37C6073285}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -154,22 +135,46 @@ Global
{1F604666-BB0F-413E-922D-9D37C6073285}.Release|x64.Build.0 = Release|Any CPU
{1F604666-BB0F-413E-922D-9D37C6073285}.Release|x86.ActiveCfg = Release|Any CPU
{1F604666-BB0F-413E-922D-9D37C6073285}.Release|x86.Build.0 = Release|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x64.Build.0 = Debug|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Debug|x86.Build.0 = Debug|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x64.ActiveCfg = Release|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x64.Build.0 = Release|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x86.ActiveCfg = Release|Any CPU
+ {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D}.Release|x86.Build.0 = Release|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x64.Build.0 = Debug|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Debug|x86.Build.0 = Debug|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x64.ActiveCfg = Release|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x64.Build.0 = Release|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x86.ActiveCfg = Release|Any CPU
+ {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x86.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}
+ {97EE048B-16C0-43F6-BDA9-4E762B2F579F} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{0B959765-40D2-43B5-87EE-FE2FEF9DBED5} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
- {570165EC-62B5-4684-A139-8D2A30DD4475} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
+ {570165EC-62B5-4684-A139-8D2A30DD4475} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{73DA578D-A63F-4956-83ED-6D7102E09140} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
{6D4BD85A-A262-44C6-8572-FE3A30410BF3} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
{026FBC6C-AF76-4568-9B87-EC73457899FD} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
{FBFB0B0B-EA86-4B41-AB2A-E0249F70C86D} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
+ {1F604666-BB0F-413E-922D-9D37C6073285} = {076E1AE4-FD25-4684-B826-CAAE37FEA0AA}
{CF2C1EB6-8449-4B35-B8C7-F43D6D90632D} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{9CD2C116-D133-4FE4-97DA-A9FEAFF045F1} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
- {1F604666-BB0F-413E-922D-9D37C6073285} = {076E1AE4-FD25-4684-B826-CAAE37FEA0AA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}
diff --git a/README.md b/README.md
index 1acbbbf799..f396be824b 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,6 @@
[](https://ci.appveyor.com/project/jaredcnance/jsonapidotnetcore)
[](https://travis-ci.org/json-api-dotnet/JsonApiDotNetCore)
[](https://www.nuget.org/packages/JsonApiDotNetCore/)
-[](https://www.myget.org/feed/research-institute/package/nuget/JsonApiDotNetCore)
[](https://gitter.im/json-api-dotnet-core/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](http://www.firsttimersonly.com/)
diff --git a/build.sh b/build.sh
index 441470e65d..50f2ab9c99 100755
--- a/build.sh
+++ b/build.sh
@@ -7,4 +7,5 @@ dotnet restore
dotnet test ./test/UnitTests/UnitTests.csproj
dotnet test ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj
-dotnet test ./test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj
\ No newline at end of file
+dotnet test ./test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj
+dotnet test ./test/OperationsExampleTests/OperationsExampleTests.csproj
diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
index ccdf8e8d9f..4266ca9741 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
@@ -1,4 +1,4 @@
-using JsonApiDotNetCoreExample.Models;
+using JsonApiDotNetCoreExample.Models;
using JsonApiDotNetCore.Models;
using Microsoft.EntityFrameworkCore;
@@ -14,12 +14,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired();
-
+
modelBuilder.Entity()
.HasOne(t => t.Assignee)
.WithMany(p => p.AssignedTodoItems)
.HasForeignKey(t => t.AssigneeId);
-
+
modelBuilder.Entity()
.HasOne(t => t.Owner)
.WithMany(p => p.TodoItems)
@@ -34,5 +34,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
[Resource("camelCasedModels")]
public DbSet CamelCasedModels { get; set; }
+
+ public DbSet Articles { get; set; }
+ public DbSet Authors { get; set; }
}
}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170315140127_initial.Designer.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170315140127_initial.Designer.cs
deleted file mode 100644
index be31f0ccae..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170315140127_initial.Designer.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using JsonApiDotNetCoreExample.Data;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- [DbContext(typeof(AppDbContext))]
- [Migration("20170315140127_initial")]
- partial class initial
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
- modelBuilder
- .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.1");
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("FirstName");
-
- b.Property("LastName");
-
- b.HasKey("Id");
-
- b.ToTable("People");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("CollectionId");
-
- b.Property("Description");
-
- b.Property("Ordinal");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("CollectionId");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItems");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("Name");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItemCollections");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.TodoItemCollection", "Collection")
- .WithMany("TodoItems")
- .HasForeignKey("CollectionId");
-
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItems")
- .HasForeignKey("OwnerId");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItemCollections")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade);
- });
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330020650_AddAssignedTodoItems.Designer.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330020650_AddAssignedTodoItems.Designer.cs
deleted file mode 100755
index 52b60adbcb..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330020650_AddAssignedTodoItems.Designer.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using JsonApiDotNetCoreExample.Data;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- [DbContext(typeof(AppDbContext))]
- [Migration("20170330020650_AddAssignedTodoItems")]
- partial class AddAssignedTodoItems
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
- modelBuilder
- .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.1");
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("FirstName");
-
- b.Property("LastName");
-
- b.HasKey("Id");
-
- b.ToTable("People");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("AssigneeId");
-
- b.Property("CollectionId");
-
- b.Property("Description");
-
- b.Property("Ordinal");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("AssigneeId");
-
- b.HasIndex("CollectionId");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItems");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("Name");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItemCollections");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee")
- .WithMany("AssignedTodoItems")
- .HasForeignKey("AssigneeId");
-
- b.HasOne("JsonApiDotNetCoreExample.Models.TodoItemCollection", "Collection")
- .WithMany("TodoItems")
- .HasForeignKey("CollectionId");
-
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItems")
- .HasForeignKey("OwnerId");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItemCollections")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade);
- });
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330020650_AddAssignedTodoItems.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330020650_AddAssignedTodoItems.cs
deleted file mode 100755
index 8a1810a9d8..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330020650_AddAssignedTodoItems.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- public partial class AddAssignedTodoItems : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "AssigneeId",
- table: "TodoItems",
- nullable: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_TodoItems_AssigneeId",
- table: "TodoItems",
- column: "AssigneeId");
-
- migrationBuilder.AddForeignKey(
- name: "FK_TodoItems_People_AssigneeId",
- table: "TodoItems",
- column: "AssigneeId",
- principalTable: "People",
- principalColumn: "Id",
- onDelete: ReferentialAction.Restrict);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_TodoItems_People_AssigneeId",
- table: "TodoItems");
-
- migrationBuilder.DropIndex(
- name: "IX_TodoItems_AssigneeId",
- table: "TodoItems");
-
- migrationBuilder.DropColumn(
- name: "AssigneeId",
- table: "TodoItems");
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330234539_AddGuidProperty.Designer.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330234539_AddGuidProperty.Designer.cs
deleted file mode 100755
index ded5d1b160..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330234539_AddGuidProperty.Designer.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using JsonApiDotNetCoreExample.Data;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- [DbContext(typeof(AppDbContext))]
- [Migration("20170330234539_AddGuidProperty")]
- partial class AddGuidProperty
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
- modelBuilder
- .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.1");
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("FirstName");
-
- b.Property("LastName");
-
- b.HasKey("Id");
-
- b.ToTable("People");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("AssigneeId");
-
- b.Property("CollectionId");
-
- b.Property("Description");
-
- b.Property("GuidProperty");
-
- b.Property("Ordinal");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("AssigneeId");
-
- b.HasIndex("CollectionId");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItems");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("Name");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItemCollections");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee")
- .WithMany("AssignedTodoItems")
- .HasForeignKey("AssigneeId");
-
- b.HasOne("JsonApiDotNetCoreExample.Models.TodoItemCollection", "Collection")
- .WithMany("TodoItems")
- .HasForeignKey("CollectionId");
-
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItems")
- .HasForeignKey("OwnerId");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItemCollections")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade);
- });
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330234539_AddGuidProperty.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330234539_AddGuidProperty.cs
deleted file mode 100755
index 27815ce504..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170330234539_AddGuidProperty.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- public partial class AddGuidProperty : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "GuidProperty",
- table: "TodoItems",
- nullable: false,
- defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "GuidProperty",
- table: "TodoItems");
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs
deleted file mode 100755
index 537b3b043a..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using JsonApiDotNetCoreExample.Data;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- [DbContext(typeof(AppDbContext))]
- [Migration("20170424180950_AddCreatesAndAchievedDates")]
- partial class AddCreatesAndAchievedDates
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
- modelBuilder
- .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.1");
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("FirstName");
-
- b.Property("LastName");
-
- b.HasKey("Id");
-
- b.ToTable("People");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("AchievedDate");
-
- b.Property("AssigneeId");
-
- b.Property("CollectionId");
-
- b.Property("CreatedDate")
- .ValueGeneratedOnAdd()
- .HasDefaultValueSql("CURRENT_TIMESTAMP");
-
- b.Property("Description");
-
- b.Property("GuidProperty");
-
- b.Property("Ordinal");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("AssigneeId");
-
- b.HasIndex("CollectionId");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItems");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("Name");
-
- b.Property("OwnerId");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("TodoItemCollections");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee")
- .WithMany("AssignedTodoItems")
- .HasForeignKey("AssigneeId");
-
- b.HasOne("JsonApiDotNetCoreExample.Models.TodoItemCollection", "Collection")
- .WithMany("TodoItems")
- .HasForeignKey("CollectionId");
-
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItems")
- .HasForeignKey("OwnerId");
- });
-
- modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b =>
- {
- b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner")
- .WithMany("TodoItemCollections")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade);
- });
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.cs
deleted file mode 100755
index 1c36034fe3..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- public partial class AddCreatesAndAchievedDates : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "AchievedDate",
- table: "TodoItems",
- nullable: true);
-
- migrationBuilder.AddColumn(
- name: "CreatedDate",
- table: "TodoItems",
- nullable: false,
- defaultValueSql: "CURRENT_TIMESTAMP");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "AchievedDate",
- table: "TodoItems");
-
- migrationBuilder.DropColumn(
- name: "CreatedDate",
- table: "TodoItems");
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170426232509_AddCamelCasedModel.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20170426232509_AddCamelCasedModel.cs
deleted file mode 100755
index fbec5e0a79..0000000000
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170426232509_AddCamelCasedModel.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Metadata;
-
-namespace JsonApiDotNetCoreExample.Migrations
-{
- public partial class AddCamelCasedModel : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "CamelCasedModels",
- columns: table => new
- {
- Id = table.Column(nullable: false)
- .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
- CompoundAttr = table.Column(nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_CamelCasedModels", x => x.Id);
- });
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "CamelCasedModels");
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170426232509_AddCamelCasedModel.Designer.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.Designer.cs
old mode 100755
new mode 100644
similarity index 72%
rename from src/Examples/JsonApiDotNetCoreExample/Migrations/20170426232509_AddCamelCasedModel.Designer.cs
rename to src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.Designer.cs
index e43d2afc1f..c86425b00c
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170426232509_AddCamelCasedModel.Designer.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.Designer.cs
@@ -1,21 +1,53 @@
-using System;
+//
+using JsonApiDotNetCoreExample.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
-using JsonApiDotNetCoreExample.Data;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
+using System;
namespace JsonApiDotNetCoreExample.Migrations
{
[DbContext(typeof(AppDbContext))]
- [Migration("20170426232509_AddCamelCasedModel")]
- partial class AddCamelCasedModel
+ [Migration("20180327120810_initial")]
+ partial class initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
+#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.1");
+ .HasAnnotation("ProductVersion", "2.0.1-rtm-125");
+
+ modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AuthorId");
+
+ b.Property("Name");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AuthorId");
+
+ b.ToTable("Articles");
+ });
+
+ modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Author", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Name");
+
+ b.HasKey("Id");
+
+ b.ToTable("Authors");
+ });
modelBuilder.Entity("JsonApiDotNetCoreExample.Models.CamelCasedModel", b =>
{
@@ -93,6 +125,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.ToTable("TodoItemCollections");
});
+ modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b =>
+ {
+ b.HasOne("JsonApiDotNetCoreExample.Models.Author", "Author")
+ .WithMany("Articles")
+ .HasForeignKey("AuthorId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
{
b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee")
@@ -115,6 +155,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade);
});
+#pragma warning restore 612, 618
}
}
}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170315140127_initial.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs
similarity index 54%
rename from src/Examples/JsonApiDotNetCoreExample/Migrations/20170315140127_initial.cs
rename to src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs
index e0b4e3b40b..ba19b62ef6 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20170315140127_initial.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs
@@ -1,6 +1,7 @@
-using System;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Metadata;
+using System;
+using System.Collections.Generic;
namespace JsonApiDotNetCoreExample.Migrations
{
@@ -8,6 +9,32 @@ public partial class initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
+ migrationBuilder.CreateTable(
+ name: "Authors",
+ columns: table => new
+ {
+ Id = table.Column(nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
+ Name = table.Column(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Authors", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "CamelCasedModels",
+ columns: table => new
+ {
+ Id = table.Column(nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
+ CompoundAttr = table.Column(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_CamelCasedModels", x => x.Id);
+ });
+
migrationBuilder.CreateTable(
name: "People",
columns: table => new
@@ -22,6 +49,26 @@ protected override void Up(MigrationBuilder migrationBuilder)
table.PrimaryKey("PK_People", x => x.Id);
});
+ migrationBuilder.CreateTable(
+ name: "Articles",
+ columns: table => new
+ {
+ Id = table.Column(nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
+ AuthorId = table.Column(nullable: false),
+ Name = table.Column(nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Articles", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Articles_Authors_AuthorId",
+ column: x => x.AuthorId,
+ principalTable: "Authors",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
migrationBuilder.CreateTable(
name: "TodoItemCollections",
columns: table => new
@@ -47,14 +94,24 @@ protected override void Up(MigrationBuilder migrationBuilder)
{
Id = table.Column(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
+ AchievedDate = table.Column(nullable: true),
+ AssigneeId = table.Column(nullable: true),
CollectionId = table.Column(nullable: true),
+ CreatedDate = table.Column(nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
Description = table.Column(nullable: true),
+ GuidProperty = table.Column(nullable: false),
Ordinal = table.Column(nullable: false),
OwnerId = table.Column(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoItems", x => x.Id);
+ table.ForeignKey(
+ name: "FK_TodoItems_People_AssigneeId",
+ column: x => x.AssigneeId,
+ principalTable: "People",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_TodoItems_TodoItemCollections_CollectionId",
column: x => x.CollectionId,
@@ -69,6 +126,21 @@ protected override void Up(MigrationBuilder migrationBuilder)
onDelete: ReferentialAction.Restrict);
});
+ migrationBuilder.CreateIndex(
+ name: "IX_Articles_AuthorId",
+ table: "Articles",
+ column: "AuthorId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_TodoItemCollections_OwnerId",
+ table: "TodoItemCollections",
+ column: "OwnerId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_TodoItems_AssigneeId",
+ table: "TodoItems",
+ column: "AssigneeId");
+
migrationBuilder.CreateIndex(
name: "IX_TodoItems_CollectionId",
table: "TodoItems",
@@ -78,18 +150,22 @@ protected override void Up(MigrationBuilder migrationBuilder)
name: "IX_TodoItems_OwnerId",
table: "TodoItems",
column: "OwnerId");
-
- migrationBuilder.CreateIndex(
- name: "IX_TodoItemCollections_OwnerId",
- table: "TodoItemCollections",
- column: "OwnerId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
+ migrationBuilder.DropTable(
+ name: "Articles");
+
+ migrationBuilder.DropTable(
+ name: "CamelCasedModels");
+
migrationBuilder.DropTable(
name: "TodoItems");
+ migrationBuilder.DropTable(
+ name: "Authors");
+
migrationBuilder.DropTable(
name: "TodoItemCollections");
diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs
index 5de99f5078..c0794103fe 100755
--- a/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs
@@ -1,8 +1,12 @@
-using System;
+//
+using JsonApiDotNetCoreExample.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
-using JsonApiDotNetCoreExample.Data;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
+using System;
namespace JsonApiDotNetCoreExample.Migrations
{
@@ -11,9 +15,38 @@ partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
+#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.1");
+ .HasAnnotation("ProductVersion", "2.0.1-rtm-125");
+
+ modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AuthorId");
+
+ b.Property("Name");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AuthorId");
+
+ b.ToTable("Articles");
+ });
+
+ modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Author", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Name");
+
+ b.HasKey("Id");
+
+ b.ToTable("Authors");
+ });
modelBuilder.Entity("JsonApiDotNetCoreExample.Models.CamelCasedModel", b =>
{
@@ -91,6 +124,14 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("TodoItemCollections");
});
+ modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b =>
+ {
+ b.HasOne("JsonApiDotNetCoreExample.Models.Author", "Author")
+ .WithMany("Articles")
+ .HasForeignKey("AuthorId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b =>
{
b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee")
@@ -113,6 +154,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade);
});
+#pragma warning restore 612, 618
}
}
}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs
new file mode 100644
index 0000000000..c633d58bdd
--- /dev/null
+++ b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs
@@ -0,0 +1,14 @@
+using JsonApiDotNetCore.Models;
+
+namespace JsonApiDotNetCoreExample.Models
+{
+ public class Article : Identifiable
+ {
+ [Attr("name")]
+ public string Name { get; set; }
+
+ [HasOne("author")]
+ public Author Author { get; set; }
+ public int AuthorId { get; set; }
+ }
+}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs
new file mode 100644
index 0000000000..c77ad007c8
--- /dev/null
+++ b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs
@@ -0,0 +1,14 @@
+using JsonApiDotNetCore.Models;
+using System.Collections.Generic;
+
+namespace JsonApiDotNetCoreExample.Models
+{
+ public class Author : Identifiable
+ {
+ [Attr("name")]
+ public string Name { get; set; }
+
+ [HasMany("articles")]
+ public List Articles { get; set; }
+ }
+}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Program.cs b/src/Examples/JsonApiDotNetCoreExample/Program.cs
index e4e3fe355e..b9bbe37b6a 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Program.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Program.cs
@@ -5,10 +5,7 @@ namespace JsonApiDotNetCoreExample
{
public class Program
{
- public static void Main(string[] args)
- {
- BuildWebHost(args).Run();
- }
+ public static void Main(string[] args) => BuildWebHost(args).Run();
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
diff --git a/src/Examples/JsonApiDotNetCoreExample/appsettings.json b/src/Examples/JsonApiDotNetCoreExample/appsettings.json
index ceb4a1ed73..578225ef14 100755
--- a/src/Examples/JsonApiDotNetCoreExample/appsettings.json
+++ b/src/Examples/JsonApiDotNetCoreExample/appsettings.json
@@ -1,14 +1,13 @@
{
- "Data": {
- "DefaultConnection":
- "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=password"
- },
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Trace",
- "System": "Trace",
- "Microsoft": "Trace"
+ "Data": {
+ "DefaultConnection": "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=postgres"
+ },
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Trace",
+ "System": "Trace",
+ "Microsoft": "Trace"
+ }
}
- }
}
diff --git a/src/Examples/NoEntityFrameworkExample/Startup.cs b/src/Examples/NoEntityFrameworkExample/Startup.cs
index 81f743aa5f..dfba27ddd9 100755
--- a/src/Examples/NoEntityFrameworkExample/Startup.cs
+++ b/src/Examples/NoEntityFrameworkExample/Startup.cs
@@ -9,6 +9,7 @@
using Microsoft.Extensions.Logging;
using NoEntityFrameworkExample.Services;
using Microsoft.EntityFrameworkCore;
+using System;
namespace NoEntityFrameworkExample
{
@@ -27,7 +28,7 @@ public Startup(IHostingEnvironment env)
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
+ public virtual IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
var mvcBuilder = services.AddMvc();
@@ -46,6 +47,8 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton(Configuration);
services.AddSingleton>(optionsBuilder.Options);
services.AddScoped();
+
+ return services.BuildServiceProvider();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
diff --git a/src/Examples/OperationsExample/Data/AppDbContext.cs b/src/Examples/OperationsExample/Data/AppDbContext.cs
deleted file mode 100644
index d00bfe4765..0000000000
--- a/src/Examples/OperationsExample/Data/AppDbContext.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using OperationsExample.Models;
-
-namespace OperationsExample.Data
-{
- public class AppDbContext : DbContext
- {
- public AppDbContext(DbContextOptions options)
- : base(options)
- { }
-
- public DbSet Articles { get; set; }
- }
-}
diff --git a/src/Examples/OperationsExample/Migrations/20170827234334_AddArticles.Designer.cs b/src/Examples/OperationsExample/Migrations/20170827234334_AddArticles.Designer.cs
deleted file mode 100755
index 10f16d49cd..0000000000
--- a/src/Examples/OperationsExample/Migrations/20170827234334_AddArticles.Designer.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using OperationsExample.Data;
-
-namespace OperationsExample.Migrations
-{
- [DbContext(typeof(AppDbContext))]
- [Migration("20170827234334_AddArticles")]
- partial class AddArticles
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
- modelBuilder
- .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.2");
-
- modelBuilder.Entity("OperationsExample.Models.Article", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("Name");
-
- b.HasKey("Id");
-
- b.ToTable("Articles");
- });
- }
- }
-}
diff --git a/src/Examples/OperationsExample/Migrations/20170827234334_AddArticles.cs b/src/Examples/OperationsExample/Migrations/20170827234334_AddArticles.cs
deleted file mode 100755
index 308e6f78f5..0000000000
--- a/src/Examples/OperationsExample/Migrations/20170827234334_AddArticles.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Metadata;
-
-namespace OperationsExample.Migrations
-{
- public partial class AddArticles : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "Articles",
- columns: table => new
- {
- Id = table.Column(nullable: false)
- .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
- Name = table.Column(nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_Articles", x => x.Id);
- });
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "Articles");
- }
- }
-}
diff --git a/src/Examples/OperationsExample/Migrations/AppDbContextModelSnapshot.cs b/src/Examples/OperationsExample/Migrations/AppDbContextModelSnapshot.cs
deleted file mode 100755
index 547bbbf2cf..0000000000
--- a/src/Examples/OperationsExample/Migrations/AppDbContextModelSnapshot.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using OperationsExample.Data;
-
-namespace OperationsExample.Migrations
-{
- [DbContext(typeof(AppDbContext))]
- partial class AppDbContextModelSnapshot : ModelSnapshot
- {
- protected override void BuildModel(ModelBuilder modelBuilder)
- {
- modelBuilder
- .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
- .HasAnnotation("ProductVersion", "1.1.2");
-
- modelBuilder.Entity("OperationsExample.Models.Article", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("Name");
-
- b.HasKey("Id");
-
- b.ToTable("Articles");
- });
- }
- }
-}
diff --git a/src/Examples/OperationsExample/Models/Article.cs b/src/Examples/OperationsExample/Models/Article.cs
deleted file mode 100644
index fc4b5d6647..0000000000
--- a/src/Examples/OperationsExample/Models/Article.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using JsonApiDotNetCore.Models;
-
-namespace OperationsExample.Models
-{
- public class Article : Identifiable
- {
- [Attr("name")]
- public string Name { get; set; }
- }
-}
diff --git a/src/Examples/OperationsExample/OperationsExample.csproj b/src/Examples/OperationsExample/OperationsExample.csproj
index 48c2654722..69ad5653ce 100644
--- a/src/Examples/OperationsExample/OperationsExample.csproj
+++ b/src/Examples/OperationsExample/OperationsExample.csproj
@@ -8,9 +8,11 @@
+
+
diff --git a/src/Examples/OperationsExample/Program.cs b/src/Examples/OperationsExample/Program.cs
index b528f47d15..1c2b6b267a 100644
--- a/src/Examples/OperationsExample/Program.cs
+++ b/src/Examples/OperationsExample/Program.cs
@@ -1,26 +1,15 @@
-using System.IO;
+using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
namespace OperationsExample
{
public class Program
- {
- public static void Main(string[] args)
- {
- var config = new ConfigurationBuilder()
- .AddCommandLine(args)
- .AddEnvironmentVariables(prefix: "ASPNETCORE_")
- .Build();
+ {
+ public static void Main(string[] args) => BuildWebHost(args).Run();
- var host = new WebHostBuilder()
- .UseConfiguration(config)
- .UseKestrel()
- .UseContentRoot(Directory.GetCurrentDirectory())
+ public static IWebHost BuildWebHost(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
.UseStartup()
.Build();
-
- host.Run();
- }
}
}
diff --git a/src/Examples/OperationsExample/Properties/launchSettings.json b/src/Examples/OperationsExample/Properties/launchSettings.json
new file mode 100644
index 0000000000..b0d8e5bd4b
--- /dev/null
+++ b/src/Examples/OperationsExample/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:53656/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "OperationsExample": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:53657/"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Examples/OperationsExample/Startup.cs b/src/Examples/OperationsExample/Startup.cs
index 1ffedf9116..c11a3e9d26 100644
--- a/src/Examples/OperationsExample/Startup.cs
+++ b/src/Examples/OperationsExample/Startup.cs
@@ -1,60 +1,56 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.EntityFrameworkCore;
-using JsonApiDotNetCore.Extensions;
-using System;
-using OperationsExample.Data;
-using JsonApiDotNetCore.Models;
-
-namespace OperationsExample
-{
- public class Startup
- {
- public readonly IConfiguration Config;
-
- public Startup(IHostingEnvironment env)
- {
- var builder = new ConfigurationBuilder()
- .SetBasePath(env.ContentRootPath)
- .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
- .AddEnvironmentVariables();
-
- Config = builder.Build();
- }
-
- public virtual IServiceProvider ConfigureServices(IServiceCollection services)
- {
- var loggerFactory = new LoggerFactory();
- loggerFactory
- .AddConsole(LogLevel.Trace);
- services.AddSingleton(loggerFactory);
-
- services.AddDbContext(options =>
- {
- options.UseNpgsql(GetDbConnectionString());
- }, ServiceLifetime.Transient);
-
- services.AddJsonApi(opt => opt.EnableExtension(JsonApiExtension.Operations));
-
- return services.BuildServiceProvider();
- }
-
- public virtual void Configure(
- IApplicationBuilder app,
- IHostingEnvironment env,
- ILoggerFactory loggerFactory,
- AppDbContext context)
- {
- context.Database.EnsureCreated();
-
- loggerFactory.AddConsole(Config.GetSection("Logging"));
- loggerFactory.AddDebug();
- app.UseJsonApi();
- }
-
- public string GetDbConnectionString() => Config["Data:DefaultConnection"];
- }
-}
\ No newline at end of file
+using System;
+using JsonApiDotNetCore.Extensions;
+using JsonApiDotNetCoreExample.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace OperationsExample
+{
+ public class Startup
+ {
+ public readonly IConfiguration Config;
+
+ public Startup(IHostingEnvironment env)
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(env.ContentRootPath)
+ .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+ .AddEnvironmentVariables();
+
+ Config = builder.Build();
+ }
+
+ public virtual IServiceProvider ConfigureServices(IServiceCollection services)
+ {
+ var loggerFactory = new LoggerFactory();
+ loggerFactory.AddConsole(LogLevel.Trace);
+
+ services.AddSingleton(loggerFactory);
+
+ services.AddDbContext(options => options.UseNpgsql(GetDbConnectionString()), ServiceLifetime.Scoped);
+
+ services.AddJsonApi(opt => opt.EnableOperations = true);
+
+ return services.BuildServiceProvider();
+ }
+
+ public virtual void Configure(
+ IApplicationBuilder app,
+ IHostingEnvironment env,
+ ILoggerFactory loggerFactory,
+ AppDbContext context)
+ {
+ context.Database.EnsureCreated();
+
+ loggerFactory.AddConsole(Config.GetSection("Logging"));
+ loggerFactory.AddDebug();
+ app.UseJsonApi();
+ }
+
+ public string GetDbConnectionString() => Config["Data:DefaultConnection"];
+ }
+}
diff --git a/src/Examples/OperationsExample/appsettings.json b/src/Examples/OperationsExample/appsettings.json
index c1061281cc..0d77cd40f2 100644
--- a/src/Examples/OperationsExample/appsettings.json
+++ b/src/Examples/OperationsExample/appsettings.json
@@ -1,13 +1,13 @@
-{
- "Data": {
- "DefaultConnection": "Host=localhost;Port=5432;Database=OperationsExample;User ID=postgres;Password=password"
- },
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Trace",
- "System": "Trace",
- "Microsoft": "Trace"
+{
+ "Data": {
+ "DefaultConnection": "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=postgres"
+ },
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Trace",
+ "System": "Trace",
+ "Microsoft": "Trace"
+ }
}
- }
}
diff --git a/src/Examples/ReportsExample/Startup.cs b/src/Examples/ReportsExample/Startup.cs
index fed3707789..72710681b9 100644
--- a/src/Examples/ReportsExample/Startup.cs
+++ b/src/Examples/ReportsExample/Startup.cs
@@ -1,7 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using JsonApiDotNetCore.Extensions;
using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Builder;
@@ -9,7 +5,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
namespace ReportsExample
{
@@ -28,12 +23,13 @@ public Startup(IHostingEnvironment env)
Config = builder.Build();
}
- public void ConfigureServices(IServiceCollection services)
+ public virtual void ConfigureServices(IServiceCollection services)
{
var mvcBuilder = services.AddMvc();
services.AddJsonApi(opt =>
{
- opt.BuildContextGraph(builder => {
+ opt.BuildContextGraph(builder =>
+ {
builder.AddResource("reports");
});
opt.Namespace = "api";
diff --git a/src/JsonApiDotNetCore/Builders/DocumentBuilderOptions.cs b/src/JsonApiDotNetCore/Builders/DocumentBuilderOptions.cs
index ec19977313..0596d8ce3c 100644
--- a/src/JsonApiDotNetCore/Builders/DocumentBuilderOptions.cs
+++ b/src/JsonApiDotNetCore/Builders/DocumentBuilderOptions.cs
@@ -1,10 +1,6 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
namespace JsonApiDotNetCore.Builders
{
- public struct DocumentBuilderOptions
+ public struct DocumentBuilderOptions
{
public DocumentBuilderOptions(bool omitNullValuedAttributes = false)
{
diff --git a/src/JsonApiDotNetCore/Builders/DocumentBuilderOptionsProvider.cs b/src/JsonApiDotNetCore/Builders/DocumentBuilderOptionsProvider.cs
index af7fb78d7c..37c5d51273 100644
--- a/src/JsonApiDotNetCore/Builders/DocumentBuilderOptionsProvider.cs
+++ b/src/JsonApiDotNetCore/Builders/DocumentBuilderOptionsProvider.cs
@@ -1,6 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Http;
diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
index e61c5cd429..502d678f0d 100644
--- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
+++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
@@ -10,17 +10,121 @@
namespace JsonApiDotNetCore.Configuration
{
+ ///
+ /// Global options.
+ /// https://json-api-dotnet.github.io/#/global-options
+ ///
public class JsonApiOptions
{
+ ///
+ /// Whether or not stack traces should be serialized in Error objects
+ ///
+ public static bool DisableErrorStackTraces { get; set; }
+
+ ///
+ /// Whether or not source URLs should be serialized in Error objects
+ ///
+ public static bool DisableErrorSource { get; set; }
+
+ ///
+ /// The base URL Namespace
+ ///
+ ///
+ /// options.Namespace = "api/v1";
+ ///
public string Namespace { get; set; }
+
+ ///
+ /// The default page size for all resources
+ ///
+ ///
+ /// options.DefaultPageSize = 10;
+ ///
public int DefaultPageSize { get; set; }
+
+ ///
+ /// Whether or not the total-record count should be included in all document
+ /// level meta objects.
+ /// Defaults to false.
+ ///
+ ///
+ /// options.IncludeTotalRecordCount = true;
+ ///
public bool IncludeTotalRecordCount { get; set; }
+
+ ///
+ /// Whether or not clients can provide ids when creating resources.
+ /// Defaults to false. When disabled the application will respond
+ /// with a 403 Forbidden respponse if a client attempts to create a
+ /// resource with a defined id.
+ ///
+ ///
+ /// options.AllowClientGeneratedIds = true;
+ ///
public bool AllowClientGeneratedIds { get; set; }
+
+ ///
+ /// The graph of all resources exposed by this application.
+ ///
public IContextGraph ContextGraph { get; set; }
+
+ ///
+ /// Use relative links for all resources.
+ ///
+ ///
+ ///
+ /// options.RelativeLinks = true;
+ ///
+ ///
+ /// {
+ /// "type": "articles",
+ /// "id": "4309",
+ /// "relationships": {
+ /// "author": {
+ /// "links": {
+ /// "self": "/api/v1/articles/4309/relationships/author",
+ /// "related": "/api/v1/articles/4309/author"
+ /// }
+ /// }
+ /// }
+ /// }
+ ///
+ ///
public bool RelativeLinks { get; set; }
+
+ ///
+ /// Whether or not to allow all custom query parameters.
+ ///
+ ///
+ ///
+ /// options.AllowCustomQueryParameters = true;
+ ///
+ ///
public bool AllowCustomQueryParameters { get; set; }
+
+ ///
+ /// The default behavior for serializing null attributes.
+ ///
+ ///
+ ///
+ /// options.NullAttributeResponseBehavior = new NullAttributeResponseBehavior {
+ /// // ...
+ ///};
+ ///
+ ///
public NullAttributeResponseBehavior NullAttributeResponseBehavior { get; set; }
+ ///
+ /// Whether or not to allow json:api v1.1 operation requests.
+ /// This is a beta feature and there may be breaking changes
+ /// in subsequent releases. For now, it should be considered
+ /// experimental.
+ ///
+ ///
+ /// This will be enabled by default in a subsequent patch JsonApiDotNetCore v2.2.x
+ ///
+ public bool EnableOperations { get; set; }
+
[Obsolete("JsonContract resolver can now be set on SerializerSettings.")]
public IContractResolver JsonContractResolver
{
@@ -33,9 +137,6 @@ public IContractResolver JsonContractResolver
ContractResolver = new DasherizedResolver()
};
- internal IContextGraphBuilder ContextGraphBuilder { get; } = new ContextGraphBuilder();
- internal List EnabledExtensions { get; set; } = new List();
-
public void BuildContextGraph(Action builder) where TContext : DbContext
{
BuildContextGraph(builder);
@@ -55,10 +156,9 @@ public void BuildContextGraph(Action builder)
}
public void EnableExtension(JsonApiExtension extension)
- {
- EnabledExtensions.Add(extension);
- }
- }
+ => EnabledExtensions.Add(extension);
-
+ internal IContextGraphBuilder ContextGraphBuilder { get; } = new ContextGraphBuilder();
+ internal List EnabledExtensions { get; set; } = new List();
+ }
}
diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
index 8785eb2266..760f8f8d56 100644
--- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
+++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
@@ -104,7 +104,7 @@ public BaseJsonApiController(
public virtual async Task GetAsync()
{
- if (_getAll == null) throw new JsonApiException(405, "Get requests are not supported");
+ if (_getAll == null) throw Exceptions.UnSupportedRequestMethod;
var entities = await _getAll.GetAsync();
@@ -113,7 +113,7 @@ public virtual async Task GetAsync()
public virtual async Task GetAsync(TId id)
{
- if (_getById == null) throw new JsonApiException(405, "Get by Id requests are not supported");
+ if (_getById == null) throw Exceptions.UnSupportedRequestMethod;
var entity = await _getById.GetAsync(id);
@@ -125,7 +125,7 @@ public virtual async Task GetAsync(TId id)
public virtual async Task GetRelationshipsAsync(TId id, string relationshipName)
{
- if (_getRelationships == null) throw new JsonApiException(405, "Get Relationships requests are not supported");
+ if (_getRelationships == null) throw Exceptions.UnSupportedRequestMethod;
var relationship = await _getRelationships.GetRelationshipsAsync(id, relationshipName);
if (relationship == null)
@@ -136,7 +136,7 @@ public virtual async Task GetRelationshipsAsync(TId id, string re
public virtual async Task GetRelationshipAsync(TId id, string relationshipName)
{
- if (_getRelationship == null) throw new JsonApiException(405, "Get Relationship requests are not supported");
+ if (_getRelationship == null) throw Exceptions.UnSupportedRequestMethod;
var relationship = await _getRelationship.GetRelationshipAsync(id, relationshipName);
@@ -145,7 +145,7 @@ public virtual async Task GetRelationshipAsync(TId id, string rel
public virtual async Task PostAsync([FromBody] T entity)
{
- if (_create == null) throw new JsonApiException(405, "Post requests are not supported");
+ if (_create == null) throw Exceptions.UnSupportedRequestMethod;
if (entity == null)
return UnprocessableEntity();
@@ -160,7 +160,7 @@ public virtual async Task PostAsync([FromBody] T entity)
public virtual async Task PatchAsync(TId id, [FromBody] T entity)
{
- if (_update == null) throw new JsonApiException(405, "Patch requests are not supported");
+ if (_update == null) throw Exceptions.UnSupportedRequestMethod;
if (entity == null)
return UnprocessableEntity();
@@ -175,7 +175,7 @@ public virtual async Task PatchAsync(TId id, [FromBody] T entity)
public virtual async Task PatchRelationshipsAsync(TId id, string relationshipName, [FromBody] List relationships)
{
- if (_updateRelationships == null) throw new JsonApiException(405, "Relationship Patch requests are not supported");
+ if (_updateRelationships == null) throw Exceptions.UnSupportedRequestMethod;
await _updateRelationships.UpdateRelationshipsAsync(id, relationshipName, relationships);
@@ -184,7 +184,7 @@ public virtual async Task PatchRelationshipsAsync(TId id, string
public virtual async Task DeleteAsync(TId id)
{
- if (_delete == null) throw new JsonApiException(405, "Delete requests are not supported");
+ if (_delete == null) throw Exceptions.UnSupportedRequestMethod;
var wasDeleted = await _delete.DeleteAsync(id);
diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiControllerMixin.cs b/src/JsonApiDotNetCore/Controllers/JsonApiControllerMixin.cs
index fafc70f161..6fff3a22c8 100644
--- a/src/JsonApiDotNetCore/Controllers/JsonApiControllerMixin.cs
+++ b/src/JsonApiDotNetCore/Controllers/JsonApiControllerMixin.cs
@@ -19,7 +19,8 @@ protected IActionResult Forbidden()
protected IActionResult Error(Error error)
{
- var errorCollection = new ErrorCollection {
+ var errorCollection = new ErrorCollection
+ {
Errors = new List { error }
};
var result = new ObjectResult(errorCollection);
@@ -36,16 +37,16 @@ protected IActionResult Errors(ErrorCollection errors)
return result;
}
- private int GetErrorStatusCode(ErrorCollection errors)
+ private int GetErrorStatusCode(ErrorCollection errors)
{
var statusCodes = errors.Errors
.Select(e => e.StatusCode)
.Distinct()
.ToList();
- if(statusCodes.Count == 1)
+ if (statusCodes.Count == 1)
return statusCodes[0];
-
+
return int.Parse(statusCodes.Max().ToString()[0] + "00");
}
}
diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs b/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs
index f3d0b6651d..c3b667bc7d 100644
--- a/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs
+++ b/src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs
@@ -5,19 +5,53 @@
namespace JsonApiDotNetCore.Controllers
{
+ ///
+ /// A controller to be used for bulk operations as defined in the json:api 1.1 specification
+ ///
public class JsonApiOperationsController : Controller
{
private readonly IOperationsProcessor _operationsProcessor;
- public JsonApiOperationsController(
- IOperationsProcessor operationsProcessor)
+ ///
+ /// The processor to handle bulk operations.
+ ///
+ public JsonApiOperationsController(IOperationsProcessor operationsProcessor)
{
_operationsProcessor = operationsProcessor;
}
+ ///
+ /// Bulk endpoint for json:api operations
+ ///
+ ///
+ /// A json:api operations request document
+ ///
+ ///
+ ///
+ /// PATCH /api/bulk HTTP/1.1
+ /// Content-Type: application/vnd.api+json
+ ///
+ /// {
+ /// "operations": [{
+ /// "op": "add",
+ /// "ref": {
+ /// "type": "authors"
+ /// },
+ /// "data": {
+ /// "type": "authors",
+ /// "attributes": {
+ /// "name": "jaredcnance"
+ /// }
+ /// }
+ /// }]
+ /// }
+ ///
+ ///
[HttpPatch]
- public async Task PatchAsync([FromBody] OperationsDocument doc)
+ public virtual async Task PatchAsync([FromBody] OperationsDocument doc)
{
+ if (doc == null) return new StatusCodeResult(422);
+
var results = await _operationsProcessor.ProcessAsync(doc.Operations);
return Ok(new OperationsDocument(results));
diff --git a/src/JsonApiDotNetCore/Data/DbContextResolver.cs b/src/JsonApiDotNetCore/Data/DbContextResolver.cs
index 7cfe0a4278..e681e660bb 100644
--- a/src/JsonApiDotNetCore/Data/DbContextResolver.cs
+++ b/src/JsonApiDotNetCore/Data/DbContextResolver.cs
@@ -3,7 +3,7 @@
namespace JsonApiDotNetCore.Data
{
- public class DbContextResolver : IDbContextResolver
+ public class DbContextResolver : IDbContextResolver
where TContext : DbContext
{
private readonly TContext _context;
diff --git a/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs b/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs
index e8b4e3d601..2606342e29 100644
--- a/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs
+++ b/src/JsonApiDotNetCore/Extensions/DbContextExtensions.cs
@@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore;
-using System.Reflection;
using System;
namespace JsonApiDotNetCore.Extensions
diff --git a/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs
index c3d7ba2ee7..651fbb44aa 100644
--- a/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs
+++ b/src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs
@@ -1,5 +1,7 @@
+using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
namespace JsonApiDotNetCore.Extensions
{
@@ -8,6 +10,14 @@ public static class IApplicationBuilderExtensions
{
public static IApplicationBuilder UseJsonApi(this IApplicationBuilder app, bool useMvc = true)
{
+ var environment = (IHostingEnvironment)app.ApplicationServices.GetService(typeof(IHostingEnvironment));
+
+ if(environment.IsProduction())
+ {
+ JsonApiOptions.DisableErrorStackTraces = true;
+ JsonApiOptions.DisableErrorSource = true;
+ }
+
app.UseMiddleware();
if (useMvc)
diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs
index 1f2a71e20b..807d21c018 100644
--- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs
+++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs
@@ -6,7 +6,6 @@
using JsonApiDotNetCore.Internal;
using JsonApiDotNetCore.Internal.Generics;
using JsonApiDotNetCore.Middleware;
-using JsonApiDotNetCore.Models;
using JsonApiDotNetCore.Serialization;
using JsonApiDotNetCore.Services;
using JsonApiDotNetCore.Services.Operations;
@@ -89,13 +88,13 @@ public static void AddJsonApiInternals(
this IServiceCollection services,
JsonApiOptions jsonApiOptions)
{
- if (!jsonApiOptions.ContextGraph.UsesDbContext)
+ if (jsonApiOptions.ContextGraph.UsesDbContext == false)
{
services.AddScoped();
services.AddSingleton(new DbContextOptionsBuilder().Options);
}
- if (jsonApiOptions.EnabledExtensions.Contains(JsonApiExtension.Operations))
+ if (jsonApiOptions.EnableOperations)
AddOperationServices(services);
services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>));
@@ -110,6 +109,9 @@ public static void AddJsonApiInternals(
services.AddScoped(typeof(IGetByIdService<>), typeof(EntityResourceService<>));
services.AddScoped(typeof(IGetByIdService<,>), typeof(EntityResourceService<,>));
+ services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<>));
+ services.AddScoped(typeof(IGetRelationshipService<,>), typeof(EntityResourceService<,>));
+
services.AddScoped(typeof(IUpdateService<>), typeof(EntityResourceService<>));
services.AddScoped(typeof(IUpdateService<,>), typeof(EntityResourceService<,>));
@@ -122,6 +124,7 @@ public static void AddJsonApiInternals(
services.AddSingleton(jsonApiOptions.ContextGraph);
services.AddScoped();
services.AddSingleton();
+ services.AddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
@@ -148,12 +151,12 @@ private static void AddOperationServices(IServiceCollection services)
services.AddScoped(typeof(IGetOpProcessor<>), typeof(GetOpProcessor<>));
services.AddScoped(typeof(IGetOpProcessor<,>), typeof(GetOpProcessor<,>));
- services.AddScoped(typeof(IReplaceOpProcessor<>), typeof(ReplaceOpProcessor<>));
- services.AddScoped(typeof(IReplaceOpProcessor<,>), typeof(ReplaceOpProcessor<,>));
-
services.AddScoped(typeof(IRemoveOpProcessor<>), typeof(RemoveOpProcessor<>));
services.AddScoped(typeof(IRemoveOpProcessor<,>), typeof(RemoveOpProcessor<,>));
+ services.AddScoped(typeof(IUpdateOpProcessor<>), typeof(UpdateOpProcessor<>));
+ services.AddScoped(typeof(IUpdateOpProcessor<,>), typeof(UpdateOpProcessor<,>));
+
services.AddSingleton();
services.AddSingleton();
}
diff --git a/src/JsonApiDotNetCore/Extensions/JObjectExtensions.cs b/src/JsonApiDotNetCore/Extensions/JObjectExtensions.cs
deleted file mode 100644
index 70aa070fc6..0000000000
--- a/src/JsonApiDotNetCore/Extensions/JObjectExtensions.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Newtonsoft.Json.Linq;
-using Newtonsoft.Json.Schema;
-using JsonApiDotNetCore.Models.Pointers;
-
-namespace JsonApiDotNetCore.Extensions
-{
- public static class JObjectExtensions
- {
- public static bool TryParse(this JObject obj, JSchema schema, out Pointer pointer)
- where TPointer : Pointer, new()
- {
- if (obj.IsValid(schema))
- {
- pointer = obj.ToObject();
- return true;
- }
-
- pointer = null;
- return false;
- }
- }
-}
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs b/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
index d644def66d..fcf0ac7850 100644
--- a/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
+++ b/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
@@ -26,8 +26,6 @@ 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))
{
@@ -40,9 +38,7 @@ public async Task WriteAsync(OutputFormatterWriteContext context)
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();
+ responseContent = GetErrorResponse(e);
response.StatusCode = 400;
}
@@ -51,9 +47,12 @@ public async Task WriteAsync(OutputFormatterWriteContext context)
}
}
- private string GetResponseBody(object responseObject)
+ private string GetResponseBody(object responseObject) => _serializer.Serialize(responseObject);
+ private string GetErrorResponse(Exception e)
{
- return _serializer.Serialize(responseObject);
- }
+ var errors = new ErrorCollection();
+ errors.Add(new Error(400, e.Message, ErrorMeta.FromException(e)));
+ return errors.GetJson();
+ }
}
}
diff --git a/src/JsonApiDotNetCore/Internal/ContextGraph.cs b/src/JsonApiDotNetCore/Internal/ContextGraph.cs
index bc3f037bf2..d21231f067 100644
--- a/src/JsonApiDotNetCore/Internal/ContextGraph.cs
+++ b/src/JsonApiDotNetCore/Internal/ContextGraph.cs
@@ -9,8 +9,8 @@ public class ContextGraph : IContextGraph
private List _entities;
public ContextGraph() { }
-
- public ContextGraph(List entities, bool usesDbContext)
+
+ public ContextGraph(List entities, bool usesDbContext)
{
_entities = entities;
UsesDbContext = usesDbContext;
@@ -42,9 +42,9 @@ public string GetRelationshipName(string relationshipName)
{
var entityType = typeof(TParent);
return _entities
- .SingleOrDefault(e => e.EntityType == entityType)
+ .SingleOrDefault(e => e.EntityType == entityType)
?.Relationships
- .SingleOrDefault(r => string.Equals(r.PublicRelationshipName, relationshipName, StringComparison.OrdinalIgnoreCase))
+ .SingleOrDefault(r => r.Is(relationshipName))
?.InternalRelationshipName;
}
}
diff --git a/src/JsonApiDotNetCore/Internal/Error.cs b/src/JsonApiDotNetCore/Internal/Error.cs
index 0443e1edb7..999611d79e 100644
--- a/src/JsonApiDotNetCore/Internal/Error.cs
+++ b/src/JsonApiDotNetCore/Internal/Error.cs
@@ -1,4 +1,6 @@
using System;
+using System.Diagnostics;
+using JsonApiDotNetCore.Configuration;
using Newtonsoft.Json;
namespace JsonApiDotNetCore.Internal
@@ -8,30 +10,40 @@ public class Error
public Error()
{ }
- public Error(string status, string title)
+ [Obsolete("Use Error constructors with int typed status")]
+ public Error(string status, string title, ErrorMeta meta = null, string source = null)
{
Status = status;
Title = title;
+ Meta = meta;
+ Source = source;
}
- public Error(int status, string title)
+ public Error(int status, string title, ErrorMeta meta = null, string source = null)
{
Status = status.ToString();
Title = title;
+ Meta = meta;
+ Source = source;
}
- public Error(string status, string title, string detail)
+ [Obsolete("Use Error constructors with int typed status")]
+ public Error(string status, string title, string detail, ErrorMeta meta = null, string source = null)
{
Status = status;
Title = title;
Detail = detail;
+ Meta = meta;
+ Source = source;
}
- public Error(int status, string title, string detail)
+ public Error(int status, string title, string detail, ErrorMeta meta = null, string source = null)
{
Status = status.ToString();
Title = title;
Detail = detail;
+ Meta = meta;
+ Source = source;
}
[JsonProperty("title")]
@@ -45,5 +57,25 @@ public Error(int status, string title, string detail)
[JsonIgnore]
public int StatusCode => int.Parse(Status);
+
+ [JsonProperty("source")]
+ public string Source { get; set; }
+
+ [JsonProperty("meta")]
+ public ErrorMeta Meta { get; set; }
+
+ public bool ShouldSerializeMeta() => (JsonApiOptions.DisableErrorStackTraces == false);
+ public bool ShouldSerializeSource() => (JsonApiOptions.DisableErrorSource == false);
+ }
+
+ public class ErrorMeta
+ {
+ [JsonProperty("stackTrace")]
+ public string[] StackTrace { get; set; }
+
+ public static ErrorMeta FromException(Exception e)
+ => new ErrorMeta {
+ StackTrace = e.Demystify().ToString().Split(new[] { "\n"}, int.MaxValue, StringSplitOptions.RemoveEmptyEntries)
+ };
}
}
diff --git a/src/JsonApiDotNetCore/Internal/Exceptions.cs b/src/JsonApiDotNetCore/Internal/Exceptions.cs
new file mode 100644
index 0000000000..6c510e562b
--- /dev/null
+++ b/src/JsonApiDotNetCore/Internal/Exceptions.cs
@@ -0,0 +1,11 @@
+namespace JsonApiDotNetCore.Internal
+{
+ internal static class Exceptions
+ {
+ private const string DOCUMENTATION_URL = "https://json-api-dotnet.github.io/#/errors/";
+ private static string BuildUrl(string title) => DOCUMENTATION_URL + title;
+
+ public static JsonApiException UnSupportedRequestMethod { get; }
+ = new JsonApiException(405, "Request method is not supported.", BuildUrl(nameof(UnSupportedRequestMethod)));
+ }
+}
diff --git a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs
index 3f88010e0a..08cba58dd1 100644
--- a/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs
+++ b/src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs
@@ -1,3 +1,5 @@
+using JsonApiDotNetCore.Services;
+using Microsoft.AspNetCore.Http;
using System;
namespace JsonApiDotNetCore.Internal.Generics
@@ -13,7 +15,9 @@ public interface IGenericProcessorFactory
/// Constructs the generic type and locates the service, then casts to TInterface
///
///
+ ///
/// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<>), typeof(TResource));
+ ///
///
TInterface GetProcessor(Type openGenericType, Type resourceType);
@@ -21,7 +25,9 @@ public interface IGenericProcessorFactory
/// Constructs the generic type and locates the service, then casts to TInterface
///
///
+ ///
/// GetProcessor<IGenericProcessor>(typeof(GenericProcessor<,>), typeof(TResource), typeof(TId));
+ ///
///
TInterface GetProcessor(Type openGenericType, Type resourceType, Type keyType);
}
@@ -30,7 +36,7 @@ public class GenericProcessorFactory : IGenericProcessorFactory
{
private readonly IServiceProvider _serviceProvider;
- public GenericProcessorFactory(IServiceProvider serviceProvider)
+ public GenericProcessorFactory(IScopedServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
diff --git a/src/JsonApiDotNetCore/Internal/JsonApiException.cs b/src/JsonApiDotNetCore/Internal/JsonApiException.cs
index aa5faf3f73..8918d07c6c 100644
--- a/src/JsonApiDotNetCore/Internal/JsonApiException.cs
+++ b/src/JsonApiDotNetCore/Internal/JsonApiException.cs
@@ -13,30 +13,29 @@ public JsonApiException(ErrorCollection errorCollection)
}
public JsonApiException(Error error)
- : base(error.Title)
- => _errors.Add(error);
+ : base(error.Title) => _errors.Add(error);
[Obsolete("Use int statusCode overload instead")]
- public JsonApiException(string statusCode, string message)
+ public JsonApiException(string statusCode, string message, string source = null)
: base(message)
- => _errors.Add(new Error(statusCode, message, null));
+ => _errors.Add(new Error(statusCode, message, null, GetMeta(), source));
[Obsolete("Use int statusCode overload instead")]
- public JsonApiException(string statusCode, string message, string detail)
+ public JsonApiException(string statusCode, string message, string detail, string source = null)
: base(message)
- => _errors.Add(new Error(statusCode, message, detail));
+ => _errors.Add(new Error(statusCode, message, detail, GetMeta(), source));
- public JsonApiException(int statusCode, string message)
+ public JsonApiException(int statusCode, string message, string source = null)
: base(message)
- => _errors.Add(new Error(statusCode, message, null));
+ => _errors.Add(new Error(statusCode, message, null, GetMeta(), source));
- public JsonApiException(int statusCode, string message, string detail)
+ public JsonApiException(int statusCode, string message, string detail, string source = null)
: base(message)
- => _errors.Add(new Error(statusCode, message, detail));
+ => _errors.Add(new Error(statusCode, message, detail, GetMeta(), source));
public JsonApiException(int statusCode, string message, Exception innerException)
: base(message, innerException)
- => _errors.Add(new Error(statusCode, message, innerException.Message));
+ => _errors.Add(new Error(statusCode, message, innerException.Message, GetMeta(innerException)));
public ErrorCollection GetError() => _errors;
@@ -53,5 +52,8 @@ public int GetStatusCode()
return 500;
}
+
+ private ErrorMeta GetMeta() => ErrorMeta.FromException(this);
+ private ErrorMeta GetMeta(Exception e) => ErrorMeta.FromException(e);
}
}
diff --git a/src/JsonApiDotNetCore/Internal/JsonApiExceptionFactory.cs b/src/JsonApiDotNetCore/Internal/JsonApiExceptionFactory.cs
index 36b4969b1d..159c9abc70 100644
--- a/src/JsonApiDotNetCore/Internal/JsonApiExceptionFactory.cs
+++ b/src/JsonApiDotNetCore/Internal/JsonApiExceptionFactory.cs
@@ -1,36 +1,25 @@
using System;
-using System.Linq;
namespace JsonApiDotNetCore.Internal
{
public static class JsonApiExceptionFactory
{
- private const string JsonApiException = nameof(JsonApiException);
- private const string InvalidCastException = nameof(InvalidCastException);
-
public static JsonApiException GetException(Exception exception)
{
- var exceptionType = exception.GetType().ToString().Split('.').Last();
- switch(exceptionType)
- {
- case JsonApiException:
- return (JsonApiException)exception;
- case InvalidCastException:
- return new JsonApiException(409, exception.Message);
- default:
- return new JsonApiException(500, exception.Message, GetExceptionDetail(exception.InnerException));
- }
- }
+ var exceptionType = exception.GetType();
- private static string GetExceptionDetail(Exception exception)
- {
- string detail = null;
- while(exception != null)
- {
- detail = $"{detail}{exception.Message}; ";
- exception = exception.InnerException;
- }
- return detail;
+ if (exceptionType == typeof(JsonApiException))
+ return (JsonApiException)exception;
+
+ // TODO: this is for mismatching type requests (e.g. posting an author to articles endpoint)
+ // however, we can't actually guarantee that this is the source of this exception
+ // we should probably use an action filter or when we improve the ContextGraph
+ // we might be able to skip most of deserialization entirely by checking the JToken
+ // directly
+ if (exceptionType == typeof(InvalidCastException))
+ return new JsonApiException(409, exception.Message, exception);
+
+ return new JsonApiException(500, exceptionType.Name, exception);
}
}
}
diff --git a/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs
index e94f872b54..59bb3f0f83 100644
--- a/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs
+++ b/src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs
@@ -17,10 +17,10 @@ public AttrFilterQuery(
var attribute = GetAttribute(filterQuery.Attribute);
- if(attribute == null)
+ if (attribute == null)
throw new JsonApiException(400, $"'{filterQuery.Attribute}' is not a valid attribute.");
- if(attribute.IsFilterable == false)
+ if (attribute.IsFilterable == false)
throw new JsonApiException(400, $"Filter is not allowed for attribute '{attribute.PublicAttributeName}'.");
FilteredAttribute = attribute;
@@ -32,9 +32,7 @@ public AttrFilterQuery(
public string PropertyValue { get; }
public FilterOperations FilterOperation { get; }
- private AttrAttribute GetAttribute(string attribute) =>
- _jsonApiContext.RequestEntity.Attributes.FirstOrDefault(
- attr => string.Equals(attr.PublicAttributeName, attribute, StringComparison.OrdinalIgnoreCase)
- );
+ private AttrAttribute GetAttribute(string attribute) =>
+ _jsonApiContext.RequestEntity.Attributes.FirstOrDefault(attr => attr.Is(attribute));
}
}
diff --git a/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs b/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs
index 52d7c66f41..5ec658873d 100644
--- a/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs
+++ b/src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs
@@ -22,11 +22,11 @@ public RelatedAttrFilterQuery(
throw new JsonApiException(400, $"{relationshipArray[1]} is not a valid relationship on {relationshipArray[0]}.");
var attribute = GetAttribute(relationship, relationshipArray[1]);
-
- if(attribute == null)
+
+ if (attribute == null)
throw new JsonApiException(400, $"'{filterQuery.Attribute}' is not a valid attribute.");
- if(attribute.IsFilterable == false)
+ if (attribute.IsFilterable == false)
throw new JsonApiException(400, $"Filter is not allowed for attribute '{attribute.PublicAttributeName}'.");
FilteredRelationship = relationship;
@@ -41,16 +41,13 @@ public RelatedAttrFilterQuery(
public RelationshipAttribute FilteredRelationship { get; }
private RelationshipAttribute GetRelationship(string propertyName)
- {
- return _jsonApiContext.RequestEntity.Relationships
- .FirstOrDefault(r => string.Equals(r.PublicRelationshipName, propertyName, StringComparison.OrdinalIgnoreCase));
- }
+ => _jsonApiContext.RequestEntity.Relationships.FirstOrDefault(r => r.Is(propertyName));
private AttrAttribute GetAttribute(RelationshipAttribute relationship, string attribute)
{
var relatedContextExntity = _jsonApiContext.ContextGraph.GetContextEntity(relationship.Type);
return relatedContextExntity.Attributes
- .FirstOrDefault(a => string.Equals(a.PublicAttributeName, attribute, StringComparison.OrdinalIgnoreCase));
+ .FirstOrDefault(a => a.Is(attribute));
}
}
}
diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
index 9fe109011b..5aa6b6b897 100755
--- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
+++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
@@ -5,6 +5,7 @@
JsonApiDotNetCore
JsonApiDotNetCore
+
jsonapi;dotnet core;emberjs;ember
https://github.com/json-api-dotnet/JsonApiDotNetCore
@@ -13,13 +14,25 @@
git
https://github.com/json-api-dotnet/JsonApiDotNetCore
+
+
-
-
+
+
+ true
+
+
+ bin\Release\netstandard2.0\JsonApiDotNetCore.xml
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/JsonApiDotNetCore/Models/AttrAttribute.cs b/src/JsonApiDotNetCore/Models/AttrAttribute.cs
index 5be036636d..db61cb56ea 100644
--- a/src/JsonApiDotNetCore/Models/AttrAttribute.cs
+++ b/src/JsonApiDotNetCore/Models/AttrAttribute.cs
@@ -47,5 +47,11 @@ public void SetValue(object entity, object newValue)
propertyInfo.SetValue(entity, convertedValue);
}
}
+
+ ///
+ /// Whether or not the provided exposed name is equivalent to the one defined in on the model
+ ///
+ public virtual bool Is(string publicRelationshipName)
+ => string.Equals(publicRelationshipName, PublicAttributeName, StringComparison.OrdinalIgnoreCase);
}
}
diff --git a/src/JsonApiDotNetCore/Models/Operations/OperationCode.cs b/src/JsonApiDotNetCore/Models/Operations/OperationCode.cs
index ffe3310985..6b6905cd59 100644
--- a/src/JsonApiDotNetCore/Models/Operations/OperationCode.cs
+++ b/src/JsonApiDotNetCore/Models/Operations/OperationCode.cs
@@ -1,10 +1,14 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
namespace JsonApiDotNetCore.Models.Operations
{
+ [JsonConverter(typeof(StringEnumConverter))]
public enum OperationCode
{
get = 1,
add = 2,
- replace = 3,
+ update = 3,
remove = 4
}
}
diff --git a/src/JsonApiDotNetCore/Models/Pointers/OperationsPointer.cs b/src/JsonApiDotNetCore/Models/Pointers/OperationsPointer.cs
deleted file mode 100644
index 0df8f64e72..0000000000
--- a/src/JsonApiDotNetCore/Models/Pointers/OperationsPointer.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System;
-using System.Collections.Generic;
-using JsonApiDotNetCore.Internal;
-using JsonApiDotNetCore.Models.Operations;
-
-namespace JsonApiDotNetCore.Models.Pointers
-{
- public class OperationsPointer : Pointer
- {
- ///
- ///
- ///
- ///
- public override object GetValue(object root)
- {
- if (root == null) throw new ArgumentNullException(nameof(root));
- if (PointerAddress == null) throw new InvalidOperationException("Cannot get pointer value from null PointerAddress");
-
- if (root is List operations)
- return GetValueFromRoot(operations);
-
- throw new ArgumentException(nameof(root));
- }
-
- private object GetValueFromRoot(List operations)
- {
- var pathSegments = PointerAddress.ToLower().Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
-
- if (pathSegments.Length < 4)
- throw BadRequestException("number of segments", pathSegments.Length);
-
- if (pathSegments[0] != "operations")
- throw BadRequestException("prefix", pathSegments[0]);
-
- // /operations/{operationIndex} → operations = [...]
- if (int.TryParse(pathSegments[1], out int operationIndex))
- return GetValueFromOperation(operations[operationIndex], pathSegments);
- else
- throw BadRequestException("operation index", operationIndex);
- }
-
- private object GetValueFromOperation(Operation operation, string[] pathSegments)
- {
- var operationPropertyName = pathSegments[2];
-
- // /operations/0/ref → ref = {...}
- if (operationPropertyName == "ref")
- return GetValueFromRef(operation.Ref, pathSegments);
-
- // /operations/0/data → data = {...}
- if (operation.DataIsList == false)
- return GetValueFromData(operation.DataObject, pathSegments, segementStartIndex: 3);
-
- // /operations/0/data/{dataIndex} → data = [...]
- if (int.TryParse(pathSegments[3], out int dataIndex))
- {
- if (operation.DataList.Count >= dataIndex - 1)
- return GetValueFromData(operation.DataList[dataIndex], pathSegments, segementStartIndex: 4);
- throw BadRequestException("data index", dataIndex, "Pointer references an index in the data array that cannot be found at the specified position.");
- }
- else
- {
- throw BadRequestException("data index", dataIndex, "Pointer segement should provide array index but could not be parsed to an integer.");
- }
- }
-
- private object GetValueFromRef(ResourceReference reference, string[] pathSegments)
- {
- const int segementStartIndex = 3;
-
- // /operations/0/ref/{dataPropertyName}
- if (pathSegments.Length <= segementStartIndex)
- throw BadRequestException("length", pathSegments.Length, "Pointer does not contain enough segments to locate ref property.");
-
- var dataPropertyName = pathSegments[segementStartIndex];
- switch (dataPropertyName)
- {
- case "id":
- return reference.Id;
- case "type":
- return reference.Type;
- default:
- throw BadRequestException("ref property name", dataPropertyName, "Only 'id' and 'type' pointers are supported.");
- }
- }
-
- private object GetValueFromData(DocumentData data, string[] pathSegments, int segementStartIndex)
- {
- // /operations/0/data/{dataPropertyName}
- if (pathSegments.Length <= segementStartIndex)
- throw BadRequestException("length", pathSegments.Length, "Pointer does not contain enough segments to locate data property.");
-
- var dataPropertyName = pathSegments[segementStartIndex];
- switch (dataPropertyName)
- {
- case "id":
- return data.Id;
- case "type":
- return data.Type;
- default:
- throw BadRequestException("data property name", dataPropertyName, "Only 'id' and 'type' pointers are supported.");
- }
- }
-
- private JsonApiException BadRequestException(string condition, object value, string extraDetail = null)
- => new JsonApiException(400, $"Operations pointer has invalid {condition} '{value}' in pointer '{PointerAddress}'. {extraDetail}");
- }
-}
diff --git a/src/JsonApiDotNetCore/Models/Pointers/Pointer.cs b/src/JsonApiDotNetCore/Models/Pointers/Pointer.cs
deleted file mode 100644
index 27f0083904..0000000000
--- a/src/JsonApiDotNetCore/Models/Pointers/Pointer.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Newtonsoft.Json;
-using Newtonsoft.Json.Schema;
-
-namespace JsonApiDotNetCore.Models.Pointers
-{
- public abstract class Pointer
- {
- public static JSchema JsonSchema { get; } = JSchema.Parse("{ 'pointer': {'type': 'string'} }");
-
- ///
- /// Location represented by the pointer
- ///
- /// /operations/0/data/id
- [JsonProperty("pointer")]
- public string PointerAddress { get; set; }
-
- ///
- /// Get the value located at the PointerAddress in the supplied object
- ///
- public abstract object GetValue(object root);
- }
-}
diff --git a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs
index 852b602bea..0dbe6a4670 100644
--- a/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs
+++ b/src/JsonApiDotNetCore/Models/RelationshipAttribute.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace JsonApiDotNetCore.Models
{
@@ -33,5 +33,11 @@ public override bool Equals(object obj)
}
return IsHasMany == attr.IsHasMany && PublicRelationshipName.Equals(attr.PublicRelationshipName);
}
+
+ ///
+ /// Whether or not the provided exposed name is equivalent to the one defined in on the model
+ ///
+ public virtual bool Is(string publicRelationshipName)
+ => string.Equals(publicRelationshipName, PublicRelationshipName, StringComparison.OrdinalIgnoreCase);
}
}
diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
index a32a40c5d4..649d6435ff 100644
--- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
+++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs
@@ -29,11 +29,14 @@ public object Deserialize(string requestBody)
{
try
{
- // TODO: determine whether or not the token should be re-used rather than performing full
- // deserialization again from the string
var bodyJToken = JToken.Parse(requestBody);
- if(bodyJToken.SelectToken("operations") != null)
+
+ if (RequestIsOperation(bodyJToken))
{
+ _jsonApiContext.IsBulkOperationRequest = true;
+
+ // TODO: determine whether or not the token should be re-used rather than performing full
+ // deserialization again from the string
var operations = JsonConvert.DeserializeObject(requestBody);
if (operations == null)
throw new JsonApiException(400, "Failed to deserialize operations request.");
@@ -53,6 +56,10 @@ public object Deserialize(string requestBody)
}
}
+ private bool RequestIsOperation(JToken bodyJToken)
+ => _jsonApiContext.Options.EnableOperations
+ && (bodyJToken.SelectToken("operations") != null);
+
public TEntity Deserialize(string requestBody) => (TEntity)Deserialize(requestBody);
public object DeserializeRelationship(string requestBody)
@@ -77,7 +84,7 @@ public List DeserializeList(string requestBody)
try
{
var documents = JsonConvert.DeserializeObject(requestBody);
-
+
var deserializedList = new List();
foreach (var data in documents.Data)
{
@@ -191,7 +198,7 @@ private object SetHasOneRelationship(object entity,
if (relationshipAttr == null)
throw new JsonApiException(400, $"{_jsonApiContext.RequestEntity.EntityName} does not contain a relationship '{relationshipName}'");
- var rio = (ResourceIdentifierObject) relationshipData.ExposedData;
+ var rio = (ResourceIdentifierObject)relationshipData.ExposedData;
if (rio == null) return entity;
diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs
index 8e94835266..20d119ff07 100644
--- a/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs
+++ b/src/JsonApiDotNetCore/Serialization/JsonApiSerializer.cs
@@ -37,9 +37,12 @@ public string Serialize(object entity)
if (entity == null)
return GetNullDataResponse();
- if (entity.GetType() == typeof(ErrorCollection) || _jsonApiContext.RequestEntity == null)
+ if (entity.GetType() == typeof(ErrorCollection) || (_jsonApiContext.RequestEntity == null && _jsonApiContext.IsBulkOperationRequest == false))
return GetErrorJson(entity, _logger);
+ if (_jsonApiContext.IsBulkOperationRequest)
+ return _serialize(entity);
+
if (entity is IEnumerable)
return SerializeDocuments(entity);
@@ -86,4 +89,4 @@ private string _serialize(object obj)
return JsonConvert.SerializeObject(obj, _jsonApiContext.Options.SerializerSettings);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/JsonApiDotNetCore/Services/EntityResourceService.cs b/src/JsonApiDotNetCore/Services/EntityResourceService.cs
index af86692166..91eabb9cfd 100644
--- a/src/JsonApiDotNetCore/Services/EntityResourceService.cs
+++ b/src/JsonApiDotNetCore/Services/EntityResourceService.cs
@@ -96,6 +96,8 @@ public virtual async Task