Skip to content
This repository was archived by the owner on Oct 16, 2022. It is now read-only.

Commit 1badaa5

Browse files
authored
Merge pull request #1 from liannoi/dev
Minimum viable product, prototype
2 parents 7d7a60d + 4ae196f commit 1badaa5

File tree

167 files changed

+43372
-43
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

167 files changed

+43372
-43
lines changed

Exam.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exam.Persistence", "Src\Per
2121
EndProject
2222
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exam.Clients.WebApi", "Src\Clients\WebAPI\Exam.Clients.WebApi.csproj", "{9CA5C67C-BE40-479E-9073-13112F7AB732}"
2323
EndProject
24+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exam.Clients.WebUI", "Src\Clients\WebUI\Exam.Clients.WebUI.csproj", "{34335F88-1E4F-493B-B66B-F707E2EC98AD}"
25+
EndProject
2426
Global
2527
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2628
Debug|Any CPU = Debug|Any CPU
@@ -47,6 +49,10 @@ Global
4749
{9CA5C67C-BE40-479E-9073-13112F7AB732}.Debug|Any CPU.Build.0 = Debug|Any CPU
4850
{9CA5C67C-BE40-479E-9073-13112F7AB732}.Release|Any CPU.ActiveCfg = Release|Any CPU
4951
{9CA5C67C-BE40-479E-9073-13112F7AB732}.Release|Any CPU.Build.0 = Release|Any CPU
52+
{34335F88-1E4F-493B-B66B-F707E2EC98AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53+
{34335F88-1E4F-493B-B66B-F707E2EC98AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
54+
{34335F88-1E4F-493B-B66B-F707E2EC98AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
55+
{34335F88-1E4F-493B-B66B-F707E2EC98AD}.Release|Any CPU.Build.0 = Release|Any CPU
5056
EndGlobalSection
5157
GlobalSection(SolutionProperties) = preSolution
5258
HideSolutionNode = FALSE
@@ -59,6 +65,7 @@ Global
5965
{2094A04E-4CA0-4FF3-9E19-FB0A9F88F3B9} = {99E2E88C-67B8-4148-A406-1B4F4A8A98DA}
6066
{498657F6-2A48-4721-AE4D-43814071C472} = {99E2E88C-67B8-4148-A406-1B4F4A8A98DA}
6167
{9CA5C67C-BE40-479E-9073-13112F7AB732} = {5B32103E-EC77-40D2-99CC-5DE9C8299C41}
68+
{34335F88-1E4F-493B-B66B-F707E2EC98AD} = {5B32103E-EC77-40D2-99CC-5DE9C8299C41}
6269
EndGlobalSection
6370
GlobalSection(ExtensibilityGlobals) = postSolution
6471
SolutionGuid = {7E938145-EDBD-4DC8-BCFA-00934E3E571F}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Exam.Application.Storage.Actors;
4+
using Exam.Application.Storage.Actors.Commands.Create;
5+
using Exam.Application.Storage.Actors.Commands.Delete;
6+
using Exam.Application.Storage.Actors.Commands.Update;
7+
using Exam.Application.Storage.Actors.Queries.Get;
8+
using Exam.Application.Storage.Actors.Queries.Get.AsList;
9+
using Exam.Application.Storage.Actors.Queries.Get.AsList.ByFilm;
10+
using FluentValidation;
11+
using Microsoft.AspNetCore.Http;
12+
using Microsoft.AspNetCore.Mvc;
13+
14+
namespace Exam.Clients.WebApi.Controllers
15+
{
16+
public class ActorsController : BaseController
17+
{
18+
[HttpGet]
19+
[ProducesResponseType(StatusCodes.Status200OK)]
20+
[ProducesDefaultResponseType]
21+
public async Task<ActionResult<ActorsListViewModel>> GetAll()
22+
{
23+
return Ok(await Mediator.Send(new GetActorsAsListQuery()));
24+
}
25+
26+
[HttpGet("{id}")]
27+
[ProducesResponseType(StatusCodes.Status200OK)]
28+
[ProducesDefaultResponseType]
29+
public async Task<ActionResult<ActorLookupDto>> Get(int id)
30+
{
31+
return Ok(await Mediator.Send(new GetActorQuery {ActorId = id}));
32+
}
33+
34+
[HttpGet("{id}")]
35+
[ProducesResponseType(StatusCodes.Status200OK)]
36+
[ProducesDefaultResponseType]
37+
public async Task<ActionResult<ActorsListViewModel>> GetAllByFilm(int id)
38+
{
39+
return Ok(await Mediator.Send(new GetActorsByFilmAsListQuery {FilmId = id}));
40+
}
41+
42+
[HttpPost]
43+
[ProducesResponseType(StatusCodes.Status200OK)]
44+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
45+
public async Task<IActionResult> Create(CreateActorCommand command)
46+
{
47+
try
48+
{
49+
return Ok(await Mediator.Send(command));
50+
}
51+
catch (ValidationException e)
52+
{
53+
return BadRequest(e.Message);
54+
}
55+
}
56+
57+
[HttpPost]
58+
[ProducesResponseType(StatusCodes.Status200OK)]
59+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
60+
public async Task<IActionResult> Update(UpdateActorCommand command)
61+
{
62+
try
63+
{
64+
return Ok(await Mediator.Send(command));
65+
}
66+
catch (ValidationException e)
67+
{
68+
return BadRequest(e.Message);
69+
}
70+
catch (ArgumentNullException)
71+
{
72+
return BadRequest("No entities with this primary key were found in the database.");
73+
}
74+
}
75+
76+
[HttpDelete("{id}")]
77+
[ProducesResponseType(StatusCodes.Status200OK)]
78+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
79+
public async Task<IActionResult> Delete(int id)
80+
{
81+
try
82+
{
83+
return Ok(await Mediator.Send(new DeleteActorCommand {ActorId = id}));
84+
}
85+
catch (ValidationException e)
86+
{
87+
return BadRequest(e.Message);
88+
}
89+
catch (ArgumentNullException)
90+
{
91+
return BadRequest("No entities with this primary key were found in the database.");
92+
}
93+
}
94+
}
95+
}

Src/Clients/WebAPI/Controllers/BaseController.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
using MediatR;
2+
using Microsoft.AspNetCore.Cors;
23
using Microsoft.AspNetCore.Mvc;
34
using Microsoft.Extensions.DependencyInjection;
45

56
namespace Exam.Clients.WebApi.Controllers
67
{
78
[ApiController]
89
[Route("api/[controller]/[action]")]
10+
[EnableCors("MyPolicy")]
911
public abstract class BaseController : ControllerBase
1012
{
1113
private IMediator _mediator;

Src/Clients/WebAPI/Controllers/FilmsController.cs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
using System.Threading.Tasks;
1+
using System;
2+
using System.Threading.Tasks;
3+
using Exam.Application.Storage.Films;
4+
using Exam.Application.Storage.Films.Commands.Create;
5+
using Exam.Application.Storage.Films.Commands.Delete;
6+
using Exam.Application.Storage.Films.Commands.Update;
7+
using Exam.Application.Storage.Films.Queries.Get;
28
using Exam.Application.Storage.Films.Queries.Get.AsList;
9+
using Exam.Application.Storage.Films.Queries.Get.AsList.ByActor;
10+
using FluentValidation;
311
using Microsoft.AspNetCore.Http;
412
using Microsoft.AspNetCore.Mvc;
513

@@ -9,9 +17,79 @@ public class FilmsController : BaseController
917
{
1018
[HttpGet]
1119
[ProducesResponseType(StatusCodes.Status200OK)]
20+
[ProducesDefaultResponseType]
1221
public async Task<ActionResult<FilmsListViewModel>> GetAll()
1322
{
1423
return Ok(await Mediator.Send(new GetFilmsAsListQuery()));
1524
}
25+
26+
[HttpGet("{id}")]
27+
[ProducesResponseType(StatusCodes.Status200OK)]
28+
[ProducesDefaultResponseType]
29+
public async Task<ActionResult<FilmLookupDto>> Get(int id)
30+
{
31+
return Ok(await Mediator.Send(new GetFilmQuery {Id = id}));
32+
}
33+
34+
[HttpGet("{id}")]
35+
[ProducesResponseType(StatusCodes.Status200OK)]
36+
[ProducesDefaultResponseType]
37+
public async Task<ActionResult<FilmsListViewModel>> GetAllByActor(int id)
38+
{
39+
return Ok(await Mediator.Send(new GetFilmsByActorAsListQuery {ActorId = id}));
40+
}
41+
42+
[HttpPost]
43+
[ProducesResponseType(StatusCodes.Status200OK)]
44+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
45+
public async Task<IActionResult> Create(CreateFilmCommand command)
46+
{
47+
try
48+
{
49+
return Ok(await Mediator.Send(command));
50+
}
51+
catch (ValidationException e)
52+
{
53+
return BadRequest(e.Message);
54+
}
55+
}
56+
57+
[HttpPost]
58+
[ProducesResponseType(StatusCodes.Status200OK)]
59+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
60+
public async Task<IActionResult> Update(UpdateFilmCommand command)
61+
{
62+
try
63+
{
64+
return Ok(await Mediator.Send(command));
65+
}
66+
catch (ValidationException e)
67+
{
68+
return BadRequest(e.Message);
69+
}
70+
catch (ArgumentNullException)
71+
{
72+
return BadRequest("No entities with this primary key were found in the database.");
73+
}
74+
}
75+
76+
[HttpDelete("{id}")]
77+
[ProducesResponseType(StatusCodes.Status200OK)]
78+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
79+
public async Task<IActionResult> Delete(int id)
80+
{
81+
try
82+
{
83+
return Ok(await Mediator.Send(new DeleteFilmCommand {FilmId = id}));
84+
}
85+
catch (ValidationException e)
86+
{
87+
return BadRequest(e.Message);
88+
}
89+
catch (ArgumentNullException)
90+
{
91+
return BadRequest("No entities with this primary key were found in the database.");
92+
}
93+
}
1694
}
1795
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Threading.Tasks;
2+
using Exam.Application.Storage.Voting;
3+
using Exam.Application.Storage.Voting.Queries.Get;
4+
using Exam.Application.Storage.Voting.Queries.Get.AsList;
5+
using Exam.Application.Storage.VotingPolles.Commands.Create;
6+
using Exam.Application.Storage.VotingPolles.Queries.Get;
7+
using Exam.Application.Storage.VotingPolles.Queries.Get.AsList;
8+
using FluentValidation;
9+
using Microsoft.AspNetCore.Http;
10+
using Microsoft.AspNetCore.Mvc;
11+
12+
namespace Exam.Clients.WebApi.Controllers
13+
{
14+
public class VotingController : BaseController
15+
{
16+
[HttpGet("{id}")]
17+
[ProducesResponseType(StatusCodes.Status200OK)]
18+
[ProducesDefaultResponseType]
19+
public async Task<ActionResult<VotingLookupDto>> Get(int id)
20+
{
21+
return Ok(await Mediator.Send(new GetVotingQuery {VotingId = id}));
22+
}
23+
24+
[HttpGet("{id}")]
25+
[ProducesResponseType(StatusCodes.Status200OK)]
26+
[ProducesDefaultResponseType]
27+
public async Task<ActionResult<VotingAnswersListViewModel>> GetAnswersByVoting(int id)
28+
{
29+
return Ok(await Mediator.Send(new GetVotingAnswersAsListQuery {VotingId = id}));
30+
}
31+
32+
[HttpGet("{id}")]
33+
[ProducesResponseType(StatusCodes.Status200OK)]
34+
[ProducesDefaultResponseType]
35+
public async Task<ActionResult<VotingPollesListViewModel>> GetVotingPolles(int id)
36+
{
37+
return Ok(await Mediator.Send(new GetVotingPollesCountQuery {VotingId = id}));
38+
}
39+
40+
[HttpPost]
41+
[ProducesResponseType(StatusCodes.Status200OK)]
42+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
43+
public async Task<IActionResult> CreateVotingPolle(CreateVotingPolleCommand command)
44+
{
45+
try
46+
{
47+
return Ok(await Mediator.Send(command));
48+
}
49+
catch (ValidationException e)
50+
{
51+
return BadRequest(e.Message);
52+
}
53+
}
54+
}
55+
}

Src/Clients/WebAPI/Startup.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ public Startup(IConfiguration configuration, IWebHostEnvironment environment)
2727
// This method gets called by the runtime. Use this method to add services to the container.
2828
public void ConfigureServices(IServiceCollection services)
2929
{
30+
// TODO: Policy name to local Consts.cs.
31+
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
32+
{
33+
builder.AllowAnyOrigin()
34+
.AllowAnyMethod()
35+
.AllowAnyHeader();
36+
}));
37+
3038
services.AddInfrastructure();
3139
services.AddPersistence(Configuration);
3240
services.AddApplication();
@@ -49,17 +57,14 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
4957
app.UseDeveloperExceptionPage();
5058
app.UseDatabaseErrorPage();
5159
}
52-
else
53-
{
54-
app.UseHsts();
55-
}
5660

5761
app.UseHealthChecks("/health");
5862
app.UseHttpsRedirection();
5963

6064
app.UseRouting();
6165

62-
app.UseAuthorization();
66+
// TODO: Policy name to local Consts.cs.
67+
app.UseCors("MyPolicy");
6368

6469
app.UseEndpoints(endpoints => endpoints.MapControllers());
6570
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Net.Http.Headers;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Newtonsoft.Json;
7+
8+
namespace Exam.Clients.WebUI.Common.Helpers.ApiTools
9+
{
10+
public class ApiTools : IApiTools
11+
{
12+
public async Task<TEntity> FetchAsync<TEntity>(string uri) where TEntity : class, new()
13+
{
14+
if (uri == null) throw new ArgumentNullException(nameof(uri));
15+
16+
return await ContinueWithDeserializeAsync<TEntity>(new HttpClient().GetAsync(uri));
17+
}
18+
19+
public async Task<TEntity> PostAsync<TEntity>(string uri, ByteArrayContent content) where TEntity : class, new()
20+
{
21+
if (uri == null) throw new ArgumentNullException(nameof(uri));
22+
if (content == null) throw new ArgumentNullException(nameof(content));
23+
24+
return await ContinueWithDeserializeAsync<TEntity>(new HttpClient().PostAsync(uri, content));
25+
}
26+
27+
public async Task<TEntity> PostAsync<TEntity>(string uri, object value) where TEntity : class, new()
28+
{
29+
if (uri == null) throw new ArgumentNullException(nameof(uri));
30+
if (value == null) throw new ArgumentNullException(nameof(value));
31+
32+
var byteContent = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)));
33+
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
34+
35+
return await ContinueWithDeserializeAsync<TEntity>(new HttpClient().PostAsync(uri, byteContent));
36+
}
37+
38+
public async Task DeleteAsync(string uri)
39+
{
40+
if (uri == null) throw new ArgumentNullException(nameof(uri));
41+
42+
await new HttpClient().DeleteAsync(uri);
43+
}
44+
45+
#region Helpers
46+
47+
// ReSharper disable once MemberCanBePrivate.Global
48+
// ReSharper disable once MemberCanBeMadeStatic.Global
49+
protected async Task<TEntity> ContinueWithDeserializeAsync<TEntity>(Task<HttpResponseMessage> task)
50+
where TEntity : class, new()
51+
{
52+
TEntity result = null;
53+
54+
await task.ContinueWith(async taskResponse =>
55+
result = JsonConvert.DeserializeObject<TEntity>(await (await taskResponse).Content
56+
.ReadAsStringAsync()));
57+
58+
return result;
59+
}
60+
61+
#endregion
62+
}
63+
}

0 commit comments

Comments
 (0)