Skip to content

Commit dbc1ba8

Browse files
authored
Merge pull request #42 from Research-Institute/missing-spec-tests
[Staging] Missing spec tests
2 parents cce3149 + e490683 commit dbc1ba8

File tree

12 files changed

+215
-15
lines changed

12 files changed

+215
-15
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ dotnet: 1.0.0-preview2-1-003177
1010
branches:
1111
only:
1212
- master
13+
- staging
1314
script:
14-
- ./build.sh
15+
- ./build.sh

appveyor.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pull_requests:
44
branches:
55
only:
66
- master
7+
- staging
78
nuget:
89
disable_publish_on_pr: true
910
build_script:
@@ -19,7 +20,7 @@ deploy:
1920
secure: 6CeYcZ4Ze+57gxfeuHzqP6ldbUkPtF6pfpVM1Gw/K2jExFrAz763gNAQ++tiacq3
2021
skip_symbols: true
2122
on:
22-
branch: master
23+
branch: staging
2324
- provider: NuGet
2425
name: production
2526
api_key:

src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ public Document Build(IIdentifiable entity)
2626
var document = new Document
2727
{
2828
Data = _getData(contextEntity, entity),
29-
Meta = _getMeta(entity)
29+
Meta = _getMeta(entity),
30+
Links = _jsonApiContext.PageManager.GetPageLinks(new LinkBuilder(_jsonApiContext))
3031
};
3132

3233
document.Included = _appendIncludedObject(document.Included, contextEntity, entity);
@@ -45,7 +46,8 @@ public Documents Build(IEnumerable<IIdentifiable> entities)
4546
var documents = new Documents
4647
{
4748
Data = new List<DocumentData>(),
48-
Meta = _getMeta(entities.FirstOrDefault())
49+
Meta = _getMeta(entities.FirstOrDefault()),
50+
Links = _jsonApiContext.PageManager.GetPageLinks(new LinkBuilder(_jsonApiContext))
4951
};
5052

5153
foreach (var entity in entities)

src/JsonApiDotNetCore/Builders/LinkBuilder.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
using JsonApiDotNetCore.Extensions;
32
using JsonApiDotNetCore.Services;
43
using Microsoft.AspNetCore.Http;
@@ -45,5 +44,10 @@ public string GetRelatedRelationLink(string parent, string parentId, string chil
4544
{
4645
return $"{_context.BasePath}/{parent.Dasherize()}/{parentId}/{child.Dasherize()}";
4746
}
47+
48+
public string GetPageLink(int pageOffset, int pageSize)
49+
{
50+
return $"{_context.BasePath}/{_context.RequestEntity.EntityName.Dasherize()}?page[size]={pageSize}&page[number]={pageOffset}";
51+
}
4852
}
4953
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,42 @@
1+
using System;
2+
using JsonApiDotNetCore.Builders;
3+
using JsonApiDotNetCore.Models;
4+
15
namespace JsonApiDotNetCore.Internal
26
{
37
public class PageManager
48
{
59
public int TotalRecords { get; set; }
610
public int PageSize { get; set; }
11+
public int DefaultPageSize { get; set; }
712
public int CurrentPage { get; set; }
813
public bool IsPaginated { get { return PageSize > 0; } }
14+
public int TotalPages {
15+
get { return (TotalRecords == 0) ? -1: (int)Math.Ceiling(decimal.Divide(TotalRecords, PageSize)); }
16+
}
17+
18+
public RootLinks GetPageLinks(LinkBuilder linkBuilder)
19+
{
20+
if(!IsPaginated || (CurrentPage == 1 && TotalPages <= 0))
21+
return null;
22+
23+
var rootLinks = new RootLinks();
24+
25+
var includePageSize = DefaultPageSize != PageSize;
26+
27+
if(CurrentPage > 1)
28+
rootLinks.First = linkBuilder.GetPageLink(1, PageSize);
29+
30+
if(CurrentPage > 1)
31+
rootLinks.Prev = linkBuilder.GetPageLink(CurrentPage - 1, PageSize);
32+
33+
if(CurrentPage < TotalPages)
34+
rootLinks.Next = linkBuilder.GetPageLink(CurrentPage + 1, PageSize);
35+
36+
if(TotalPages > 0)
37+
rootLinks.Last = linkBuilder.GetPageLink(TotalPages, PageSize);
38+
39+
return rootLinks;
40+
}
941
}
1042
}

src/JsonApiDotNetCore/Models/DocumentBase.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ namespace JsonApiDotNetCore.Models
55
{
66
public class DocumentBase
77
{
8+
[JsonProperty("links")]
9+
public RootLinks Links { get; set; }
10+
811
[JsonProperty("included")]
912
public List<DocumentData> Included { get; set; }
1013

@@ -21,5 +24,10 @@ public bool ShouldSerializeMeta()
2124
{
2225
return (Meta != null);
2326
}
27+
28+
public bool ShouldSerializeLinks()
29+
{
30+
return (Links != null);
31+
}
2432
}
2533
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Newtonsoft.Json;
2+
3+
namespace JsonApiDotNetCore.Models
4+
{
5+
public class RootLinks
6+
{
7+
[JsonProperty("self")]
8+
public string Self { get; set; }
9+
10+
[JsonProperty("next")]
11+
public string Next { get; set; }
12+
13+
[JsonProperty("prev")]
14+
public string Prev { get; set; }
15+
16+
[JsonProperty("first")]
17+
public string First { get; set; }
18+
19+
[JsonProperty("last")]
20+
public string Last { get; set; }
21+
22+
// http://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
23+
public bool ShouldSerializeSelf()
24+
{
25+
return (!string.IsNullOrEmpty(Self));
26+
}
27+
28+
public bool ShouldSerializeFirst()
29+
{
30+
return (!string.IsNullOrEmpty(First));
31+
}
32+
33+
public bool ShouldSerializeNext()
34+
{
35+
return (!string.IsNullOrEmpty(Next));
36+
}
37+
38+
public bool ShouldSerializePrev()
39+
{
40+
return (!string.IsNullOrEmpty(Prev));
41+
}
42+
43+
public bool ShouldSerializeLast()
44+
{
45+
return (!string.IsNullOrEmpty(Last));
46+
}
47+
}
48+
}

src/JsonApiDotNetCore/Services/JsonApiContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.Collections.Generic;
32
using System.Linq;
43
using JsonApiDotNetCore.Builders;
@@ -58,6 +57,7 @@ private PageManager GetPageManager()
5857
var query = QuerySet?.PageQuery ?? new PageQuery();
5958

6059
return new PageManager {
60+
DefaultPageSize = Options.DefaultPageSize,
6161
CurrentPage = query.PageOffset > 0 ? query.PageOffset : 1,
6262
PageSize = query.PageSize > 0 ? query.PageSize : Options.DefaultPageSize
6363
};

src/JsonApiDotNetCore/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.2.12",
2+
"version": "1.0.0-beta1-*",
33

44
"dependencies": {
55
"Microsoft.NETCore.App": {

src/JsonApiDotNetCoreExample/Startup.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services)
4444
{
4545
opt.Namespace = "api/v1";
4646
opt.DefaultPageSize = 5;
47+
opt.IncludeTotalRecordCount = true;
4748
});
4849

4950
services.AddDocumentationConfiguration(Config);
@@ -52,6 +53,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services)
5253
var appContext = provider.GetRequiredService<AppDbContext>();
5354
if(appContext == null)
5455
throw new ArgumentException();
56+
5557
return provider;
5658
}
5759

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System.Net;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using DotNetCoreDocs;
5+
using DotNetCoreDocs.Writers;
6+
using JsonApiDotNetCoreExample;
7+
using Microsoft.AspNetCore.Hosting;
8+
using Microsoft.AspNetCore.TestHost;
9+
using Newtonsoft.Json;
10+
using Xunit;
11+
using Person = JsonApiDotNetCoreExample.Models.Person;
12+
using JsonApiDotNetCore.Models;
13+
using JsonApiDotNetCoreExample.Data;
14+
using Bogus;
15+
using JsonApiDotNetCoreExample.Models;
16+
using System.Linq;
17+
using System;
18+
19+
namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec.DocumentTests
20+
{
21+
[Collection("WebHostCollection")]
22+
public class PagingTests
23+
{
24+
private DocsFixture<Startup, JsonDocWriter> _fixture;
25+
private AppDbContext _context;
26+
private Faker<Person> _personFaker;
27+
private Faker<TodoItem> _todoItemFaker;
28+
private Faker<TodoItemCollection> _todoItemCollectionFaker;
29+
30+
public PagingTests(DocsFixture<Startup, JsonDocWriter> fixture)
31+
{
32+
_fixture = fixture;
33+
_context = fixture.GetService<AppDbContext>();
34+
_personFaker = new Faker<Person>()
35+
.RuleFor(p => p.FirstName, f => f.Name.FirstName())
36+
.RuleFor(p => p.LastName, f => f.Name.LastName());
37+
38+
_todoItemFaker = new Faker<TodoItem>()
39+
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
40+
.RuleFor(t => t.Ordinal, f => f.Random.Number());
41+
42+
_todoItemCollectionFaker = new Faker<TodoItemCollection>()
43+
.RuleFor(t => t.Name, f => f.Company.CatchPhrase());
44+
}
45+
46+
[Fact]
47+
public async Task Server_IncludesPagination_Links()
48+
{
49+
// arrange
50+
var pageSize = 5;
51+
const int minimumNumberOfRecords = 11;
52+
_context.TodoItems.RemoveRange(_context.TodoItems);
53+
54+
for(var i=0; i < minimumNumberOfRecords; i++)
55+
_context.TodoItems.Add(_todoItemFaker.Generate());
56+
57+
await _context.SaveChangesAsync();
58+
59+
var numberOfPages = (int)Math.Ceiling(decimal.Divide(minimumNumberOfRecords, pageSize));
60+
var startPageNumber = 2;
61+
62+
var builder = new WebHostBuilder()
63+
.UseStartup<Startup>();
64+
65+
var httpMethod = new HttpMethod("GET");
66+
var route = $"/api/v1/todo-items?page[number]=2";
67+
68+
var server = new TestServer(builder);
69+
var client = server.CreateClient();
70+
var request = new HttpRequestMessage(httpMethod, route);
71+
72+
// act
73+
var response = await client.SendAsync(request);
74+
var documents = JsonConvert.DeserializeObject<Documents>(await response.Content.ReadAsStringAsync());
75+
var links = documents.Links;
76+
77+
// assert
78+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
79+
Assert.NotEmpty(links.First);
80+
Assert.NotEmpty(links.Next);
81+
Assert.NotEmpty(links.Last);
82+
83+
Assert.Equal($"http://localhost/api/v1/todo-items?page[size]={pageSize}&page[number]={startPageNumber+1}", links.Next);
84+
Assert.Equal($"http://localhost/api/v1/todo-items?page[size]={pageSize}&page[number]={startPageNumber-1}", links.Prev);
85+
Assert.Equal($"http://localhost/api/v1/todo-items?page[size]={pageSize}&page[number]={numberOfPages}", links.Last);
86+
Assert.Equal($"http://localhost/api/v1/todo-items?page[size]={pageSize}&page[number]=1", links.First);
87+
}
88+
}
89+
}

test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using JsonApiDotNetCore.Models;
1212
using JsonApiDotNetCoreExample.Data;
1313
using System.Linq;
14+
using Bogus;
15+
using JsonApiDotNetCoreExample.Models;
1416

1517
namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec.DocumentTests
1618
{
@@ -19,10 +21,15 @@ public class Relationships
1921
{
2022
private DocsFixture<Startup, JsonDocWriter> _fixture;
2123
private AppDbContext _context;
24+
private Faker<TodoItem> _todoItemFaker;
25+
2226
public Relationships(DocsFixture<Startup, JsonDocWriter> fixture)
2327
{
2428
_fixture = fixture;
2529
_context = fixture.GetService<AppDbContext>();
30+
_todoItemFaker = new Faker<TodoItem>()
31+
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
32+
.RuleFor(t => t.Ordinal, f => f.Random.Number());
2633
}
2734

2835
[Fact]
@@ -31,18 +38,22 @@ public async Task Correct_RelationshipObjects_For_ManyToOne_Relationships()
3138
// arrange
3239
var builder = new WebHostBuilder()
3340
.UseStartup<Startup>();
41+
42+
var todoItem = _todoItemFaker.Generate();
43+
_context.TodoItems.Add(todoItem);
44+
await _context.SaveChangesAsync();
3445

3546
var httpMethod = new HttpMethod("GET");
36-
var route = $"/api/v1/todo-items";
47+
var route = $"/api/v1/todo-items/{todoItem.Id}";
3748

3849
var server = new TestServer(builder);
3950
var client = server.CreateClient();
4051
var request = new HttpRequestMessage(httpMethod, route);
4152

4253
// act
4354
var response = await client.SendAsync(request);
44-
var documents = JsonConvert.DeserializeObject<Documents>(await response.Content.ReadAsStringAsync());
45-
var data = documents.Data[0];
55+
var document = JsonConvert.DeserializeObject<Document>(await response.Content.ReadAsStringAsync());
56+
var data = document.Data;
4657
var expectedOwnerSelfLink = $"http://localhost/api/v1/todo-items/{data.Id}/relationships/owner";
4758
var expectedOwnerRelatedLink = $"http://localhost/api/v1/todo-items/{data.Id}/owner";
4859

@@ -56,13 +67,15 @@ public async Task Correct_RelationshipObjects_For_ManyToOne_Relationships()
5667
public async Task Correct_RelationshipObjects_For_ManyToOne_Relationships_ById()
5768
{
5869
// arrange
59-
var todoItemId = _context.TodoItems.Last().Id;
60-
6170
var builder = new WebHostBuilder()
6271
.UseStartup<Startup>();
72+
73+
var todoItem = _todoItemFaker.Generate();
74+
_context.TodoItems.Add(todoItem);
75+
await _context.SaveChangesAsync();
6376

6477
var httpMethod = new HttpMethod("GET");
65-
var route = $"/api/v1/todo-items/{todoItemId}";
78+
var route = $"/api/v1/todo-items/{todoItem.Id}";
6679

6780
var server = new TestServer(builder);
6881
var client = server.CreateClient();
@@ -72,8 +85,8 @@ public async Task Correct_RelationshipObjects_For_ManyToOne_Relationships_ById()
7285
var response = await client.SendAsync(request);
7386
var responseString = await response.Content.ReadAsStringAsync();
7487
var data = JsonConvert.DeserializeObject<Document>(responseString).Data;
75-
var expectedOwnerSelfLink = $"http://localhost/api/v1/todo-items/{todoItemId}/relationships/owner";
76-
var expectedOwnerRelatedLink = $"http://localhost/api/v1/todo-items/{todoItemId}/owner";
88+
var expectedOwnerSelfLink = $"http://localhost/api/v1/todo-items/{todoItem.Id}/relationships/owner";
89+
var expectedOwnerRelatedLink = $"http://localhost/api/v1/todo-items/{todoItem.Id}/owner";
7790

7891
// assert
7992
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

0 commit comments

Comments
 (0)