diff --git a/Build.ps1 b/Build.ps1 index 33cc65f4d9..15123ebe79 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -27,21 +27,7 @@ CheckLastExitCode dotnet build -c Release CheckLastExitCode -# Workaround: running 'dotnet test -c Release' fails for yet unknown reasons on AppVeyor, so we run tests one by one. - -dotnet test ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj -c Release --no-build -CheckLastExitCode - -dotnet test ./test/DiscoveryTests/DiscoveryTests.csproj -c Release --no-build -CheckLastExitCode - -dotnet test ./test/IntegrationTests/IntegrationTests.csproj -c Release --no-build -CheckLastExitCode - -dotnet test ./test/UnitTests/UnitTests.csproj -c Release --no-build -CheckLastExitCode - -dotnet test ./test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj -c Release --no-build +dotnet test -c Release --no-build CheckLastExitCode Write-Output "APPVEYOR_REPO_TAG: $env:APPVEYOR_REPO_TAG" @@ -62,7 +48,7 @@ If($env:APPVEYOR_REPO_TAG -eq $true) { } } Else { - Write-Output "VERSION-SUFFIX: alpha1-$revision" + Write-Output "VERSION-SUFFIX: alpha5-$revision" 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 --include-symbols CheckLastExitCode diff --git a/appveyor.yml b/appveyor.yml index 406d1e974e..1f1c3b0cce 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,13 +2,8 @@ version: '{build}' os: Visual Studio 2019 environment: - POSTGRES_PORT: tcp://localhost:5432 - POSTGRES_ENV_POSTGRES_USER: postgres - POSTGRES_ENV_POSTGRES_PASSWORD: Password12! - POSTGRES_ENV_POSTGRES_DB: JsonApiDotNetCoreExample PGUSER: postgres PGPASSWORD: Password12! - Data:DefaultConnection: "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=Password12!" ACCESS_TOKEN: secure: g1T332Uarmdgtkftchpafa8tDP/7eM4O0BD6iu1wu+zR224IyH5R8pb4sTChr4Ia @@ -63,10 +58,9 @@ init: - SET PATH=C:\Program Files\PostgreSQL\9.6\bin\;%PATH% services: - - postgresql + - postgresql96 build_script: -- ps: createdb JsonApiDotNetCoreExample - ps: dotnet --version - ps: .\Build.ps1 diff --git a/src/Examples/GettingStarted/Controllers/ArticlesController.cs b/src/Examples/GettingStarted/Controllers/ArticlesController.cs index c25e740825..fc79e4bed8 100644 --- a/src/Examples/GettingStarted/Controllers/ArticlesController.cs +++ b/src/Examples/GettingStarted/Controllers/ArticlesController.cs @@ -4,7 +4,7 @@ using JsonApiDotNetCore.Services; using Microsoft.Extensions.Logging; -namespace GettingStarted +namespace GettingStarted.Controllers { public sealed class ArticlesController : JsonApiController
{ diff --git a/src/Examples/GettingStarted/Controllers/PeopleController.cs b/src/Examples/GettingStarted/Controllers/PeopleController.cs index e6a9a2a4ff..05baa40b68 100644 --- a/src/Examples/GettingStarted/Controllers/PeopleController.cs +++ b/src/Examples/GettingStarted/Controllers/PeopleController.cs @@ -4,7 +4,7 @@ using JsonApiDotNetCore.Services; using Microsoft.Extensions.Logging; -namespace GettingStarted +namespace GettingStarted.Controllers { public sealed class PeopleController : JsonApiController { diff --git a/src/Examples/GettingStarted/Data/SampleDbContext.cs b/src/Examples/GettingStarted/Data/SampleDbContext.cs index ede5e02baf..80397254d8 100644 --- a/src/Examples/GettingStarted/Data/SampleDbContext.cs +++ b/src/Examples/GettingStarted/Data/SampleDbContext.cs @@ -1,14 +1,20 @@ using GettingStarted.Models; -using GettingStarted.ResourceDefinitionExample; using Microsoft.EntityFrameworkCore; -namespace GettingStarted +namespace GettingStarted.Data { public class SampleDbContext : DbContext { - public SampleDbContext(DbContextOptions options) : base(options) { } public DbSet
Articles { get; set; } - public DbSet People { get; set; } - public DbSet Models { get; set; } + + public SampleDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(); + } } -} \ No newline at end of file +} diff --git a/src/Examples/GettingStarted/Models/Article.cs b/src/Examples/GettingStarted/Models/Article.cs index e8bf99fcaf..b10ede2fb7 100644 --- a/src/Examples/GettingStarted/Models/Article.cs +++ b/src/Examples/GettingStarted/Models/Article.cs @@ -6,8 +6,8 @@ public sealed class Article : Identifiable { [Attr] public string Title { get; set; } + [HasOne] public Person Author { get; set; } - public int AuthorId { get; set; } } } diff --git a/src/Examples/GettingStarted/Models/Person.cs b/src/Examples/GettingStarted/Models/Person.cs index 952edd6642..afbdc091ba 100644 --- a/src/Examples/GettingStarted/Models/Person.cs +++ b/src/Examples/GettingStarted/Models/Person.cs @@ -5,10 +5,10 @@ namespace GettingStarted.Models { public sealed class Person : Identifiable { - [Attr] + [Attr] public string Name { get; set; } - [HasMany] + [HasMany] public ICollection
Articles { get; set; } } } diff --git a/src/Examples/GettingStarted/Program.cs b/src/Examples/GettingStarted/Program.cs index d558bead45..c7d55fcf4d 100644 --- a/src/Examples/GettingStarted/Program.cs +++ b/src/Examples/GettingStarted/Program.cs @@ -1,5 +1,5 @@ -using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; namespace GettingStarted { @@ -7,12 +7,14 @@ public class Program { public static void Main(string[] args) { - CreateWebHostBuilder(args).Build().Run(); + CreateHostBuilder(args).Build().Run(); } - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .UseUrls("http://localhost:5001"); + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } } diff --git a/src/Examples/GettingStarted/Properties/launchSettings.json b/src/Examples/GettingStarted/Properties/launchSettings.json index 417b2e9f1e..7083903219 100644 --- a/src/Examples/GettingStarted/Properties/launchSettings.json +++ b/src/Examples/GettingStarted/Properties/launchSettings.json @@ -1,12 +1,30 @@ { - "profiles": { - "GettingStarted": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14141", + "sslPort": 44344 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "launchUrl": "api/people", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Kestrel": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "api/people", + "applicationUrl": "https://localhost:44344;http://localhost:14141", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } } - } -} \ No newline at end of file +} diff --git a/src/Examples/GettingStarted/README.md b/src/Examples/GettingStarted/README.md index d2c91c6d6a..d79afbd494 100644 --- a/src/Examples/GettingStarted/README.md +++ b/src/Examples/GettingStarted/README.md @@ -5,10 +5,10 @@ `dotnet run` to run the project You can verify the project is running by checking this endpoint: -`localhost:5001/api/sample-model` +`localhost:14141/api/people` For further documentation and implementation of a JsonApiDotnetCore Application see the documentation or GitHub page: Repository: https://github.com/json-api-dotnet/JsonApiDotNetCore -Documentation: https://json-api-dotnet.github.io/ \ No newline at end of file +Documentation: https://json-api-dotnet.github.io/ diff --git a/src/Examples/GettingStarted/Startup.cs b/src/Examples/GettingStarted/Startup.cs index 0a394d9f9b..5a59ba2514 100644 --- a/src/Examples/GettingStarted/Startup.cs +++ b/src/Examples/GettingStarted/Startup.cs @@ -1,31 +1,53 @@ +using GettingStarted.Data; +using GettingStarted.Models; +using JsonApiDotNetCore; using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; -using JsonApiDotNetCore; +using Microsoft.Extensions.DependencyInjection; namespace GettingStarted { public sealed class Startup { + // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddDbContext(options => - { - options.UseSqlite("Data Source=sample.db"); - }); + services.AddDbContext( + options => options.UseSqlite("Data Source=sample.db")); - var mvcBuilder = services.AddMvcCore(); - services.AddJsonApi( - options => options.Namespace = "api", - discover => discover.AddCurrentAssembly(), mvcBuilder: mvcBuilder); + services.AddJsonApi( + options => options.Namespace = "api"); } + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, SampleDbContext context) { - // indices need to be reset context.Database.EnsureDeleted(); context.Database.EnsureCreated(); + CreateSampleData(context); + app.UseJsonApi(); } + + private static void CreateSampleData(SampleDbContext context) + { + context.Articles.AddRange(new Article + { + Title = "What's new in JsonApiDotNetCore", + Author = new Person + { + Name = "John Doe" + } + }, new Article + { + Title = ".NET Core Best Practices", + Author = new Person + { + Name = "Microsoft" + } + }); + + context.SaveChanges(); + } } } diff --git a/src/Examples/GettingStarted/appsettings.json b/src/Examples/GettingStarted/appsettings.json new file mode 100644 index 0000000000..eb57d97280 --- /dev/null +++ b/src/Examples/GettingStarted/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/ModelsController.cs similarity index 84% rename from src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs rename to src/Examples/JsonApiDotNetCoreExample/Controllers/ModelsController.cs index 3b2d83e8c8..2c9ce2898a 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelsController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/ModelsController.cs @@ -1,9 +1,10 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; using Microsoft.Extensions.Logging; -namespace GettingStarted.ResourceDefinitionExample +namespace JsonApiDotNetCoreExample.Controllers { public sealed class ModelsController : JsonApiController { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/ArticleDefinition.cs similarity index 80% rename from src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/ArticleDefinition.cs index 1a598d585e..94526f898a 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/ArticleResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/ArticleDefinition.cs @@ -2,17 +2,17 @@ using System.Linq; using System.Net; using JsonApiDotNetCore.Exceptions; -using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Models.JsonApiDocuments; +using JsonApiDotNetCoreExample.Models; -namespace JsonApiDotNetCoreExample.Resources +namespace JsonApiDotNetCoreExample.Definitions { - public class ArticleResource : ResourceDefinition
+ public class ArticleDefinition : ResourceDefinition
{ - public ArticleResource(IResourceGraph resourceGraph) : base(resourceGraph) { } + public ArticleDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } public override IEnumerable
OnReturn(HashSet
entities, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/LockableDefinition.cs similarity index 75% rename from src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/LockableDefinition.cs index c9addf5e09..233a4f79bb 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/LockableResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/LockableDefinition.cs @@ -7,11 +7,11 @@ using JsonApiDotNetCore.Models.JsonApiDocuments; using JsonApiDotNetCoreExample.Models; -namespace JsonApiDotNetCoreExample.Resources +namespace JsonApiDotNetCoreExample.Definitions { - public abstract class LockableResource : ResourceDefinition where T : class, IIsLockable, IIdentifiable + public abstract class LockableDefinition : ResourceDefinition where T : class, IIsLockable, IIdentifiable { - protected LockableResource(IResourceGraph resourceGraph) : base(resourceGraph) { } + protected LockableDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } protected void DisallowLocked(IEnumerable entities) { diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/ModelDefinition.cs similarity index 88% rename from src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/ModelDefinition.cs index ac89fc97f2..f471347126 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/ModelDefinition.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/ModelDefinition.cs @@ -1,7 +1,8 @@ using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; +using JsonApiDotNetCoreExample.Models; -namespace GettingStarted.ResourceDefinitionExample +namespace JsonApiDotNetCoreExample.Definitions { public class ModelDefinition : ResourceDefinition { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/PassportDefinition.cs similarity index 88% rename from src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/PassportDefinition.cs index 30fe8d0d2a..cd4a87a62b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PassportResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/PassportDefinition.cs @@ -2,17 +2,17 @@ using System.Linq; using System.Net; using JsonApiDotNetCore.Exceptions; -using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Models.JsonApiDocuments; +using JsonApiDotNetCoreExample.Models; -namespace JsonApiDotNetCoreExample.Resources +namespace JsonApiDotNetCoreExample.Definitions { - public class PassportResource : ResourceDefinition + public class PassportDefinition : ResourceDefinition { - public PassportResource(IResourceGraph resourceGraph) : base(resourceGraph) + public PassportDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/PersonDefinition.cs similarity index 84% rename from src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/PersonDefinition.cs index c1969e9b19..c73224ebb9 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/PersonResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/PersonDefinition.cs @@ -1,15 +1,15 @@ using System.Collections.Generic; using System.Linq; using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models; +using JsonApiDotNetCoreExample.Models; -namespace JsonApiDotNetCoreExample.Resources +namespace JsonApiDotNetCoreExample.Definitions { - public class PersonResource : LockableResource, IHasMeta + public class PersonDefinition : LockableDefinition, IHasMeta { - public PersonResource(IResourceGraph resourceGraph) : base(resourceGraph) { } + public PersonDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } public override IEnumerable BeforeUpdateRelationship(HashSet ids, IRelationshipsDictionary entitiesByRelationship, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/TagDefinition.cs similarity index 76% rename from src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/TagDefinition.cs index c9b90a0bd3..d9d7366437 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TagResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/TagDefinition.cs @@ -1,15 +1,15 @@ using System.Collections.Generic; using System.Linq; -using JsonApiDotNetCore.Models; using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCoreExample.Models; -namespace JsonApiDotNetCoreExample.Resources +namespace JsonApiDotNetCoreExample.Definitions { - public class TagResource : ResourceDefinition + public class TagDefinition : ResourceDefinition { - public TagResource(IResourceGraph resourceGraph) : base(resourceGraph) { } + public TagDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } public override IEnumerable BeforeCreate(IEntityHashSet affected, ResourcePipeline pipeline) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoDefinition.cs similarity index 83% rename from src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/TodoDefinition.cs index a741d60eda..6f27e716a3 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/TodoResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoDefinition.cs @@ -3,15 +3,15 @@ using System.Net; using JsonApiDotNetCore.Exceptions; using JsonApiDotNetCore.Hooks; -using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCore.Internal.Contracts; using JsonApiDotNetCore.Models.JsonApiDocuments; +using JsonApiDotNetCoreExample.Models; -namespace JsonApiDotNetCoreExample.Resources +namespace JsonApiDotNetCoreExample.Definitions { - public class TodoResource : LockableResource + public class TodoDefinition : LockableDefinition { - public TodoResource(IResourceGraph resourceGraph) : base(resourceGraph) { } + public TodoDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { } public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/UserDefinition.cs similarity index 85% rename from src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs rename to src/Examples/JsonApiDotNetCoreExample/Definitions/UserDefinition.cs index 517e9137c0..5c2ae3fed0 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Resources/UserResource.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/UserDefinition.cs @@ -1,14 +1,14 @@ using System.Linq; +using JsonApiDotNetCore.Internal.Contracts; +using JsonApiDotNetCore.Internal.Query; using JsonApiDotNetCore.Models; using JsonApiDotNetCoreExample.Models; -using JsonApiDotNetCore.Internal.Query; -using JsonApiDotNetCore.Internal.Contracts; -namespace JsonApiDotNetCoreExample.Resources +namespace JsonApiDotNetCoreExample.Definitions { - public class UserResource : ResourceDefinition + public class UserDefinition : ResourceDefinition { - public UserResource(IResourceGraph resourceGraph) : base(resourceGraph) + public UserDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { HideFields(u => u.Password); } diff --git a/src/Examples/JsonApiDotNetCoreExample/Dockerfile b/src/Examples/JsonApiDotNetCoreExample/Dockerfile index 048dfbae94..c5a5d90ff4 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Dockerfile +++ b/src/Examples/JsonApiDotNetCoreExample/Dockerfile @@ -8,6 +8,6 @@ RUN ["dotnet", "restore"] RUN ["dotnet", "build"] -EXPOSE 5000/tcp +EXPOSE 14140/tcp -CMD ["dotnet", "run", "--server.urls", "http://*:5000"] +CMD ["dotnet", "run", "--server.urls", "http://*:14140"] diff --git a/src/Examples/GettingStarted/ResourceDefinitionExample/Model.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Model.cs similarity index 75% rename from src/Examples/GettingStarted/ResourceDefinitionExample/Model.cs rename to src/Examples/JsonApiDotNetCoreExample/Models/Model.cs index c12d8946f8..977292d0a7 100644 --- a/src/Examples/GettingStarted/ResourceDefinitionExample/Model.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Model.cs @@ -1,6 +1,6 @@ using JsonApiDotNetCore.Models; -namespace GettingStarted.ResourceDefinitionExample +namespace JsonApiDotNetCoreExample.Models { public sealed class Model : Identifiable { diff --git a/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json b/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json index 0e97dd4898..1e3998e4f0 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json +++ b/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json @@ -1,29 +1,30 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:1479/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": false, - "launchUrl": "api/values", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14140", + "sslPort": 44340 + } }, - "JsonApiDotNetCoreExample": { - "commandName": "Project", - "launchBrowser": false, - "launchUrl": "http://localhost:5000/api/values", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:5000/" + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "launchUrl": "api/v1/todoItems", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Kestrel": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "api/v1/todoItems", + "applicationUrl": "https://localhost:44340;http://localhost:14140", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } } - } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs index 56026e96d9..9b10947629 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs @@ -9,7 +9,9 @@ namespace JsonApiDotNetCoreExample /// public sealed class NoDefaultPageSizeStartup : TestStartup { - public NoDefaultPageSizeStartup(IWebHostEnvironment env) : base(env) { } + public NoDefaultPageSizeStartup(IWebHostEnvironment env) : base(env) + { + } protected override void ConfigureJsonApiOptions(JsonApiOptions options) { diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs index 7ac33389fb..169d01e99a 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs @@ -16,7 +16,7 @@ namespace JsonApiDotNetCoreExample { public class Startup { - public readonly IConfiguration Config; + private readonly string _connectionString; public Startup(IWebHostEnvironment env) { @@ -25,7 +25,10 @@ public Startup(IWebHostEnvironment env) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); - Config = builder.Build(); + var configuration = builder.Build(); + + string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; + _connectionString = configuration["Data:DefaultConnection"].Replace("###", postgresPassword); } public virtual void ConfigureServices(IServiceCollection services) @@ -40,7 +43,7 @@ public virtual void ConfigureServices(IServiceCollection services) { options .EnableSensitiveDataLogging() - .UseNpgsql(GetDbConnectionString(), innerOptions => innerOptions.SetPostgresVersion(new Version(9,6))); + .UseNpgsql(_connectionString, innerOptions => innerOptions.SetPostgresVersion(new Version(9,6))); }, ServiceLifetime.Transient) .AddJsonApi(ConfigureJsonApiOptions, discovery => discovery.AddCurrentAssembly()); @@ -72,7 +75,5 @@ public void Configure( context.Database.EnsureCreated(); app.UseJsonApi(); } - - public string GetDbConnectionString() => Config["Data:DefaultConnection"]; } } diff --git a/src/Examples/JsonApiDotNetCoreExample/appsettings.json b/src/Examples/JsonApiDotNetCoreExample/appsettings.json index 58ef17a7a4..2b56699951 100644 --- 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=postgres" + "DefaultConnection": "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=###" }, "Logging": { - "IncludeScopes": false, "LogLevel": { - "Default": "Debug", - "System": "Warning", + "Default": "Warning", "Microsoft": "Warning", - "Microsoft.EntityFrameworkCore": "Debug" + "Microsoft.EntityFrameworkCore.Database.Command": "Warning" } - } + }, + "AllowedHosts": "*" } diff --git a/src/Examples/NoEntityFrameworkExample/Controllers/TodoItemsController.cs b/src/Examples/NoEntityFrameworkExample/Controllers/WorkItemsController.cs similarity index 71% rename from src/Examples/NoEntityFrameworkExample/Controllers/TodoItemsController.cs rename to src/Examples/NoEntityFrameworkExample/Controllers/WorkItemsController.cs index 79b487d721..6d1abae1f0 100644 --- a/src/Examples/NoEntityFrameworkExample/Controllers/TodoItemsController.cs +++ b/src/Examples/NoEntityFrameworkExample/Controllers/WorkItemsController.cs @@ -6,12 +6,12 @@ namespace NoEntityFrameworkExample.Controllers { - public sealed class TodoItemsController : JsonApiController + public sealed class WorkItemsController : JsonApiController { - public TodoItemsController( + public WorkItemsController( IJsonApiOptions jsonApiOptions, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(jsonApiOptions, loggerFactory, resourceService) { } } diff --git a/src/Examples/NoEntityFrameworkExample/Data/AppDbContext.cs b/src/Examples/NoEntityFrameworkExample/Data/AppDbContext.cs index 383041c6be..abf11869dd 100644 --- a/src/Examples/NoEntityFrameworkExample/Data/AppDbContext.cs +++ b/src/Examples/NoEntityFrameworkExample/Data/AppDbContext.cs @@ -1,14 +1,15 @@ -using NoEntityFrameworkExample.Models; using Microsoft.EntityFrameworkCore; +using NoEntityFrameworkExample.Models; namespace NoEntityFrameworkExample.Data { public sealed class AppDbContext : DbContext { - public AppDbContext(DbContextOptions options) - : base(options) - { } + public DbSet WorkItems { get; set; } - public DbSet TodoItems { get; set; } + public AppDbContext(DbContextOptions options) + : base(options) + { + } } } diff --git a/src/Examples/NoEntityFrameworkExample/Models/TodoItem.cs b/src/Examples/NoEntityFrameworkExample/Models/TodoItem.cs deleted file mode 100644 index 8e9c7c8674..0000000000 --- a/src/Examples/NoEntityFrameworkExample/Models/TodoItem.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using JsonApiDotNetCore.Models; - -namespace NoEntityFrameworkExample.Models -{ - public sealed class TodoItem : Identifiable - { - public TodoItem() - { - GuidProperty = Guid.NewGuid(); - } - - public bool IsLocked { get; set; } - - [Attr] - public string Description { get; set; } - - [Attr] - public long Ordinal { get; set; } - - [Attr] - public Guid GuidProperty { get; set; } - } -} diff --git a/src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs b/src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs new file mode 100644 index 0000000000..d2d1b274e6 --- /dev/null +++ b/src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs @@ -0,0 +1,20 @@ +using System; +using JsonApiDotNetCore.Models; + +namespace NoEntityFrameworkExample.Models +{ + public sealed class WorkItem : Identifiable + { + [Attr] + public bool IsBlocked { get; set; } + + [Attr] + public string Title { get; set; } + + [Attr] + public long DurationInHours { get; set; } + + [Attr] + public Guid ProjectId { get; set; } = Guid.NewGuid(); + } +} diff --git a/src/Examples/NoEntityFrameworkExample/Program.cs b/src/Examples/NoEntityFrameworkExample/Program.cs index 9cd9c3ce22..a4d260e5a6 100755 --- a/src/Examples/NoEntityFrameworkExample/Program.cs +++ b/src/Examples/NoEntityFrameworkExample/Program.cs @@ -1,5 +1,5 @@ -using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; namespace NoEntityFrameworkExample { @@ -7,10 +7,14 @@ public class Program { public static void Main(string[] args) { - CreateWebHostBuilder(args).Build().Run(); + CreateHostBuilder(args).Build().Run(); } - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup(); + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } } diff --git a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json index 087c2dfacb..e7b6f86e76 100644 --- a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json +++ b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json @@ -1,27 +1,30 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:22785/", - "sslPort": 44393 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14149", + "sslPort": 44349 + } }, - "NoEntityFrameworkExample": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "launchUrl": "/api/reports", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Kestrel": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "/api/reports", + "applicationUrl": "https://localhost:44349;http://localhost:14149", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } } - } -} \ No newline at end of file +} diff --git a/src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs b/src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs deleted file mode 100644 index c68977c547..0000000000 --- a/src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Configuration; -using Npgsql; -using Dapper; -using System.Data; -using NoEntityFrameworkExample.Models; -using System.Linq; - -namespace NoEntityFrameworkExample.Services -{ - public sealed class TodoItemService : IResourceService - { - private readonly string _connectionString; - - public TodoItemService(IConfiguration config) - { - _connectionString = config.GetValue("Data:DefaultConnection"); - } - - private IDbConnection Connection => new NpgsqlConnection(_connectionString); - - private async Task> QueryAsync(Func>> query) - { - using IDbConnection dbConnection = Connection; - dbConnection.Open(); - return await query(dbConnection); - } - - public async Task> GetAsync() - { - return await QueryAsync(async connection => - await connection.QueryAsync("select * from \"TodoItems\"")); - } - - public async Task GetAsync(int id) - { - var query = await QueryAsync(async connection => - await connection.QueryAsync("select * from \"TodoItems\" where \"Id\"= @id", new {id})); - - return query.SingleOrDefault(); - } - - public Task GetRelationshipAsync(int id, string relationshipName) - { - throw new NotImplementedException(); - } - - public Task GetRelationshipsAsync(int id, string relationshipName) - { - throw new NotImplementedException(); - } - - public async Task CreateAsync(TodoItem entity) - { - return (await QueryAsync(async connection => - { - var query = "insert into \"TodoItems\" (\"Description\", \"IsLocked\", \"Ordinal\", \"GuidProperty\") values (@description, @isLocked, @ordinal, @guidProperty) returning \"Id\",\"Description\", \"IsLocked\", \"Ordinal\", \"GuidProperty\""; - var result = await connection.QueryAsync(query, new { description = entity.Description, ordinal = entity.Ordinal, guidProperty = entity.GuidProperty, isLocked = entity.IsLocked}); - return result; - })).SingleOrDefault(); - } - - public Task DeleteAsync(int id) - { - throw new NotImplementedException(); - } - - public Task UpdateAsync(int id, TodoItem entity) - { - throw new NotImplementedException(); - } - - public Task UpdateRelationshipsAsync(int id, string relationshipName, object relationships) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs b/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs new file mode 100644 index 0000000000..4b6fa24055 --- /dev/null +++ b/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using Dapper; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Configuration; +using NoEntityFrameworkExample.Models; +using Npgsql; + +namespace NoEntityFrameworkExample.Services +{ + public sealed class WorkItemService : IResourceService + { + private readonly string _connectionString; + + public WorkItemService(IConfiguration configuration) + { + string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; + _connectionString = configuration["Data:DefaultConnection"].Replace("###", postgresPassword); + } + + public async Task> GetAsync() + { + return await QueryAsync(async connection => + await connection.QueryAsync(@"select * from ""WorkItems""")); + } + + public async Task GetAsync(int id) + { + var query = await QueryAsync(async connection => + await connection.QueryAsync(@"select * from ""WorkItems"" where ""Id""=@id", new { id })); + + return query.Single(); + } + + public Task GetRelationshipAsync(int id, string relationshipName) + { + throw new NotImplementedException(); + } + + public Task GetRelationshipsAsync(int id, string relationshipName) + { + throw new NotImplementedException(); + } + + public async Task CreateAsync(WorkItem entity) + { + return (await QueryAsync(async connection => + { + var query = @"insert into ""WorkItems"" (""Title"", ""IsBlocked"", ""DurationInHours"", ""ProjectId"") values (@description, @isLocked, @ordinal, @uniqueId) returning ""Id"", ""Title"", ""IsBlocked"", ""DurationInHours"", ""ProjectId"""; + var result = await connection.QueryAsync(query, new { description = entity.Title, ordinal = entity.DurationInHours, uniqueId = entity.ProjectId, isLocked = entity.IsBlocked }); + return result; + })).SingleOrDefault(); + } + + public async Task DeleteAsync(int id) + { + await QueryAsync(async connection => + await connection.QueryAsync(@"delete from ""WorkItems"" where ""Id""=@id", new { id })); + } + + public Task UpdateAsync(int id, WorkItem entity) + { + throw new NotImplementedException(); + } + + public Task UpdateRelationshipsAsync(int id, string relationshipName, object relationships) + { + throw new NotImplementedException(); + } + + private async Task> QueryAsync(Func>> query) + { + using IDbConnection dbConnection = GetConnection; + dbConnection.Open(); + return await query(dbConnection); + } + + private IDbConnection GetConnection => new NpgsqlConnection(_connectionString); + } +} diff --git a/src/Examples/NoEntityFrameworkExample/Startup.cs b/src/Examples/NoEntityFrameworkExample/Startup.cs index 2805c984b7..bdcbea066c 100644 --- a/src/Examples/NoEntityFrameworkExample/Startup.cs +++ b/src/Examples/NoEntityFrameworkExample/Startup.cs @@ -2,55 +2,48 @@ using JsonApiDotNetCore; using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using NoEntityFrameworkExample.Services; -using Microsoft.EntityFrameworkCore; using NoEntityFrameworkExample.Data; using NoEntityFrameworkExample.Models; +using NoEntityFrameworkExample.Services; namespace NoEntityFrameworkExample { public class Startup { - public Startup(IWebHostEnvironment env) + private readonly string _connectionString; + + public Startup(IConfiguration configuration) { - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) - .AddEnvironmentVariables(); - Configuration = builder.Build(); + string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres"; + _connectionString = configuration["Data:DefaultConnection"].Replace("###", postgresPassword); } - public IConfigurationRoot Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public virtual void ConfigureServices(IServiceCollection services) + public void ConfigureServices(IServiceCollection services) { - // Add framework services. - var mvcBuilder = services.AddMvcCore(); services.AddJsonApi( - options => options.Namespace = "api/v1", - resources: resources => resources.AddResource("todoItems"), - mvcBuilder: mvcBuilder - ); - services.AddScoped, TodoItemService>(); - var optionsBuilder = new DbContextOptionsBuilder(); - optionsBuilder.UseNpgsql(GetDbConnectionString(), options => options.SetPostgresVersion(new Version(9,6))); - services.AddSingleton(Configuration); - services.AddSingleton(optionsBuilder.Options); - services.AddScoped(); + options => options.Namespace = "api/v1", + resources: builder => builder.AddResource("workItems") + ); + + services.AddScoped, WorkItemService>(); + + services.AddDbContext(options => + { + options.UseNpgsql(_connectionString, + postgresOptions => postgresOptions.SetPostgresVersion(new Version(9, 6))); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, AppDbContext context) { context.Database.EnsureCreated(); + app.UseJsonApi(); } - - public string GetDbConnectionString() => Configuration["Data:DefaultConnection"]; } } diff --git a/src/Examples/NoEntityFrameworkExample/appsettings.json b/src/Examples/NoEntityFrameworkExample/appsettings.json index 7392e6fb01..ef303ea8e2 100644 --- a/src/Examples/NoEntityFrameworkExample/appsettings.json +++ b/src/Examples/NoEntityFrameworkExample/appsettings.json @@ -1,11 +1,13 @@ { "Data": { - "DefaultConnection": "Host=localhost;Port=5432;Database=JsonApiDotNetCoreNoEFCoreExample;User ID=postgres;Password=postgres" + "DefaultConnection": "Host=localhost;Port=5432;Database=NoEntityFrameworkExample;User ID=postgres;Password=###" }, "Logging": { - "IncludeScopes": false, "LogLevel": { - "Default": "Warning" + "Default": "Warning", + "Microsoft": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Information" } - } + }, + "AllowedHosts": "*" } diff --git a/src/Examples/ReportsExample/.bowerrc b/src/Examples/ReportsExample/.bowerrc deleted file mode 100644 index 78d4e9d826..0000000000 --- a/src/Examples/ReportsExample/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory": "wwwroot/lib" -} diff --git a/src/Examples/ReportsExample/Models/Report.cs b/src/Examples/ReportsExample/Models/Report.cs index 6a682f3dc1..b7433e89cc 100644 --- a/src/Examples/ReportsExample/Models/Report.cs +++ b/src/Examples/ReportsExample/Models/Report.cs @@ -6,13 +6,8 @@ public sealed class Report : Identifiable { [Attr] public string Title { get; set; } - - [Attr] - public ComplexType ComplexType { get; set; } - } - public sealed class ComplexType - { - public string CompoundPropertyName { get; set; } + [Attr] + public ReportStatistics Statistics { get; set; } } } diff --git a/src/Examples/ReportsExample/Models/ReportStatistics.cs b/src/Examples/ReportsExample/Models/ReportStatistics.cs new file mode 100644 index 0000000000..85b1ffdf1f --- /dev/null +++ b/src/Examples/ReportsExample/Models/ReportStatistics.cs @@ -0,0 +1,8 @@ +namespace ReportsExample.Models +{ + public sealed class ReportStatistics + { + public string ProgressIndication { get; set; } + public int HoursSpent { get; set; } + } +} diff --git a/src/Examples/ReportsExample/Program.cs b/src/Examples/ReportsExample/Program.cs index 3794a268c4..d2d216af50 100644 --- a/src/Examples/ReportsExample/Program.cs +++ b/src/Examples/ReportsExample/Program.cs @@ -1,5 +1,5 @@ -using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; namespace ReportsExample { @@ -7,10 +7,14 @@ public class Program { public static void Main(string[] args) { - CreateWebHostBuilder(args).Build().Run(); + CreateHostBuilder(args).Build().Run(); } - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup(); + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } } diff --git a/src/Examples/ReportsExample/Properties/launchSettings.json b/src/Examples/ReportsExample/Properties/launchSettings.json index 643dc89799..0fcb421625 100644 --- a/src/Examples/ReportsExample/Properties/launchSettings.json +++ b/src/Examples/ReportsExample/Properties/launchSettings.json @@ -1,27 +1,30 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:55653/", - "sslPort": 0 - } - }, - "profiles": { - "ReportsExample": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:55654/" + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14148", + "sslPort": 44348 + } }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "launchUrl": "/api/reports", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Kestrel": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "/api/reports", + "applicationUrl": "https://localhost:44348;http://localhost:14148", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } } - } -} \ No newline at end of file +} diff --git a/src/Examples/ReportsExample/Services/ReportService.cs b/src/Examples/ReportsExample/Services/ReportService.cs index 7fa801d827..c502fa9987 100644 --- a/src/Examples/ReportsExample/Services/ReportService.cs +++ b/src/Examples/ReportsExample/Services/ReportService.cs @@ -17,22 +17,24 @@ public ReportService(ILoggerFactory loggerFactory) public Task> GetAsync() { - _logger.LogWarning("GetAsync"); + _logger.LogInformation("GetAsync"); - var task = new Task>(Get); - - task.RunSynchronously(TaskScheduler.Default); + IEnumerable reports = GetReports(); - return task; + return Task.FromResult(reports); } - private IEnumerable Get() + private IEnumerable GetReports() { - return new List { - new Report { - Title = "My Report", - ComplexType = new ComplexType { - CompoundPropertyName = "value" + return new List + { + new Report + { + Title = "Status Report", + Statistics = new ReportStatistics + { + ProgressIndication = "Almost done", + HoursSpent = 24 } } }; diff --git a/src/Examples/ReportsExample/Startup.cs b/src/Examples/ReportsExample/Startup.cs index d02f29dc38..f4f077d016 100644 --- a/src/Examples/ReportsExample/Startup.cs +++ b/src/Examples/ReportsExample/Startup.cs @@ -1,31 +1,23 @@ using JsonApiDotNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace ReportsExample { public sealed class Startup { - public readonly IConfiguration Config; - - public Startup(IWebHostEnvironment env) + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) { - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) - .AddEnvironmentVariables(); - - Config = builder.Build(); + services.AddJsonApi( + options => options.Namespace = "api", + discovery => discovery.AddCurrentAssembly()); } - public void ConfigureServices(IServiceCollection services) + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app) { - var mvcBuilder = services.AddMvcCore(); - services.AddJsonApi( - opt => opt.Namespace = "api", - discovery => discovery.AddCurrentAssembly(), mvcBuilder: mvcBuilder); + app.UseJsonApi(); } } } diff --git a/src/Examples/ReportsExample/appsettings.json b/src/Examples/ReportsExample/appsettings.json index 5766595e6d..69a873bd79 100644 --- a/src/Examples/ReportsExample/appsettings.json +++ b/src/Examples/ReportsExample/appsettings.json @@ -1,8 +1,9 @@ -{ - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - } +{ + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft": "Warning" + } + }, + "AllowedHosts": "*" } diff --git a/test/DiscoveryTests/DiscoveryTests.csproj b/test/DiscoveryTests/DiscoveryTests.csproj index 57c14b39d4..94d67936e3 100644 --- a/test/DiscoveryTests/DiscoveryTests.csproj +++ b/test/DiscoveryTests/DiscoveryTests.csproj @@ -4,7 +4,13 @@ - + + PreserveNewest + + + + + diff --git a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs index e58de0c4dc..3f1266b3c6 100644 --- a/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs +++ b/test/DiscoveryTests/ServiceDiscoveryFacadeTests.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using GettingStarted.Models; -using GettingStarted.ResourceDefinitionExample; using JsonApiDotNetCore.Builders; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Data; @@ -16,6 +14,7 @@ using JsonApiDotNetCore.Serialization; using JsonApiDotNetCore.Serialization.Server.Builders; using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/test/DiscoveryTests/xunit.runner.json b/test/DiscoveryTests/xunit.runner.json new file mode 100644 index 0000000000..9db029ba52 --- /dev/null +++ b/test/DiscoveryTests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} diff --git a/test/IntegrationTests/IntegrationTests.csproj b/test/IntegrationTests/IntegrationTests.csproj index 54ad8b5f98..075038b3d0 100644 --- a/test/IntegrationTests/IntegrationTests.csproj +++ b/test/IntegrationTests/IntegrationTests.csproj @@ -1,11 +1,16 @@ - + $(NetCoreAppVersion) + + + PreserveNewest + + + - diff --git a/test/IntegrationTests/xunit.runner.json b/test/IntegrationTests/xunit.runner.json new file mode 100644 index 0000000000..9db029ba52 --- /dev/null +++ b/test/IntegrationTests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ContentNegotiationTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ContentNegotiationTests.cs index 7c1e20d6d1..9cb092ee2b 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ContentNegotiationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/ContentNegotiationTests.cs @@ -231,8 +231,8 @@ public async Task Respond_200_If_Accept_Headers_Include_Application_Prefix() var route = "/api/v1/todoItems"; var server = new TestServer(builder); var client = server.CreateClient(); - client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("text/html")); - client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/*")); + client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("text/html;q=0.8")); + client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/*;q=0.2")); var request = new HttpRequestMessage(HttpMethod.Get, route); // Act diff --git a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj index df2f7f25b6..574e486669 100644 --- a/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj +++ b/test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj @@ -11,10 +11,10 @@ - + diff --git a/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs b/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs deleted file mode 100644 index cb14880b05..0000000000 --- a/test/NoEntityFrameworkTests/Acceptance/Extensibility/NoEntityFrameworkTests.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using JsonApiDotNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Newtonsoft.Json; -using NoEntityFrameworkExample; -using NoEntityFrameworkExample.Models; -using Xunit; - -namespace NoEntityFrameworkTests.Acceptance.Extensibility -{ - public sealed class NoEntityFrameworkTests : IClassFixture - { - private readonly TestFixture _fixture; - - public NoEntityFrameworkTests(TestFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task Can_Get_TodoItems() - { - // Arrange - _fixture.Context.TodoItems.Add(new TodoItem()); - _fixture.Context.SaveChanges(); - - var client = _fixture.Server.CreateClient(); - - var httpMethod = new HttpMethod("GET"); - var route = "/api/v1/todoItems"; - - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = _fixture.GetDeserializer().DeserializeList(responseBody).Data; - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.NotNull(deserializedBody); - Assert.NotEmpty(deserializedBody); - } - - [Fact] - public async Task Can_Get_TodoItems_By_Id() - { - // Arrange - var todoItem = new TodoItem(); - _fixture.Context.TodoItems.Add(todoItem); - _fixture.Context.SaveChanges(); - - var client = _fixture.Server.CreateClient(); - - var httpMethod = new HttpMethod("GET"); - var route = $"/api/v1/todoItems/{todoItem.Id}"; - - var request = new HttpRequestMessage(httpMethod, route); - - // Act - var response = await client.SendAsync(request); - var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = _fixture.GetDeserializer().DeserializeSingle(responseBody).Data; - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.NotNull(deserializedBody); - Assert.Equal(todoItem.Id, deserializedBody.Id); - } - - [Fact] - public async Task Can_Create_TodoItems() - { - // Arrange - var description = Guid.NewGuid().ToString(); - var httpMethod = new HttpMethod("POST"); - var route = "/api/v1/todoItems/"; - var content = new - { - data = new - { - type = "todoItems", - attributes = new - { - description, - ordinal = 1 - } - } - }; - - var request = new HttpRequestMessage(httpMethod, route) - { - Content = new StringContent(JsonConvert.SerializeObject(content)) - }; - request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); - - var builder = new WebHostBuilder() - .UseStartup(); - var server = new TestServer(builder); - var client = server.CreateClient(); - - // Act - var response = await client.SendAsync(request); - var responseBody = await response.Content.ReadAsStringAsync(); - var deserializedBody = _fixture.GetDeserializer().DeserializeSingle(responseBody).Data; - - // Assert - Assert.Equal(HttpStatusCode.Created, response.StatusCode); - Assert.NotNull(deserializedBody); - Assert.Equal(description, deserializedBody.Description); - } - } -} diff --git a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj index d98c5144eb..f4f39b2be4 100644 --- a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj +++ b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj @@ -11,11 +11,10 @@ - - + diff --git a/test/NoEntityFrameworkTests/TestFixture.cs b/test/NoEntityFrameworkTests/TestFixture.cs deleted file mode 100644 index 19c0be041f..0000000000 --- a/test/NoEntityFrameworkTests/TestFixture.cs +++ /dev/null @@ -1,44 +0,0 @@ -using JsonApiDotNetCore.Builders; -using JsonApiDotNetCore.Serialization.Client; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using NoEntityFrameworkExample.Data; -using NoEntityFrameworkExample.Models; -using System; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Internal; -using Microsoft.Extensions.Logging.Abstractions; -using NoEntityFrameworkExample; - -namespace NoEntityFrameworkTests -{ - public class TestFixture : IDisposable - { - public AppDbContext Context { get; } - public TestServer Server { get; } - private readonly IServiceProvider _services; - public TestFixture() - { - var builder = new WebHostBuilder().UseStartup(); - Server = new TestServer(builder); - Context = (AppDbContext)Server.Services.GetService(typeof(AppDbContext)); - Context.Database.EnsureCreated(); - _services = Server.Host.Services; - } - - public IResponseDeserializer GetDeserializer() - { - var options = GetService(); - - var resourceGraph = new ResourceGraphBuilder(options, NullLoggerFactory.Instance).AddResource("todoItems").Build(); - return new ResponseDeserializer(resourceGraph, new DefaultResourceFactory(_services)); - } - - public T GetService() => (T)_services.GetService(typeof(T)); - - public void Dispose() - { - Server.Dispose(); - } - } -} diff --git a/test/NoEntityFrameworkTests/TestScopedServiceProvider.cs b/test/NoEntityFrameworkTests/TestScopedServiceProvider.cs deleted file mode 100644 index 6d209dc491..0000000000 --- a/test/NoEntityFrameworkTests/TestScopedServiceProvider.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using JsonApiDotNetCore.Services; -using Microsoft.AspNetCore.Http; -using Moq; - -namespace NoEntityFrameworkTests -{ - public sealed class TestScopedServiceProvider : IScopedServiceProvider - { - private readonly IServiceProvider _serviceProvider; - private readonly Mock _httpContextAccessorMock = new Mock(); - - public TestScopedServiceProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public object GetService(Type serviceType) - { - if (serviceType == typeof(IHttpContextAccessor)) - { - return _httpContextAccessorMock.Object; - } - - return _serviceProvider.GetService(serviceType); - } - } -} diff --git a/test/NoEntityFrameworkTests/TestStartup.cs b/test/NoEntityFrameworkTests/TestStartup.cs deleted file mode 100644 index 986706c9db..0000000000 --- a/test/NoEntityFrameworkTests/TestStartup.cs +++ /dev/null @@ -1,20 +0,0 @@ -using JsonApiDotNetCore.Services; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using NoEntityFrameworkExample; - -namespace NoEntityFrameworkTests -{ - public sealed class TestStartup : Startup - { - public TestStartup(IWebHostEnvironment env) : base(env) - { } - - public override void ConfigureServices(IServiceCollection services) - { - base.ConfigureServices(services); - services.AddScoped(); - services.BuildServiceProvider(); - } - } -} diff --git a/test/NoEntityFrameworkTests/WorkItemTests.cs b/test/NoEntityFrameworkTests/WorkItemTests.cs new file mode 100644 index 0000000000..c4f687c383 --- /dev/null +++ b/test/NoEntityFrameworkTests/WorkItemTests.cs @@ -0,0 +1,169 @@ +using JsonApiDotNetCore; +using JsonApiDotNetCore.Models; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using NoEntityFrameworkExample; +using NoEntityFrameworkExample.Data; +using NoEntityFrameworkExample.Models; +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Xunit; + +namespace NoEntityFrameworkTests +{ + public sealed class WorkItemTests : IClassFixture> + { + private readonly WebApplicationFactory _factory; + + public WorkItemTests(WebApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task Can_Get_WorkItems() + { + // Arrange + await ExecuteOnDbContextAsync(async dbContext => + { + dbContext.WorkItems.Add(new WorkItem()); + await dbContext.SaveChangesAsync(); + }); + + var client = _factory.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/workItems"); + + // Act + var response = await client.SendAsync(request); + + // Assert + AssertStatusCode(HttpStatusCode.OK, response); + + string responseBody = await response.Content.ReadAsStringAsync(); + var document = JsonConvert.DeserializeObject(responseBody); + + Assert.NotEmpty(document.ManyData); + } + + [Fact] + public async Task Can_Get_WorkItem_By_Id() + { + // Arrange + var workItem = new WorkItem(); + + await ExecuteOnDbContextAsync(async dbContext => + { + dbContext.WorkItems.Add(workItem); + await dbContext.SaveChangesAsync(); + }); + + var client = _factory.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Get, "/api/v1/workItems/" + workItem.StringId); + + // Act + var response = await client.SendAsync(request); + + // Assert + AssertStatusCode(HttpStatusCode.OK, response); + + string responseBody = await response.Content.ReadAsStringAsync(); + var document = JsonConvert.DeserializeObject(responseBody); + + Assert.NotNull(document.SingleData); + Assert.Equal(workItem.StringId, document.SingleData.Id); + } + + [Fact] + public async Task Can_Create_WorkItem() + { + // Arrange + var title = Guid.NewGuid().ToString(); + + var requestContent = new + { + data = new + { + type = "workItems", + attributes = new + { + title, + ordinal = 1 + } + } + }; + + var requestBody = JsonConvert.SerializeObject(requestContent); + + var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/workItems/") + { + Content = new StringContent(requestBody) + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue(HeaderConstants.MediaType); + + var client = _factory.CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + AssertStatusCode(HttpStatusCode.Created, response); + + string responseBody = await response.Content.ReadAsStringAsync(); + var document = JsonConvert.DeserializeObject(responseBody); + + Assert.NotNull(document.SingleData); + Assert.Equal(title, document.SingleData.Attributes["title"]); + } + + [Fact] + public async Task Can_Delete_WorkItem() + { + // Arrange + var workItem = new WorkItem(); + + await ExecuteOnDbContextAsync(async dbContext => + { + dbContext.WorkItems.Add(workItem); + await dbContext.SaveChangesAsync(); + }); + + var client = _factory.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Delete, "/api/v1/workItems/" + workItem.StringId); + + // Act + var response = await client.SendAsync(request); + + // Assert + AssertStatusCode(HttpStatusCode.NoContent, response); + + string responseBody = await response.Content.ReadAsStringAsync(); + var document = JsonConvert.DeserializeObject(responseBody); + + Assert.Null(document.Data); + } + + private async Task ExecuteOnDbContextAsync(Func asyncAction) + { + using IServiceScope scope = _factory.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + await asyncAction(dbContext); + } + + private static void AssertStatusCode(HttpStatusCode expected, HttpResponseMessage response) + { + if (expected != response.StatusCode) + { + var responseBody = response.Content.ReadAsStringAsync().Result; + Assert.True(false, $"Got {response.StatusCode} status code instead of {expected}. Payload: {responseBody}"); + } + } + } +} diff --git a/test/NoEntityFrameworkTests/xunit.runner.json b/test/NoEntityFrameworkTests/xunit.runner.json new file mode 100644 index 0000000000..9db029ba52 --- /dev/null +++ b/test/NoEntityFrameworkTests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} diff --git a/test/UnitTests/Middleware/CurrentRequestMiddlewareTests.cs b/test/UnitTests/Middleware/JsonApiMiddlewareTests.cs similarity index 99% rename from test/UnitTests/Middleware/CurrentRequestMiddlewareTests.cs rename to test/UnitTests/Middleware/JsonApiMiddlewareTests.cs index 5afd4ec1ca..ba008bb8d6 100644 --- a/test/UnitTests/Middleware/CurrentRequestMiddlewareTests.cs +++ b/test/UnitTests/Middleware/JsonApiMiddlewareTests.cs @@ -15,7 +15,7 @@ namespace UnitTests.Middleware { - public sealed class CurrentRequestMiddlewareTests + public sealed class JsonApiMiddlewareTests { [Fact] public async Task ParseUrlBase_ObfuscatedIdClass_ShouldSetIdCorrectly() diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 6a49628d6a..f9c5e4b14a 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppVersion) @@ -10,7 +10,6 @@ -