Skip to content

Commit 1ba66f7

Browse files
authored
Merge pull request #149 from cosmin-ciuc/fix_148
Fix for ResourceConverter class.
2 parents ec45be6 + 7ea66ed commit 1ba66f7

39 files changed

Lines changed: 864 additions & 278 deletions

Readme.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
Release Notes
22

3+
Version 2.3.1
4+
- Fixed the concurrency bug occurring when simultaneous http requests need to serialize large sets of data
5+
- Upgraded demo web app to ASP.NET Core 2.1 and Entity Framework Core 2.1
6+
- Demo app can't run with a database created by the previous demo app. An existing database needs to be removed first.
7+
38
Version 2.3.0.1
49
- Fix bug introduced in 2.3.0 of non-array embedded Representation getting serialized improperly as array
510
- Serialize link Title

WebApi.Hal.Tests/HalResourceMixedContentTest.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Threading.Tasks;
77
using Assent;
88
using Microsoft.AspNetCore.Http;
9+
using Microsoft.AspNetCore.Mvc;
910
using Microsoft.AspNetCore.Mvc.Internal;
1011
using Microsoft.AspNetCore.Mvc.ModelBinding;
1112
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
@@ -80,7 +81,8 @@ public async Task peopledetail_post_json_props_test()
8081
NullLogger.Instance,
8182
new JsonSerializerSettings { Formatting = Formatting.Indented },
8283
ArrayPool<char>.Shared,
83-
new DefaultObjectPoolProvider());
84+
new DefaultObjectPoolProvider(),
85+
new MvcOptions(), new MvcJsonOptions());
8486

8587
var type = typeof(OrganisationWithPeopleDetailRepresentation);
8688
const string json = @"
@@ -122,7 +124,9 @@ public async Task peopledetail_post_json_links_test()
122124
NullLogger.Instance,
123125
new JsonSerializerSettings { Formatting = Formatting.Indented },
124126
ArrayPool<char>.Shared,
125-
new DefaultObjectPoolProvider());
127+
new DefaultObjectPoolProvider(),
128+
new MvcOptions(),
129+
new MvcJsonOptions());
126130

127131
var type = typeof(OrganisationWithPeopleRepresentation);
128132
const string json = @"
@@ -182,7 +186,9 @@ public async Task peopledetail_post_json_embedded_singles_test()
182186
NullLogger.Instance,
183187
new JsonSerializerSettings { Formatting = Formatting.Indented },
184188
ArrayPool<char>.Shared,
185-
new DefaultObjectPoolProvider());
189+
new DefaultObjectPoolProvider(),
190+
new MvcOptions(),
191+
new MvcJsonOptions());
186192

187193
var type = typeof(OrganisationWithPeopleDetailRepresentation);
188194
const string json = @"
@@ -231,7 +237,9 @@ public async Task peopledetail_post_json_embedded_arrays_test()
231237
NullLogger.Instance,
232238
new JsonSerializerSettings { Formatting = Formatting.Indented },
233239
ArrayPool<char>.Shared,
234-
new DefaultObjectPoolProvider());
240+
new DefaultObjectPoolProvider(),
241+
new MvcOptions(),
242+
new MvcJsonOptions());
235243

236244
var type = typeof(OrganisationWithPeopleDetailRepresentation);
237245
const string json = @"
@@ -284,7 +292,9 @@ public async Task peopledetail_post_json_embedded_null_test()
284292
NullLogger.Instance,
285293
new JsonSerializerSettings { Formatting = Formatting.Indented },
286294
ArrayPool<char>.Shared,
287-
new DefaultObjectPoolProvider());
295+
new DefaultObjectPoolProvider(),
296+
new MvcOptions(),
297+
new MvcJsonOptions());
288298

289299
var type = typeof(OrganisationWithPeopleDetailRepresentation);
290300
const string json = @"
@@ -335,7 +345,9 @@ public async Task simplelist_post_json_test()
335345
NullLogger.Instance,
336346
new JsonSerializerSettings { Formatting = Formatting.Indented },
337347
ArrayPool<char>.Shared,
338-
new DefaultObjectPoolProvider());
348+
new DefaultObjectPoolProvider(),
349+
new MvcOptions(),
350+
new MvcJsonOptions());
339351

340352
var type = typeof(MySimpleList);
341353
const string json = @"

WebApi.Hal.Tests/WebApi.Hal.Tests.csproj

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="Assent" Version="1.1.0" />
15-
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
16-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20171025-02" />
17-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
18-
<PackageReference Include="xunit" Version="2.3.1" />
19-
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
14+
<PackageReference Include="Assent" Version="1.3.0" />
15+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
16+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
17+
<PackageReference Include="xunit" Version="2.4.0" />
18+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0">
19+
<PrivateAssets>all</PrivateAssets>
20+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
21+
</PackageReference>
2022
</ItemGroup>
2123

2224
<ItemGroup>

WebApi.Hal.Web/Api/BeerController.cs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
using System;
22
using System.Linq;
3+
using System.Net;
34
using Microsoft.AspNetCore.Mvc;
45
using Microsoft.EntityFrameworkCore;
56
using WebApi.Hal.Web.Api.Resources;
67
using WebApi.Hal.Web.Data;
7-
using WebApi.Hal.Web.Models;
88

99
namespace WebApi.Hal.Web.Api
1010
{
1111
[Route("[controller]")]
12-
public class BeerController : Controller
12+
[ApiController]
13+
[Produces("application/hal+json")]
14+
public class BeerController : ControllerBase
1315
{
1416
readonly IBeerDbContext beerDbContext;
1517

@@ -18,9 +20,10 @@ public BeerController(IBeerDbContext beerDbContext)
1820
this.beerDbContext = beerDbContext;
1921
}
2022

21-
[HttpGet("{id}")]
2223
// GET beer/5
23-
public BeerRepresentation Get(int id)
24+
[HttpGet("{id}")]
25+
[ProducesResponseType(typeof(BeerRepresentation), (int)HttpStatusCode.OK)]
26+
public ActionResult<BeerRepresentation> Get(int id)
2427
{
2528
var beer = beerDbContext.Beers
2629
.Include("Brewery") // lazy loading isn't on for this query; force loading
@@ -31,23 +34,25 @@ public BeerRepresentation Get(int id)
3134
{
3235
Id = beer.Id,
3336
Name = beer.Name,
34-
BreweryId = beer.Brewery == null ? (int?)null : beer.Brewery.Id,
35-
BreweryName = beer.Brewery == null ? null : beer.Brewery.Name,
36-
StyleId = beer.Style == null ? (int?)null : beer.Style.Id,
37-
StyleName = beer.Style == null ? null : beer.Style.Name,
37+
BreweryId = beer.Brewery?.Id,
38+
BreweryName = beer.Brewery?.Name,
39+
StyleId = beer.Style?.Id,
40+
StyleName = beer.Style?.Name,
3841
ReviewIds = beerDbContext.Reviews.Where(r => r.Beer_Id == id).Select(r => r.Id).ToList()
3942
};
4043
}
4144

42-
[HttpPut("{id}")]
4345
// PUT beer/5 with a hal representation in the body as json. Be sure to set content-type: application/hal+json (and accept: application/hal+json for the response)
46+
[HttpPut("{id}")]
47+
[ProducesResponseType((int)HttpStatusCode.OK)]
4448
public void Put(int id, [FromBody] BeerRepresentation value)
4549
{
46-
Console.WriteLine(string.Format("new beer would be updated if repostory supported it! {0}, {1}", value.Id, value.Name));
50+
Console.WriteLine($"new beer would be updated if repostory supported it! {value.Id}, {value.Name}");
4751
}
4852

49-
[HttpDelete]
5053
// DELETE beer?id=1
54+
[HttpDelete]
55+
[ProducesResponseType((int)HttpStatusCode.OK)]
5156
public void Delete(int id)
5257
{
5358
}

WebApi.Hal.Web/Api/BeerDetailController.cs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
4+
using System.Net;
5+
using System.Threading.Tasks;
36
using Microsoft.AspNetCore.Mvc;
47
using Microsoft.EntityFrameworkCore;
58
using WebApi.Hal.Web.Api.Resources;
@@ -8,6 +11,8 @@
811
namespace WebApi.Hal.Web.Api
912
{
1013
[Route("[controller]")]
14+
[ApiController]
15+
[Produces("application/hal+json")]
1116
public class BeerDetailController : Controller
1217
{
1318
readonly IBeerDbContext beerDbContext;
@@ -17,13 +22,14 @@ public BeerDetailController(IBeerDbContext beerDbContext)
1722
this.beerDbContext = beerDbContext;
1823
}
1924

20-
[HttpGet("{id}")]
2125
// GET beerdetail/5
22-
public BeerDetailRepresentation Get(int id)
26+
[HttpGet("{id}")]
27+
[ProducesResponseType(typeof(BeerDetailRepresentation), (int)HttpStatusCode.OK)]
28+
public ActionResult<BeerDetailRepresentation> Get(int id)
2329
{
2430
var beer = beerDbContext.Beers
25-
.Include("Brewery") // lazy loading isn't on for this query; force loading
26-
.Include("BeerStyle")
31+
.Include(b => b.Brewery) // lazy loading isn't on for this query; force loading
32+
.Include(b => b.Style)
2733
.Single(br => br.Id == id);
2834

2935
var reviews = beerDbContext.Reviews
@@ -56,13 +62,55 @@ public BeerDetailRepresentation Get(int id)
5662
return detail;
5763
}
5864

59-
[HttpPut("{id}")]
6065
// PUT beerdetail/5
66+
[HttpPut("{id}")]
67+
[ProducesResponseType((int)HttpStatusCode.OK)]
6168
public void Put(int id, BeerDetailRepresentation beer)
6269
{
6370
// this is here just to see how the deserializer is working
6471
// we should get the links and all the embedded objects deserialized
6572
// we'd be better off creating a client to test the full deserializing, but this way is cheap for now
6673
}
74+
75+
[HttpGet("largeset")]
76+
[ProducesResponseType(typeof(BeerDetailListRepresentation), (int)HttpStatusCode.OK)]
77+
public async Task<ActionResult<BeerDetailListRepresentation>> GetLargeSet(int setSize = 500)
78+
{
79+
var random = new Random();
80+
var largeSet = new BeerDetailRepresentation[setSize];
81+
Parallel.For(0, setSize, index =>
82+
{
83+
largeSet[index] = new BeerDetailRepresentation
84+
{
85+
Id = index + 1,
86+
Name = $"Test beer name {Guid.NewGuid()}",
87+
Reviews = new List<ReviewRepresentation>(),
88+
Style = new BeerStyleRepresentation
89+
{
90+
Id = random.Next(1, 50),
91+
Name = $"Test beer style name {Guid.NewGuid()}"
92+
},
93+
Brewery = new BreweryRepresentation
94+
{
95+
Id = random.Next(1, 10),
96+
Name = $"Test brewery name {Guid.NewGuid()}"
97+
}
98+
};
99+
var numberOfReviews = random.Next(2, 20);
100+
for (var reviewIndex = 0; reviewIndex < numberOfReviews; reviewIndex++)
101+
{
102+
largeSet[index].Reviews.Add(new ReviewRepresentation
103+
{
104+
Id = random.Next(setSize * 10, setSize * 100) + largeSet[index].Id,
105+
Content = $"Test beer review content {Guid.NewGuid()}",
106+
Title = $"Test beer review title {Guid.NewGuid()}",
107+
Beer_Id = largeSet[index].Id
108+
});
109+
}
110+
});
111+
112+
await Task.CompletedTask;
113+
return new BeerDetailListRepresentation(largeSet, largeSet.Length, 1, 1, LinkTemplates.BeerDetails.GetBeerDetail);
114+
}
67115
}
68116
}

WebApi.Hal.Web/Api/BeersController.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Linq;
2+
using System.Net;
23
using Microsoft.AspNetCore.Mvc;
34
using WebApi.Hal.Web.Api.Resources;
45
using WebApi.Hal.Web.Data;
@@ -8,7 +9,9 @@
89
namespace WebApi.Hal.Web.Api
910
{
1011
[Route("[controller]")]
11-
public class BeersController : Controller
12+
[ApiController]
13+
[Produces("application/hal+json")]
14+
public class BeersController : ControllerBase
1215
{
1316
public const int PageSize = 5;
1417

@@ -19,9 +22,10 @@ public BeersController(IRepository repository)
1922
this.repository = repository;
2023
}
2124

22-
[HttpGet]
2325
// GET beers
24-
public BeerListRepresentation Get(int page = 1)
26+
[HttpGet]
27+
[ProducesResponseType(typeof(BeerListRepresentation), (int)HttpStatusCode.OK)]
28+
public ActionResult<BeerListRepresentation> Get(int page = 1)
2529
{
2630
var beers = repository.Find(new GetBeersQuery(), page, PageSize);
2731

@@ -30,9 +34,10 @@ public BeerListRepresentation Get(int page = 1)
3034
return resourceList;
3135
}
3236

33-
[HttpGet("Search")]
3437
// GET beers/Search?searchTerm=Roger
35-
public BeerListRepresentation Search(string searchTerm, int page = 1)
38+
[HttpGet("Search")]
39+
[ProducesResponseType(typeof(BeerListRepresentation), (int)HttpStatusCode.OK)]
40+
public ActionResult<BeerListRepresentation> Search(string searchTerm, int page = 1)
3641
{
3742
var beers = repository.Find(new GetBeersQuery(b => b.Name.Contains(searchTerm)), page, PageSize);
3843

@@ -54,11 +59,13 @@ public BeerListRepresentation Search(string searchTerm, int page = 1)
5459
return beersResource;
5560
}
5661

57-
[HttpPost]
5862
// POST beers
63+
[HttpPost]
64+
[ProducesResponseType(typeof(Beer), (int)HttpStatusCode.Created)]
5965
public IActionResult Post([FromBody] BeerRepresentation value)
6066
{
61-
var newBeer = new Beer(value.Name) { Style_Id = value.StyleId.Value, Brewery_Id = value.BreweryId.Value };
67+
var newBeer = new Beer
68+
{Name = value.Name, Style_Id = value.StyleId.Value, Brewery_Id = value.BreweryId.Value};
6269
repository.Add(newBeer);
6370

6471
return Created(LinkTemplates.Beers.Beer.CreateUri(new {id = newBeer.Id}), newBeer);

WebApi.Hal.Web/Api/BeersFromBreweryController.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Linq;
2+
using System.Net;
23
using Microsoft.AspNetCore.Mvc;
34
using WebApi.Hal.Web.Api.Resources;
45
using WebApi.Hal.Web.Data;
@@ -7,7 +8,9 @@
78
namespace WebApi.Hal.Web.Api
89
{
910
[Route("[controller]")]
10-
public class BeersFromBreweryController : Controller
11+
[ApiController]
12+
[Produces("application/hal+json")]
13+
public class BeersFromBreweryController : ControllerBase
1114
{
1215
readonly IRepository repository;
1316

@@ -16,9 +19,10 @@ public BeersFromBreweryController(IRepository repository)
1619
this.repository = repository;
1720
}
1821

19-
[HttpGet("{id}")]
2022
// GET BeersFromBrewery/5
21-
public BeerListRepresentation Get(int id, int page = 1)
23+
[HttpGet("{id}")]
24+
[ProducesResponseType(typeof(BeerListRepresentation), (int)HttpStatusCode.OK)]
25+
public ActionResult<BeerListRepresentation> Get([FromRoute]int id, int page = 1)
2226
{
2327
var beers = repository.Find(new GetBeersQuery(b => b.Brewery.Id == id), page, BeersController.PageSize);
2428
return new BeerListRepresentation(beers.ToList(), beers.TotalResults, beers.TotalPages, page, LinkTemplates.Breweries.AssociatedBeers, new {id});

WebApi.Hal.Web/Api/BeersFromStyleController.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Linq;
2+
using System.Net;
23
using Microsoft.AspNetCore.Mvc;
34
using WebApi.Hal.Web.Api.Resources;
45
using WebApi.Hal.Web.Data;
@@ -7,7 +8,9 @@
78
namespace WebApi.Hal.Web.Api
89
{
910
[Route("[controller]")]
10-
public class BeersFromStyleController : Controller
11+
[ApiController]
12+
[Produces("application/hal+json")]
13+
public class BeersFromStyleController : ControllerBase
1114
{
1215
readonly IRepository repository;
1316

@@ -16,9 +19,10 @@ public BeersFromStyleController(IRepository repository)
1619
this.repository = repository;
1720
}
1821

19-
[HttpGet("{id}")]
2022
// GET BeersFromStyle/5
21-
public BeerListRepresentation Get(int id, int page = 1)
23+
[HttpGet("{id}")]
24+
[ProducesResponseType(typeof(BeerListRepresentation), (int)HttpStatusCode.OK)]
25+
public ActionResult<BeerListRepresentation> Get(int id, int page = 1)
2226
{
2327
var beers = repository.Find(new GetBeersQuery(b => b.Style.Id == id), page, BeersController.PageSize);
2428
var resourceList = new BeerListRepresentation(

0 commit comments

Comments
 (0)