Progresss
This commit is contained in:
@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace KTUSAPS.Data.Model
|
namespace KTUSAPS.Data.Model
|
||||||
@@ -23,9 +24,11 @@ namespace KTUSAPS.Data.Model
|
|||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
public int IssueTypeId { get; set; }
|
public int IssueTypeId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual IssueType IssueType { get; set; }
|
public virtual IssueType IssueType { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual PublishedProblem Problem { get; set; }
|
public virtual PublishedProblem Problem { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual PublishedFeedback Feedback { get; set; }
|
public virtual PublishedFeedback Feedback { get; set; }
|
||||||
|
|
||||||
public Issue()
|
public Issue()
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace KTUSAPS.Data.Model
|
namespace KTUSAPS.Data.Model
|
||||||
@@ -12,6 +13,7 @@ namespace KTUSAPS.Data.Model
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string NameEn { get; set; }
|
public string NameEn { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public virtual HashSet<Issue> Issues { get; set; }
|
public virtual HashSet<Issue> Issues { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -7,9 +7,9 @@
|
|||||||
<FileName>Model\Issue.cs</FileName>
|
<FileName>Model\Issue.cs</FileName>
|
||||||
</TypeIdentifier>
|
</TypeIdentifier>
|
||||||
<ShowAsAssociation>
|
<ShowAsAssociation>
|
||||||
|
<Property Name="IssueType" />
|
||||||
<Property Name="Problem" />
|
<Property Name="Problem" />
|
||||||
<Property Name="Feedback" />
|
<Property Name="Feedback" />
|
||||||
<Property Name="IssueType" />
|
|
||||||
</ShowAsAssociation>
|
</ShowAsAssociation>
|
||||||
</Class>
|
</Class>
|
||||||
<Class Name="KTUSAPS.Data.Model.PublishedFeedback">
|
<Class Name="KTUSAPS.Data.Model.PublishedFeedback">
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
</ShowAsAssociation>
|
</ShowAsAssociation>
|
||||||
</Class>
|
</Class>
|
||||||
<Class Name="KTUSAPS.Data.Model.IssueType">
|
<Class Name="KTUSAPS.Data.Model.IssueType">
|
||||||
<Position X="12.25" Y="5.25" Width="1.5" />
|
<Position X="11" Y="5.5" Width="1.5" />
|
||||||
<TypeIdentifier>
|
<TypeIdentifier>
|
||||||
<HashCode>AAECAAAAAAAAAAAAACAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>
|
<HashCode>AAECAAAAAAAAAAAAACAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>
|
||||||
<FileName>Model\IssueType.cs</FileName>
|
<FileName>Model\IssueType.cs</FileName>
|
||||||
@@ -66,5 +66,12 @@
|
|||||||
<Property Name="Issues" />
|
<Property Name="Issues" />
|
||||||
</ShowAsCollectionAssociation>
|
</ShowAsCollectionAssociation>
|
||||||
</Class>
|
</Class>
|
||||||
|
<Class Name="KTUSAPS.Data.Model.Admin">
|
||||||
|
<Position X="0.75" Y="4.5" Width="1.5" />
|
||||||
|
<TypeIdentifier>
|
||||||
|
<HashCode>AAAAAAAEAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAA=</HashCode>
|
||||||
|
<FileName>Model\Admin.cs</FileName>
|
||||||
|
</TypeIdentifier>
|
||||||
|
</Class>
|
||||||
<Font Name="Segoe UI" Size="9" />
|
<Font Name="Segoe UI" Size="9" />
|
||||||
</ClassDiagram>
|
</ClassDiagram>
|
@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace KTUSAPS.Data.Model
|
namespace KTUSAPS.Data.Model
|
||||||
@@ -22,6 +23,7 @@ namespace KTUSAPS.Data.Model
|
|||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
|
|
||||||
public int? IssueId { get; set; }
|
public int? IssueId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual Issue Issue { get; set; }
|
public virtual Issue Issue { get; set; }
|
||||||
|
|
||||||
public PublishedFeedback()
|
public PublishedFeedback()
|
||||||
|
@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace KTUSAPS.Data.Model
|
namespace KTUSAPS.Data.Model
|
||||||
@@ -22,10 +23,13 @@ namespace KTUSAPS.Data.Model
|
|||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
|
|
||||||
public int? IssueId { get; set; }
|
public int? IssueId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual Issue Issue { get; set; }
|
public virtual Issue Issue { get; set; }
|
||||||
|
|
||||||
public int? SolutionId { get; set; }
|
public int? SolutionId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual Solution Solution { get; set; }
|
public virtual Solution Solution { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual HashSet<Vote> Votes { get; set; }
|
public virtual HashSet<Vote> Votes { get; set; }
|
||||||
|
|
||||||
public PublishedProblem()
|
public PublishedProblem()
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace KTUSAPS.Data.Model
|
namespace KTUSAPS.Data.Model
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ namespace KTUSAPS.Data.Model
|
|||||||
[MaxLength]
|
[MaxLength]
|
||||||
public string SolutionEn { get; set; }
|
public string SolutionEn { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
public virtual PublishedProblem Problem { get; set; }
|
public virtual PublishedProblem Problem { get; set; }
|
||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace KTUSAPS.Data.Model
|
namespace KTUSAPS.Data.Model
|
||||||
@@ -12,6 +13,7 @@ namespace KTUSAPS.Data.Model
|
|||||||
[MaxLength(64)]
|
[MaxLength(64)]
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }
|
||||||
public int ProblemId { get; set; }
|
public int ProblemId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
public virtual PublishedProblem Problem { get; set; }
|
public virtual PublishedProblem Problem { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
38
KTUSAPS/AuthorizeCheckOperationFilter.cs
Normal file
38
KTUSAPS/AuthorizeCheckOperationFilter.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace KTUSAPS
|
||||||
|
{
|
||||||
|
public class AuthorizeCheckOperationFilter : IOperationFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||||
|
{
|
||||||
|
var hasAuthorize =
|
||||||
|
context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
|
||||||
|
|| context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
|
||||||
|
|
||||||
|
if (hasAuthorize)
|
||||||
|
{
|
||||||
|
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
|
||||||
|
operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
|
||||||
|
|
||||||
|
operation.Security = new List<OpenApiSecurityRequirement>
|
||||||
|
{
|
||||||
|
new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
[
|
||||||
|
new OpenApiSecurityScheme {Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "msad"}
|
||||||
|
}
|
||||||
|
] = new[] {"email", "openid", "profile"}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -41,7 +41,6 @@ namespace KTUSAPS.Controllers
|
|||||||
/// <response code="200">Provided token is correct.</response>
|
/// <response code="200">Provided token is correct.</response>
|
||||||
/// <response code="401">No valid token provided.</response>
|
/// <response code="401">No valid token provided.</response>
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[HttpGet("Authed")]
|
[HttpGet("Authed")]
|
||||||
public bool IsAuthed() => true;
|
public bool IsAuthed() => true;
|
||||||
|
303
KTUSAPS/Controllers/GlueController.cs
Normal file
303
KTUSAPS/Controllers/GlueController.cs
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
using KTUSAPS.Data.Model;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
|
||||||
|
namespace KTUSAPS.Controllers
|
||||||
|
{
|
||||||
|
[Route("api")]
|
||||||
|
[ApiController]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class GlueController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly Data.SAPSDataContext dataContext;
|
||||||
|
private readonly IssuesController issuesController;
|
||||||
|
private readonly PublishedProblemsController publishedProblemsController;
|
||||||
|
private readonly PublishedFeedbacksController publishedFeedbacksController;
|
||||||
|
|
||||||
|
public GlueController(Data.SAPSDataContext dataContext, IssuesController issuesController, PublishedProblemsController publishedProblemsController, PublishedFeedbacksController publishedFeedbacksController)
|
||||||
|
{
|
||||||
|
this.dataContext = dataContext;
|
||||||
|
this.issuesController = issuesController;
|
||||||
|
this.publishedProblemsController = publishedProblemsController;
|
||||||
|
this.publishedFeedbacksController = publishedFeedbacksController;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("IssueTypes/{typeId}/Issues")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<IEnumerable<Issue>>> GetIssueTypeIssuesAsync(int typeId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
return Ok(issueType.Issues);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("IssueTypes/{typeId}/Issues")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
public async Task<ActionResult<Issue>> CreateIssueTypeIssueAsync(int typeId, [FromBody] Issue issueToCreate)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
issueToCreate.IssueTypeId = issueType.Id;
|
||||||
|
var createResult = await issuesController.CreateIssueAsync(issueToCreate);
|
||||||
|
if(createResult.Result is CreatedAtActionResult actionResult && actionResult.Value is Issue createdIssue)
|
||||||
|
return CreatedAtAction(nameof(GetIssueTypeIssueAsync), new { typeId = typeId, issueId = createdIssue.Id }, createdIssue);
|
||||||
|
return createResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("IssueTypes/{typeId}/Issues/{issueId}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<Issue>> GetIssueTypeIssueAsync(int typeId, int issueId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPatch("IssueTypes/{typeId}/Issues/{issueId}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<Issue>> UpdateIssueTypeIssueAsync(int typeId, int issueId, [FromBody] Issue issue)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var dbIssue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (dbIssue == null)
|
||||||
|
return NotFound();
|
||||||
|
return await issuesController.UpdateIssueAsync(dbIssue.Id, issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("IssueTypes/{typeId}/Issues/{issueId}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<IActionResult> DeleteIssueTypeIssueAsync(int typeId, int issueId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
return await issuesController.DeleteIssueAsync(issue.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("IssueTypes/{typeId}/Issues/{issueId}/Problem")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<PublishedProblem>> GetIssueTypeIssueProblemAsync(int typeId, int issueId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Problem == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(issue.Problem);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("IssueTypes/{typeId}/Issues/{issueId}/Problem")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||||
|
public async Task<ActionResult<PublishedProblem>> CreateIssueTypeIssueProblemAsync(int typeId, int issueId, [FromBody] PublishedProblem publishedProblem)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Problem != null)
|
||||||
|
return Conflict();
|
||||||
|
publishedProblem.IssueId = issue.Id;
|
||||||
|
var createResult = await publishedProblemsController.CreatePublishedProblem(publishedProblem);
|
||||||
|
if (createResult.Result is CreatedAtActionResult actionResult && actionResult.Value is PublishedProblem createdProblem)
|
||||||
|
return CreatedAtAction(nameof(GetIssueTypeIssueProblemAsync), new { typeId = typeId, issueId = issue.Id }, createdProblem);
|
||||||
|
return createResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("IssueTypes/{typeId}/Issues/{issueId}/Problem")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<IActionResult> DeleteIssueTypeIssueProblemAsync(int typeId, int issueId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Problem == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
dataContext.PublishedProblems.Remove(issue.Problem);
|
||||||
|
await dataContext.SaveChangesAsync();
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("IssueTypes/{typeId}/Issues/{issueId}/Feedback")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<PublishedProblem>> GetIssueTypeIssueFeedbackAsync(int typeId, int issueId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Feedback).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Feedback == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(issue.Feedback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("IssueTypes/{typeId}/Issues/{issueId}/Feedback")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||||
|
public async Task<ActionResult<PublishedFeedback>> CreateIssueTypeIssueFeedbackAsync(int typeId, int issueId, PublishedFeedback publishedFeedback)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Feedback).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Feedback != null)
|
||||||
|
return Conflict();
|
||||||
|
|
||||||
|
publishedFeedback.IssueId = issue.Id;
|
||||||
|
var createResult = await publishedFeedbacksController.PostPublishedFeedback(publishedFeedback);
|
||||||
|
if (createResult.Result is CreatedAtActionResult actionResult && actionResult.Value is PublishedFeedback createdFeedback)
|
||||||
|
return CreatedAtAction(nameof(GetIssueTypeIssueFeedbackAsync), new { typeId = typeId, issueId = issue.Id }, createdFeedback);
|
||||||
|
return createResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("IssueTypes/{typeId}/Issues/{issueId}/Feedback")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<IActionResult> DeleteIssueTypeIssueFeedbackAsync(int typeId, int issueId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Feedback == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
dataContext.PublishedFeedbacks.Remove(issue.Feedback);
|
||||||
|
await dataContext.SaveChangesAsync();
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("IssueTypes/{typeId}/Issues/{issueId}/Problem/Votes")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<IEnumerable<Vote>>> GetIssueTypeIssueProblemVotesAsync(int typeId, int issueId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).ThenInclude(p => p.Votes).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Problem == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(issue.Problem.Votes);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("IssueTypes/{typeId}/Issues/{issueId}/Problem/Votes")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
|
public async Task<ActionResult<Vote>> CreateIssueTypeIssueProblemVotesAsync(int typeId, int issueId, Vote vote)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).ThenInclude(p => p.Votes).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Problem == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
// TODO: Get user id from auth claims
|
||||||
|
if (vote.UserId == default)
|
||||||
|
return BadRequest("Please provide user id");
|
||||||
|
|
||||||
|
vote.Problem = issue.Problem;
|
||||||
|
dataContext.Votes.Add(vote);
|
||||||
|
await dataContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(GetIssueTypeIssueProblemVoteAsync), new { typeId = typeId, issueId = issue.Id, userId = vote.UserId }, vote);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("IssueTypes/{typeId}/Issues/{issueId}/Problem/Votes/{userId}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<Vote>> GetIssueTypeIssueProblemVoteAsync(int typeId, int issueId, string userId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).ThenInclude(p => p.Votes).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Problem == null)
|
||||||
|
return NotFound();
|
||||||
|
var vote = issue.Problem.Votes.FirstOrDefault(v => v.UserId == userId);
|
||||||
|
if(vote == default)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(vote);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("IssueTypes/{typeId}/Issues/{issueId}/Problem/Votes/{userId}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<Vote>> DeleteIssueTypeIssueProblemVoteAsync(int typeId, int issueId, string userId)
|
||||||
|
{
|
||||||
|
var issueType = await dataContext.IssueTypes.Include(t => t.Issues).ThenInclude(i => i.Problem).ThenInclude(p => p.Votes).FirstOrDefaultAsync(t => t.Id == typeId);
|
||||||
|
if (issueType == default)
|
||||||
|
return NotFound();
|
||||||
|
var issue = issueType.Issues.FirstOrDefault(i => i.Id == issueId);
|
||||||
|
if (issue == null)
|
||||||
|
return NotFound();
|
||||||
|
if (issue.Problem == null)
|
||||||
|
return NotFound();
|
||||||
|
var vote = issue.Problem.Votes.FirstOrDefault(v => v.UserId == userId);
|
||||||
|
if (vote == default)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
dataContext.Votes.Remove(vote);
|
||||||
|
await dataContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -58,11 +58,11 @@ namespace KTUSAPS.Controllers
|
|||||||
if (issueType == null)
|
if (issueType == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
return issueType;
|
return Ok(issueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> UpdateIssueType(int id, IssueType issueType)
|
public async Task<IActionResult> UpdateIssueType(int id, IssueType issueType)
|
||||||
{
|
{
|
||||||
@@ -79,7 +79,7 @@ namespace KTUSAPS.Controllers
|
|||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
return NoContent();
|
return Ok(eIssueType.Entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -77,7 +77,7 @@ namespace KTUSAPS.Controllers
|
|||||||
nameof(databaseIssue.Publishable)
|
nameof(databaseIssue.Publishable)
|
||||||
});
|
});
|
||||||
await dataContext.SaveChangesAsync();
|
await dataContext.SaveChangesAsync();
|
||||||
return NoContent();
|
return Ok(eIssue.Entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
|
@@ -61,9 +61,9 @@ namespace KTUSAPS.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> UpdatePublishedFeedback(int id, PublishedFeedback publishedFeedback)
|
public async Task<ActionResult<PublishedFeedback>> UpdatePublishedFeedback(int id, PublishedFeedback publishedFeedback)
|
||||||
{
|
{
|
||||||
var databasePublishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);
|
var databasePublishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);
|
||||||
if (databasePublishedFeedback == default)
|
if (databasePublishedFeedback == default)
|
||||||
@@ -81,7 +81,7 @@ namespace KTUSAPS.Controllers
|
|||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
return NoContent();
|
return Ok(ePublishedFeedback.Entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -39,7 +39,7 @@ namespace KTUSAPS.Controllers
|
|||||||
if (publishedProblem.Id != default)
|
if (publishedProblem.Id != default)
|
||||||
return BadRequest("Id has been set on create request, please do not do that, set id to 0 or ommit it.");
|
return BadRequest("Id has been set on create request, please do not do that, set id to 0 or ommit it.");
|
||||||
if (publishedProblem.Issue != null || publishedProblem.Solution != null || publishedProblem.Votes != null)
|
if (publishedProblem.Issue != null || publishedProblem.Solution != null || publishedProblem.Votes != null)
|
||||||
return BadRequest("Do not privide navigation property values.");
|
return BadRequest("Do not provide navigation property values.");
|
||||||
|
|
||||||
_context.PublishedProblems.Add(publishedProblem);
|
_context.PublishedProblems.Add(publishedProblem);
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
@@ -61,9 +61,9 @@ namespace KTUSAPS.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> UpdatePublishedProblem(int id, PublishedProblem publishedProblem)
|
public async Task<ActionResult<PublishedProblem>> UpdatePublishedProblem(int id, PublishedProblem publishedProblem)
|
||||||
{
|
{
|
||||||
var databasePublishedProblem = await _context.PublishedProblems.FindAsync(id);
|
var databasePublishedProblem = await _context.PublishedProblems.FindAsync(id);
|
||||||
if (databasePublishedProblem == default)
|
if (databasePublishedProblem == default)
|
||||||
@@ -82,7 +82,7 @@ namespace KTUSAPS.Controllers
|
|||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
return NoContent();
|
return Ok(ePublishedProblem.Entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ namespace KTUSAPS.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/Votes")]
|
[HttpPost("{id}/Votes")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Vote>> Vote(int id, Vote vote)
|
public async Task<ActionResult<Vote>> Vote(int id, Vote vote)
|
||||||
|
@@ -10,20 +10,24 @@
|
|||||||
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.10" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.12" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.11">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.11">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="5.0.2" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="5.0.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.1" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.2.1" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="5.6.3" />
|
||||||
<PackageReference Include="VueCliMiddleware" Version="5.0.0" />
|
<PackageReference Include="VueCliMiddleware" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using VueCliMiddleware;
|
using VueCliMiddleware;
|
||||||
|
|
||||||
namespace KTUSAPS
|
namespace KTUSAPS
|
||||||
@@ -23,7 +24,10 @@ namespace KTUSAPS
|
|||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddControllers();
|
services.AddControllers(options =>
|
||||||
|
options.SuppressAsyncSuffixInActionNames = false
|
||||||
|
)
|
||||||
|
.AddControllersAsServices();
|
||||||
services.AddSpaStaticFiles(configuration =>
|
services.AddSpaStaticFiles(configuration =>
|
||||||
{
|
{
|
||||||
configuration.RootPath = "ClientApp/dist";
|
configuration.RootPath = "ClientApp/dist";
|
||||||
@@ -32,8 +36,9 @@ namespace KTUSAPS
|
|||||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
|
options.MetadataAddress = "https://login.microsoftonline.com/3415f2f7-f5a8-4092-b52a-003aaf844853/v2.0/.well-known/openid-configuration";
|
||||||
options.Audience = Configuration["ClientId"];
|
options.Audience = Configuration["ClientId"];
|
||||||
options.Authority = Configuration["Authority"];
|
//options.Authority = Configuration["Authority"];
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddAuthorization((configure) =>
|
services.AddAuthorization((configure) =>
|
||||||
@@ -47,7 +52,28 @@ namespace KTUSAPS
|
|||||||
services.AddDbContext<Data.SAPSDataContext>((options) => options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
services.AddDbContext<Data.SAPSDataContext>((options) => options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
|
||||||
services.AddHostedService<DatabaseInitializationService>();
|
services.AddHostedService<DatabaseInitializationService>();
|
||||||
|
|
||||||
services.AddSwaggerGen();
|
services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.OperationFilter<AuthorizeCheckOperationFilter>();
|
||||||
|
options.AddSecurityDefinition("msad", new Microsoft.OpenApi.Models.OpenApiSecurityScheme()
|
||||||
|
{
|
||||||
|
Type = Microsoft.OpenApi.Models.SecuritySchemeType.OAuth2,
|
||||||
|
Flows = new Microsoft.OpenApi.Models.OpenApiOAuthFlows()
|
||||||
|
{
|
||||||
|
AuthorizationCode = new Microsoft.OpenApi.Models.OpenApiOAuthFlow()
|
||||||
|
{
|
||||||
|
AuthorizationUrl = new Uri("https://login.microsoftonline.com/3415f2f7-f5a8-4092-b52a-003aaf844853/oauth2/v2.0/authorize"),
|
||||||
|
TokenUrl = new Uri("https://login.microsoftonline.com/3415f2f7-f5a8-4092-b52a-003aaf844853/oauth2/v2.0/token"),
|
||||||
|
Scopes = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "openid", "Access to user's id" },
|
||||||
|
{ "profile", "Access to user's name" },
|
||||||
|
{ "email", "Access to email" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
@@ -60,6 +86,10 @@ namespace KTUSAPS
|
|||||||
app.UseSwaggerUI(c =>
|
app.UseSwaggerUI(c =>
|
||||||
{
|
{
|
||||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
|
||||||
|
|
||||||
|
c.OAuthClientId(Configuration["ClientId"]);
|
||||||
|
c.OAuthAppName("KTUSA Problem<65> sistema");
|
||||||
|
c.OAuthUsePkce();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"Main": "Server=localhost;User=saps;Password=B35eJUmIJxeG0g9yi6ni;Database=saps"
|
"Main": "Server=localhost;User=saps;Password=FuhcMfapPkGOH8DjPSAw;Database=saps"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
@@ -13,4 +13,4 @@
|
|||||||
"ClientId": "411a8715-3d63-4a03-9be8-9370d920e36f",
|
"ClientId": "411a8715-3d63-4a03-9be8-9370d920e36f",
|
||||||
"Authority": "https://login.microsoftonline.com/3415f2f7-f5a8-4092-b52a-003aaf844853/v2.0",
|
"Authority": "https://login.microsoftonline.com/3415f2f7-f5a8-4092-b52a-003aaf844853/v2.0",
|
||||||
"Tenant": "3415f2f7-f5a8-4092-b52a-003aaf844853"
|
"Tenant": "3415f2f7-f5a8-4092-b52a-003aaf844853"
|
||||||
}
|
}
|
Reference in New Issue
Block a user