Merge branch 'dev-api-documentation' of rajukottedi/PortBlog.API into dev

This commit is contained in:
Bangara Raju Kottedi 2024-04-29 16:04:20 +05:30 committed by Gogs
commit 46edddc84f
5 changed files with 354 additions and 35 deletions

View File

@ -1,4 +1,5 @@
using AutoMapper;
using Asp.Versioning;
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using PortBlog.API.Entities;
@ -7,8 +8,9 @@ using PortBlog.API.Repositories.Contracts;
namespace PortBlog.API.Controllers
{
[Route("api/cv")]
[Route("api/v{versions:apiVersion}/cv")]
[ApiController]
[ApiVersion(1)]
public class CvController : ControllerBase
{
private readonly ILogger<CvController> _logger;
@ -24,7 +26,19 @@ namespace PortBlog.API.Controllers
_mapper = mapper;
}
/// <summary>
/// Get CV details of the candidate by candidateid.
/// </summary>
/// <param name="candidateId">The id of the candidate whose cv to get</param>
/// <returns>CV details of the candidate</returns>
/// <response code="200">Returns the requested cv of the candidate</response>
[HttpGet("{candidateId}")]
[Obsolete]
[ApiVersion(0.1, Deprecated = true)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<ResumeDto>> Get(int candidateId)
{
try
@ -47,7 +61,17 @@ namespace PortBlog.API.Controllers
}
}
/// <summary>
/// Get hobbies of the candidate by candidateid
/// </summary>
/// <param name="candidateId">The id of the candidate whose hobbies to get</param>
/// <returns>Hobbies of the candidate</returns>
/// <response code="200">Returns the requested hobbies of the candidate</response>
[HttpGet("GetHobbies/{candidateId}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<AboutDto>> GetHobbies(int candidateId)
{
try
@ -70,7 +94,17 @@ namespace PortBlog.API.Controllers
}
}
/// <summary>
/// Get Candidate details with social links by candidateid
/// </summary>
/// <param name="candidateId">The id of the candidate whose detials to get with social links</param>
/// <returns>Candidate details with sociallinks</returns>
/// <response code="200">Returns the requested candidate details with social links</response>
[HttpGet("GetCandidateWithSocialLinks/{candidateId}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<CandidateSocialLinksDto>> GetContact(int candidateId)
{
try
@ -93,7 +127,17 @@ namespace PortBlog.API.Controllers
}
}
/// <summary>
/// Get Candidate resume by candidateid
/// </summary>
/// <param name="candidateId">The id of the candidate whose resume to get</param>
/// <returns>Candidate resume</returns>
/// <response code="200">Returns the requested candidate resume</response>
[HttpGet("GetResume/{candidateId}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<ResumeDto>> GetResume(int candidateId)
{
try
@ -116,7 +160,17 @@ namespace PortBlog.API.Controllers
}
}
/// <summary>
/// Get Candidate projects by candidateid
/// </summary>
/// <param name="candidateId">The id of the candidate whose projects to get</param>
/// <returns>Candidate projects</returns>
/// <response code="200">Returns the requested candidate projects</response>
[HttpGet("GetProjects/{candidateId}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<ProjectsDto>> GetProjects(int candidateId)
{
try
@ -139,7 +193,17 @@ namespace PortBlog.API.Controllers
}
}
/// <summary>
/// Get Candidate blog with posts by candidateid
/// </summary>
/// <param name="candidateId">The id of the candidate whose blog with posts to get</param>
/// <returns>Candidate blog with posts</returns>
/// <response code="200">Returns the requested candidate blog with posts</response>
[HttpGet("GetBlog/{candidateId}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<BlogDto>> GetBlog(int candidateId)
{
try

View File

@ -1,17 +1,33 @@
namespace PortBlog.API.Models
{
/// <summary>
/// CV details of the candidate
/// </summary>
public class ResumeDto
{
/// <summary>
/// The id of the cv
/// </summary>
public int ResumeId { get; set; }
/// <summary>
/// The title of the candidate
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// A brief description about the candidate
/// </summary>
public string About { get; set; } = string.Empty;
/// <summary>
/// Candidate's information
/// </summary>
public CandidateDto? Candidate { get; set; }
/// <summary>
/// Candidate's Social Media links
/// </summary>
public SocialLinksDto? SocialLinks { get; set; }
/// <summary>
/// Candidate's blog posts
/// </summary>
public ICollection<PostDto> Posts
{
get
@ -19,19 +35,33 @@
return SocialLinks?.Posts ?? new List<PostDto>();
}
}
/// <summary>
/// The education details of the candidate
/// </summary>
public ICollection<AcademicDto> Academics { get; set; } = new List<AcademicDto>();
/// <summary>
/// The skills of the candidate
/// </summary>
public ICollection<SkillDto> Skills { get; set; } = new List<SkillDto>();
/// <summary>
/// The work experiences of the candidate
/// </summary>
public ICollection<ExperienceDto> Experiences { get; set; } = new List<ExperienceDto>();
/// <summary>
/// The certifications done by the candidate
/// </summary>
public ICollection<CertificationDto> Certifications { get; set; } = new List<CertificationDto>();
/// <summary>
/// The hobbies of the candidate
/// </summary>
public ICollection<HobbyDto> Hobbies { get; set; } = new List<HobbyDto>();
/// <summary>
/// The projects of the candidate
/// </summary>
public ICollection<ProjectDto> Projects { get; set; } = new List<ProjectDto>();
/// <summary>
/// The project categories of all the projects
/// </summary>
public ICollection<string> ProjectsCategories
{
get

View File

@ -4,9 +4,12 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>PortBlog.API.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />

View File

@ -0,0 +1,174 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>PortBlog.API</name>
</assembly>
<members>
<member name="M:PortBlog.API.Controllers.CvController.Get(System.Int32)">
<summary>
Get CV details of the candidate by candidateid.
</summary>
<param name="candidateId">The id of the candidate whose cv to get</param>
<returns>CV details of the candidate</returns>
<response code="200">Returns the requested cv of the candidate</response>
</member>
<member name="M:PortBlog.API.Controllers.CvController.GetHobbies(System.Int32)">
<summary>
Get hobbies of the candidate by candidateid
</summary>
<param name="candidateId">The id of the candidate whose hobbies to get</param>
<returns>Hobbies of the candidate</returns>
<response code="200">Returns the requested hobbies of the candidate</response>
</member>
<member name="M:PortBlog.API.Controllers.CvController.GetContact(System.Int32)">
<summary>
Get Candidate details with social links by candidateid
</summary>
<param name="candidateId">The id of the candidate whose detials to get with social links</param>
<returns>Candidate details with sociallinks</returns>
<response code="200">Returns the requested candidate details with social links</response>
</member>
<member name="M:PortBlog.API.Controllers.CvController.GetResume(System.Int32)">
<summary>
Get Candidate resume by candidateid
</summary>
<param name="candidateId">The id of the candidate whose resume to get</param>
<returns>Candidate resume</returns>
<response code="200">Returns the requested candidate resume</response>
</member>
<member name="M:PortBlog.API.Controllers.CvController.GetProjects(System.Int32)">
<summary>
Get Candidate projects by candidateid
</summary>
<param name="candidateId">The id of the candidate whose projects to get</param>
<returns>Candidate projects</returns>
<response code="200">Returns the requested candidate projects</response>
</member>
<member name="M:PortBlog.API.Controllers.CvController.GetBlog(System.Int32)">
<summary>
Get Candidate blog with posts by candidateid
</summary>
<param name="candidateId">The id of the candidate whose blog with posts to get</param>
<returns>Candidate blog with posts</returns>
<response code="200">Returns the requested candidate blog with posts</response>
</member>
<member name="T:PortBlog.API.Migrations.InitialDBMigration">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.InitialDBMigration.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.InitialDBMigration.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.InitialDBMigration.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
<inheritdoc />
</member>
<member name="T:PortBlog.API.Migrations.InitialSeedData">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.InitialSeedData.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.InitialSeedData.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.InitialSeedData.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
<inheritdoc />
</member>
<member name="T:PortBlog.API.Migrations.AddDobToCandidate">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.AddDobToCandidate.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.AddDobToCandidate.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.AddDobToCandidate.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
<inheritdoc />
</member>
<member name="T:PortBlog.API.Migrations.AddDobData">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.AddDobData.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.AddDobData.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:PortBlog.API.Migrations.AddDobData.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
<inheritdoc />
</member>
<member name="T:PortBlog.API.Models.ResumeDto">
<summary>
CV details of the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.ResumeId">
<summary>
The id of the cv
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Title">
<summary>
The title of the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.About">
<summary>
A brief description about the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Candidate">
<summary>
Candidate's information
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.SocialLinks">
<summary>
Candidate's Social Media links
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Posts">
<summary>
Candidate's blog posts
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Academics">
<summary>
The education details of the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Skills">
<summary>
The skills of the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Experiences">
<summary>
The work experiences of the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Certifications">
<summary>
The certifications done by the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Hobbies">
<summary>
The hobbies of the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.Projects">
<summary>
The projects of the candidate
</summary>
</member>
<member name="P:PortBlog.API.Models.ResumeDto.ProjectsCategories">
<summary>
The project categories of all the projects
</summary>
</member>
</members>
</doc>

View File

@ -1,3 +1,5 @@
using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
using Microsoft.OpenApi.Models;
@ -5,6 +7,7 @@ using PortBlog.API.DbContexts;
using PortBlog.API.Extensions;
using PortBlog.API.Middleware;
using Serilog;
using System.Reflection;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
@ -27,8 +30,64 @@ builder.Services.AddProblemDetails();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
var connectionString = builder.Configuration.GetConnectionString("PortBlogDBConnectionString");
if (builder.Configuration.GetValue<bool>("ConnectionStrings:Encryption"))
{
connectionString = builder.Configuration.DecryptConnectionString(connectionString);
}
if (string.IsNullOrEmpty(connectionString))
{
throw new Exception("Connection string cannot be empty");
}
builder.Services
.AddDbContext<CvBlogContext>(dbContextOptions
=> dbContextOptions.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
builder.Services.AddRepositories();
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
// Registering API Versioning Specification services
builder.Services.AddApiVersioning(setupAction =>
{
setupAction.ReportApiVersions = true;
setupAction.AssumeDefaultVersionWhenUnspecified = true;
setupAction.DefaultApiVersion = new ApiVersion(1, 0);
}).AddMvc()
.AddApiExplorer(setupAction =>
{
setupAction.SubstituteApiVersionInUrl = true;
});
var apiVersionDescriptionProvider = builder.Services.BuildServiceProvider()
.GetRequiredService<IApiVersionDescriptionProvider>();
builder.Services.AddSwaggerGen(c =>
{
foreach(var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
{
c.SwaggerDoc(
$"{description.GroupName}",
new()
{
Title = "Portfolio Blog API",
Version = description.ApiVersion.ToString(),
Description = "Through this API you can access candidate cv details and along with other details."
});
}
// XML Comments file for API Documentation
var xmlCommentsFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlCommentsFullPath = $"{Path.Combine(AppContext.BaseDirectory, xmlCommentsFile)}";
c.IncludeXmlComments(xmlCommentsFullPath);
// Add Security Definition to the Swagger
c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Description = "ApiKey must appear in header",
@ -56,26 +115,6 @@ builder.Services.AddSwaggerGen(c =>
c.AddSecurityRequirement(requirement);
});
var connectionString = builder.Configuration.GetConnectionString("PortBlogDBConnectionString");
if (builder.Configuration.GetValue<bool>("ConnectionStrings:Encryption"))
{
connectionString = builder.Configuration.DecryptConnectionString(connectionString);
}
if (string.IsNullOrEmpty(connectionString))
{
throw new Exception("Connection string cannot be empty");
}
builder.Services
.AddDbContext<CvBlogContext>(dbContextOptions
=> dbContextOptions.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
builder.Services.AddRepositories();
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
var app = builder.Build();
if (!app.Environment.IsDevelopment())
@ -87,7 +126,16 @@ if (!app.Environment.IsDevelopment())
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
app.UseSwaggerUI(setupAction =>
{
var descriptions = app.DescribeApiVersions();
foreach(var description in descriptions)
{
setupAction.SwaggerEndpoint(
$"/swagger/{description.GroupName}/swagger.json",
description.GroupName.ToUpperInvariant());
}
});
}
app.UseHttpsRedirection();