Skip to content

Commit

Permalink
Merge pull request #425 from DigitalExcellence/develop
Browse files Browse the repository at this point in the history
Release v.1.2.0-beta - 15-04-2021
  • Loading branch information
niraymak authored Apr 15, 2021
2 parents 8e1191d + e3b8d11 commit 0e04071
Show file tree
Hide file tree
Showing 157 changed files with 452,349 additions and 9,563 deletions.
25 changes: 25 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
9 changes: 6 additions & 3 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,18 @@ jobs:
- name: Docker Compose
run: docker-compose up --build -d

- name: Sleep for 30 seconds
- name: Sleep for 90 seconds
uses: juliangruber/sleep-action@v1
with:
time: 30s
time: 90s

- name: Setup node
uses: actions/setup-node@v1
with:
node-version: '10.15.3'

- name: Postman tests
run: npm install -g newman && newman run Postman/dex.postman_collection.json -e Postman/local.postman_environment.json --insecure --delay-request=200 -n 3
run: npm install -g newman && newman run Postman/dex.postman_collection.json -e Postman/local.postman_environment.json --insecure --reporter-cli-no-console --reporter-cli-no-success-assertions -n 3

- name: Recommendation tests
run: npm install -g newman && newman run Postman/ElasticSearch/DeX_Elastic.postman_collection.json -e Postman/ElasticSearch/DeX_Elastic.postman_environment.json --insecure --reporter-cli-no-console --reporter-cli-no-success-assertions -n 3
7 changes: 3 additions & 4 deletions API/01_API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<DocumentationFile>.\API.xml</DocumentationFile>
<Company>Digital Excellence Fontys</Company>
<LangVersion>8</LangVersion>
<Version>1.1.0-beta</Version>
<Version>1.2.0-beta</Version>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -41,18 +41,17 @@
<PackageReference Include="Microsoft.OpenApi" Version="1.1.4" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.1" />
<PackageReference Include="NetEscapades.Configuration.Validation" Version="2.0.0" />
<PackageReference Include="RestSharp" Version="106.11.2" />
<PackageReference Include="RestSharp" Version="106.11.4" />
<PackageReference Include="Sentry.AspNetCore" Version="2.1.1" />
<PackageReference Include="Sentry.Serilog" Version="2.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Data\06_Data.csproj" />
<ProjectReference Include="..\MessageBrokerPublisher\09_MessageBrokerPublisher.csproj" />
<ProjectReference Include="..\Models\07_Models.csproj" />
<ProjectReference Include="..\NotificationSystem\11_NotificationSystem.csproj" />
<ProjectReference Include="..\Services\02_Services.csproj" />
</ItemGroup>

Expand Down
40 changes: 25 additions & 15 deletions API/Configuration/MappingProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Models;
using Services.ExternalDataProviders;
using Services.ExternalDataProviders.Resources;
using System.Collections.Generic;

namespace API.Configuration
{
Expand Down Expand Up @@ -83,7 +84,8 @@ public MappingProfile()

CreateMap<User, LimitedUserResourceResult>();

CreateMap<ProjectResource, Project>();
CreateMap<ProjectResource, Project>()
.ForMember(q => q.Categories, opt => opt.Ignore());
CreateMap<Project, ProjectResourceResult>();
CreateMap<ProjectLike, ProjectLikesResourceResult>();
CreateMap<Project, ProjectHighlightResourceResult>();
Expand All @@ -103,6 +105,13 @@ public MappingProfile()
CreateMap<RoleResource, Role>();
CreateMap<Role, RoleResourceResult>();

CreateMap<CategoryResource, Category>();
CreateMap<Category, CategoryResourceResult>();
CreateMap<ProjectCategoryResource, ProjectCategory>();
CreateMap<ProjectCategory, ProjectCategoryResourceResult>()
.ForMember(q => q.Id, opt => opt.MapFrom(q=> q.Category.Id))
.ForMember(q => q.Name, opt => opt.MapFrom(q => q.Category.Name));

CreateMap<EmbeddedProjectResource, EmbeddedProject>();
CreateMap<EmbeddedProject, EmbeddedProjectResourceResult>();

Expand Down Expand Up @@ -142,20 +151,6 @@ public MappingProfile()

CreateMap<OauthTokens, OauthTokensResourceResult>();

CreateExternalSourceMappingProfiles();
}

private void CreateExternalSourceMappingProfiles()
{
CreateMap<JsFiddleDataSourceResourceResult, Project>()
.ForMember(d => d.Name, opt => opt.MapFrom(m => m.Title));

CreateMap<GithubDataSourceResourceResult, Project>()
.ForMember(dest => dest.ShortDescription, opt => opt.MapFrom(src => src.Description));

CreateMap<GitlabDataSourceResourceResult, Project>()
.ForMember(dest => dest.ShortDescription, opt => opt.MapFrom(src => src.Description));

CreateMap<CallToActionResource, CallToAction>()
.ForMember(dest => dest.OptionValue, opt => opt.MapFrom(src => src.OptionValue.ToLower()));
CreateMap<CallToAction, CallToActionResourceResult>();
Expand All @@ -177,6 +172,21 @@ private void CreateExternalSourceMappingProfiles()
.ForMember(dest => dest.InstititutionName, opt => opt.MapFrom(src => src.Institution.Name))
.ForMember(dest => dest.ProjectName, opt => opt.MapFrom(src => src.Project.Name));


CreateExternalSourceMappingProfiles();
}

private void CreateExternalSourceMappingProfiles()
{
CreateMap<JsFiddleDataSourceResourceResult, Project>()
.ForMember(d => d.Name, opt => opt.MapFrom(m => m.Title));

CreateMap<GithubDataSourceResourceResult, Project>()
.ForMember(dest => dest.ShortDescription, opt => opt.MapFrom(src => src.Description));

CreateMap<GitlabDataSourceResourceResult, Project>()
.ForMember(dest => dest.ShortDescription, opt => opt.MapFrom(src => src.Description));

}

}
Expand Down
233 changes: 233 additions & 0 deletions API/Controllers/CategoryController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
using API.Resources;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Models;
using Serilog;
using Services.Services;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using static Models.Defaults.Defaults;

namespace API.Controllers
{

/// <summary>
/// This class is responsible for handling HTTP requests that are related
/// to categories, for example creating, retrieving, updating or deleting.
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class CategoryController : ControllerBase
{

private readonly IMapper mapper;
private readonly ICategoryService categoryService;
private readonly IProjectCategoryService projectCategoryService;

/// <summary>
/// Initializes a new instance of the <see cref="CategoryController" /> class
/// </summary>
/// <param name="categoryService">The category service which is used to communicate with the logic layer.</param>
/// <param name="projectCategoryService">The project category service which is used to communicate with the logic layer.</param>
/// <param name="mapper">The mapper which is used to convert the resources to the model to the resource result.</param>
public CategoryController(ICategoryService categoryService, IProjectCategoryService projectCategoryService, IMapper mapper)
{
this.categoryService = categoryService;
this.projectCategoryService = projectCategoryService;
this.mapper = mapper;
}

/// <summary>
/// This method is responsible for retrieving all categories.
/// </summary>
/// <returns>This method returns a list of category resource results.</returns>
/// <response code="200">This endpoint returns a list of categories.</response>
[HttpGet]
[Authorize(Policy = nameof(Scopes.CategoryRead))]
[ProducesResponseType(typeof(IEnumerable<CategoryResourceResult>), (int) HttpStatusCode.OK)]
public async Task<IActionResult> GetAllCategories()
{
IEnumerable<Category> categories = await categoryService.GetAll()
.ConfigureAwait(false);

return Ok(mapper.Map<IEnumerable<Category>, IEnumerable<CategoryResourceResult>>(categories));
}

/// <summary>
/// This method is responsible for retrieving a single category.
/// </summary>
/// <returns>This method returns the category resource result.</returns>
/// <response code="200">This endpoint returns the category with the specified id.</response>
/// <response code="400">The 400 Bad Request status code is returned when the specified category id is invalid.</response>
/// <response code="404">The 404 Not Found status code is returned when no category is found with the specified category id.</response>
[HttpGet("{categoryId}")]
[Authorize(Policy = nameof(Scopes.CategoryRead))]
[ProducesResponseType(typeof(CategoryResourceResult), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(ProblemDetails), (int) HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), (int) HttpStatusCode.NotFound)]
public async Task<IActionResult> GetCategory(int categoryId)
{
if(categoryId < 0)
{
ProblemDetails problem = new ProblemDetails
{
Title = "Failed getting category.",
Detail =
"The Id is smaller then 0 and therefore it could never be a valid category id.",
Instance = "758F4B36-A047-42D4-9F9E-B09BF8106F85"
};
return BadRequest(problem);
}

Category category = await categoryService.FindAsync(categoryId)
.ConfigureAwait(false);
if(category == null)
{
ProblemDetails problem = new ProblemDetails
{
Title = "Failed getting category.",
Detail = "The category could not be found in the database.",
Instance = "872DEE7C-D1C8-4161-B8BA-B577EAA5A1C9"
};
return NotFound(problem);
}

return Ok(mapper.Map<Category, CategoryResourceResult>(category));
}

/// <summary>
/// This method is responsible for creating the category.
/// </summary>
/// <param name="categoryResource">The category resource which is used to create a category.</param>
/// <returns>This method returns the created category resource result.</returns>
/// <response code="201">This endpoint returns the created category.</response>
/// <response code="400">The 400 Bad Request status code is returned when unable to create category.</response>
[HttpPost]
[Authorize(Policy = nameof(Scopes.CategoryWrite))]
[ProducesResponseType(typeof(CategoryResourceResult), (int) HttpStatusCode.Created)]
[ProducesResponseType(typeof(ProblemDetails), (int) HttpStatusCode.BadRequest)]
public async Task<IActionResult> CreateCategoryAsync([FromBody] CategoryResource categoryResource)
{
if(categoryResource == null)
{
ProblemDetails problem = new ProblemDetails
{
Title = "Failed to create a new category.",
Detail = "The specified category resource was null",
Instance = "ABA3B997-1B80-47FC-A72B-69BC0D8DFA93"
};
return BadRequest(problem);
}
Category category = mapper.Map<CategoryResource, Category>(categoryResource);

try
{
await categoryService.AddAsync(category)
.ConfigureAwait(false);
categoryService.Save();
return Created(nameof(CreateCategoryAsync), mapper.Map<Category, CategoryResourceResult>(category));
}
catch(DbUpdateException e)
{
Log.Logger.Error(e, "Database exception");

ProblemDetails problem = new ProblemDetails
{
Title = "Failed to save the new category.",
Detail = "There was a problem while saving the category to the database.",
Instance = "D56DBE55-57A1-4655-99C5-4F4ECEEE3BE4"
};
return BadRequest(problem);
}
}

/// <summary>
/// This method is responsible for updating the category.
/// </summary>
/// <param name="categoryId">The category identifier which is used for searching the category.</param>
/// <param name="categoryResource">The category resource which is used to update the category.</param>
/// <returns>This method returns the updated category resource result.</returns>
/// <response code="200">This endpoint returns the updated category.</response>
/// <response code="404">The 404 Not Found status code is returned when the category with the specified id could not be found.</response>
[HttpPut("{categoryId}")]
[Authorize(Policy = nameof(Scopes.CategoryWrite))]
[ProducesResponseType(typeof(CategoryResourceResult), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(ProblemDetails), (int) HttpStatusCode.NotFound)]
public async Task<IActionResult> UpdateCategory(int categoryId, CategoryResource categoryResource)
{
Category currentCategory = await categoryService.FindAsync(categoryId)
.ConfigureAwait(false);
if(currentCategory == null)
{
ProblemDetails problem = new ProblemDetails
{
Title = "Failed to update the category.",
Detail = "The specified category could not be found in the database",
Instance = "8F167FDF-3B2B-4E71-B3D0-AA2B1C1CE2C3"
};
return NotFound(problem);
}
mapper.Map(categoryResource, currentCategory);

categoryService.Update(currentCategory);
categoryService.Save();

return Ok(mapper.Map<Category, CategoryResourceResult>(currentCategory));
}

/// <summary>
/// This method is responsible for deleting the category.
/// </summary>
/// <param name="categoryId">The category identifier which is used for searching the category.</param>
/// <returns>This method returns status code 200.</returns>
/// <response code="200">This endpoint returns status code 200. Category is deleted.</response>
/// <response code="404">The 404 Not Found status code is returned when the category with the specified id could not be found.</response>
/// <response code="409">The 409 Conflict status code is returned when the category is still connected to a project.</response>
[HttpDelete("{categoryId}")]
[Authorize(Policy = nameof(Scopes.CategoryWrite))]
[ProducesResponseType(typeof(CategoryResourceResult), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(ProblemDetails), (int) HttpStatusCode.Conflict)]
[ProducesResponseType(typeof(ProblemDetails), (int) HttpStatusCode.NotFound)]
public async Task<IActionResult> DeleteCategory(int categoryId)
{
Category category = await categoryService.FindAsync(categoryId)
.ConfigureAwait(false);
if(category == null)
{
ProblemDetails problem = new ProblemDetails
{
Title = "Failed to delete the category.",
Detail = "The category could not be found in the database.",
Instance = "A0853DE4-C881-4597-A5A7-42F6761CECE0"
};
return NotFound(problem);
}

ProjectCategory projectCategory = await projectCategoryService.GetProjectCategory(categoryId);

if(projectCategory != null)
{
ProblemDetails problem = new ProblemDetails
{
Title = "Failed to delete the category.",
Detail = "The category is still connected to a project.",
Instance = "4AA5102B-3A6F-4144-BF01-0EC32B4E69A8"
};
return Conflict(problem);
}

await categoryService.RemoveAsync(category.Id)
.ConfigureAwait(false);
categoryService.Save();

IEnumerable<Category> categories = await categoryService.GetAll()
.ConfigureAwait(false);

return Ok(mapper.Map<IEnumerable<Category>, IEnumerable<CategoryResourceResult>>(categories));
}
}

}
Loading

0 comments on commit 0e04071

Please sign in to comment.