mirror of
https://github.com/rjNemo/ticket_manager
synced 2026-06-12 11:46:40 +00:00
pull backend to master
This commit is contained in:
commit
d6cd9abeda
21 changed files with 535 additions and 174 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -5,3 +5,6 @@ obj/
|
||||||
Migrations/
|
Migrations/
|
||||||
app.db*
|
app.db*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
app.db
|
||||||
|
client/node_modules
|
||||||
|
Scripts/
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ using TicketManager.Models;
|
||||||
|
|
||||||
namespace TicketManager.Controllers
|
namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Route("api/v1/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class UsersController : ControllerBase
|
public class UsersController : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
@ -23,22 +23,16 @@ namespace TicketManager.Controllers
|
||||||
|
|
||||||
// GET: api/Users
|
// GET: api/Users
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
|
public async Task<ActionResult<IEnumerable<AppUser>>> GetUsers()
|
||||||
{
|
{
|
||||||
return await _context.Users
|
return await getAllAppUsersAsync();
|
||||||
.Include(p => p.Assignments)
|
|
||||||
.Include(p => p.Edits)
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/Users/5
|
// GET: api/Users/5
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<ActionResult<User>> GetUser(Guid id)
|
public async Task<ActionResult<AppUser>> GetUser(Guid id)
|
||||||
{
|
{
|
||||||
var user = await _context.Users
|
var user = await getAppUserByIdAsync(id);
|
||||||
.Include(p => p.Assignments)
|
|
||||||
.Include(p => p.Edits)
|
|
||||||
.FirstOrDefaultAsync(p => p.Id == id);
|
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
|
|
@ -52,7 +46,7 @@ namespace TicketManager.Controllers
|
||||||
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
||||||
// more details see https://aka.ms/RazorPagesCRUD.
|
// more details see https://aka.ms/RazorPagesCRUD.
|
||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
public async Task<IActionResult> PutUser(Guid id, User user)
|
public async Task<IActionResult> PutUser(Guid id, AppUser user)
|
||||||
{
|
{
|
||||||
if (id != user.Id)
|
if (id != user.Id)
|
||||||
{
|
{
|
||||||
|
|
@ -84,9 +78,9 @@ namespace TicketManager.Controllers
|
||||||
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
||||||
// more details see https://aka.ms/RazorPagesCRUD.
|
// more details see https://aka.ms/RazorPagesCRUD.
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult<User>> PostUser(User user)
|
public async Task<ActionResult<AppUser>> PostUser(AppUser user)
|
||||||
{
|
{
|
||||||
_context.Users.Add(user);
|
_context.AppUsers.Add(user);
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
return CreatedAtAction("GetUser", new { id = user.Id }, user);
|
return CreatedAtAction("GetUser", new { id = user.Id }, user);
|
||||||
|
|
@ -94,23 +88,64 @@ namespace TicketManager.Controllers
|
||||||
|
|
||||||
// DELETE: api/Users/5
|
// DELETE: api/Users/5
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<ActionResult<User>> DeleteUser(int id)
|
public async Task<ActionResult<AppUser>> DeleteUser(int id)
|
||||||
{
|
{
|
||||||
var user = await _context.Users.FindAsync(id);
|
var user = await _context.AppUsers.FindAsync(id);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Users.Remove(user);
|
_context.AppUsers.Remove(user);
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/projects")]
|
||||||
|
public async Task<ActionResult<IEnumerable<Project>>> GetAppUserProjects(Guid id)
|
||||||
|
{
|
||||||
|
AppUser user = await getAppUserByIdAsync(id);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
return user.GetProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/tickets/")]
|
||||||
|
public async Task<ActionResult<IEnumerable<Ticket>>> GetAppUserTickets(Guid id)
|
||||||
|
{
|
||||||
|
AppUser user = await getAppUserByIdAsync(id);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
return user.GetTickets();
|
||||||
|
}
|
||||||
|
|
||||||
private bool UserExists(Guid id)
|
private bool UserExists(Guid id)
|
||||||
{
|
{
|
||||||
return _context.Users.Any(e => e.Id == id);
|
return _context.AppUsers.Any(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<AppUser> appUserQuery()
|
||||||
|
{
|
||||||
|
return _context.AppUsers
|
||||||
|
.Include(p => p.Assignments)
|
||||||
|
.ThenInclude(a => a.Project)
|
||||||
|
.ThenInclude(p => p.Tickets)
|
||||||
|
.Include(p => p.Edits);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ActionResult<IEnumerable<AppUser>>> getAllAppUsersAsync()
|
||||||
|
{
|
||||||
|
return await appUserQuery().ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<AppUser> getAppUserByIdAsync(Guid id)
|
||||||
|
{
|
||||||
|
return await appUserQuery().FirstOrDefaultAsync(a => a.Id == id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ using TicketManager.Models;
|
||||||
|
|
||||||
namespace TicketManager.Controllers
|
namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Route("api/v1/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class FilesController : ControllerBase
|
public class FilesController : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ using TicketManager.Models;
|
||||||
|
|
||||||
namespace TicketManager.Controllers
|
namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Route("api/v1/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class HistoriesController : ControllerBase
|
public class HistoriesController : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ using TicketManager.Models;
|
||||||
|
|
||||||
namespace TicketManager.Controllers
|
namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Route("api/v1/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class NotesController : ControllerBase
|
public class NotesController : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Mime;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
@ -8,9 +9,11 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using TicketManager.Data;
|
using TicketManager.Data;
|
||||||
using TicketManager.Models;
|
using TicketManager.Models;
|
||||||
|
|
||||||
|
|
||||||
namespace TicketManager.Controllers
|
namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Produces("application/json")]
|
||||||
|
[Route("api/v1/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class ProjectsController : ControllerBase
|
public class ProjectsController : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
@ -21,59 +24,82 @@ namespace TicketManager.Controllers
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/Projects
|
/// <summary>
|
||||||
|
/// Returns all existing projects.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Sample request:
|
||||||
|
///
|
||||||
|
/// GET: api/Projects
|
||||||
|
///
|
||||||
|
/// </remarks>
|
||||||
|
/// <response code="200">Returns all existing projects</response>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult<IEnumerable<Project>>> GetProjects()
|
public async Task<ActionResult<IEnumerable<Project>>> GetProjects()
|
||||||
{
|
{
|
||||||
return await _context.Projects
|
return await GetAllProjectsAsync();
|
||||||
.Include(p => p.Assignments)
|
|
||||||
.ThenInclude(a => a.User)
|
|
||||||
.Include(p => p.Tickets)
|
|
||||||
.Include(p => p.Manager)
|
|
||||||
.Include(p => p.Files)
|
|
||||||
.AsNoTracking()
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/Projects/5
|
/// <summary>
|
||||||
|
/// Returns a specific project.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Sample request:
|
||||||
|
///
|
||||||
|
/// GET: api/Projects/2
|
||||||
|
///
|
||||||
|
/// </remarks>
|
||||||
|
/// <response code="200">Returns a specific project</response>
|
||||||
|
/// <response code="404">If the required project is null</response>
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Project>> GetProject(int id)
|
public async Task<ActionResult<Project>> GetProject(int id)
|
||||||
{
|
{
|
||||||
var project = await _context.Projects
|
Project project = await GetProjectByIdAsync(id);
|
||||||
.Include(p => p.Assignments)
|
|
||||||
.Include(p => p.Tickets)
|
|
||||||
.Include(p => p.Manager)
|
|
||||||
.Include(p => p.Files)
|
|
||||||
.AsNoTracking()
|
|
||||||
.FirstOrDefaultAsync(p => p.Id == id);
|
|
||||||
|
|
||||||
|
|
||||||
if (project == null)
|
if (project == null)
|
||||||
{ return NotFound(); }
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/Projects/5/Members
|
|
||||||
[HttpGet("{id}/members")]
|
|
||||||
public async Task<ActionResult<IEnumerable<User>>> GetProjectMembers(int id)
|
|
||||||
{
|
|
||||||
var project = await _context.Projects.FindAsync(id);
|
|
||||||
|
|
||||||
if (project == null)
|
|
||||||
{ return NotFound(); }
|
|
||||||
|
|
||||||
return project.GetMembers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT: api/Projects/5
|
// PUT: api/Projects/5
|
||||||
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
|
||||||
// more details see https://aka.ms/RazorPagesCRUD.
|
// more details see https://aka.ms/RazorPagesCRUD.
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a specific project.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Sample request:
|
||||||
|
///
|
||||||
|
/// PUT: api/Projects/3
|
||||||
|
/// {
|
||||||
|
/// "id": "357727fd-5262-4522-b8a3-38271d43de84",
|
||||||
|
/// "firstName": "Thomas",
|
||||||
|
/// "lastName": "Price",
|
||||||
|
/// "presentation": "New Team?!",
|
||||||
|
/// "email": "tp@mail.com",
|
||||||
|
/// "phone": "0198237645"
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// </remarks>
|
||||||
|
/// <response code="200">Returns the modified project</response>
|
||||||
|
/// <response code="204">Request was succesful but no content is changed</response>
|
||||||
|
/// <response code="404">If the required project is null</response>
|
||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> PutProject(int id, Project project)
|
public async Task<IActionResult> PutProject(int id, Project project)
|
||||||
{
|
{
|
||||||
if (id != project.Id)
|
if (id != project.Id)
|
||||||
{ return BadRequest(); }
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
_context.Entry(project).State = EntityState.Modified;
|
_context.Entry(project).State = EntityState.Modified;
|
||||||
|
|
||||||
|
|
@ -109,7 +135,7 @@ namespace TicketManager.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/addmembers")]
|
[HttpPost("{id}/addmembers")]
|
||||||
public async Task<ActionResult<Project>> PostAssignment(int id, List<User> usersToAdd)
|
public async Task<ActionResult<Project>> PostAssignment(int id, List<AppUser> usersToAdd)
|
||||||
{
|
{
|
||||||
var project = await _context.Projects.FindAsync(id);
|
var project = await _context.Projects.FindAsync(id);
|
||||||
|
|
||||||
|
|
@ -157,6 +183,76 @@ namespace TicketManager.Controllers
|
||||||
|
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
// GET: api/Projects/5/Members
|
||||||
|
[HttpGet("{id}/members")]
|
||||||
|
public async Task<ActionResult<List<AppUser>>> GetProjectMembers(int id)
|
||||||
|
{
|
||||||
|
Project project = await GetProjectByIdAsync(id);
|
||||||
|
if (project == null)
|
||||||
|
{ return NotFound(); }
|
||||||
|
return project.GetMembers();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}/members")]
|
||||||
|
public async Task<ActionResult<Project>> SetProjectMembers(int id, List<AppUser> projectMembers)
|
||||||
|
{
|
||||||
|
Project project = await GetProjectByIdAsync(id);
|
||||||
|
project.SetMembers(projectMembers);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateException /* ex */)
|
||||||
|
{
|
||||||
|
//Log the error (uncomment ex variable name and write a log.)
|
||||||
|
ModelState.AddModelError("", "Unable to save changes. " +
|
||||||
|
"Try again, and if the problem persists, " +
|
||||||
|
"see your system administrator.");
|
||||||
|
}
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}/addMembers")]
|
||||||
|
public async Task<ActionResult<Project>> AddMembersToProject(int id, List<AppUser> usersToAdd)
|
||||||
|
{
|
||||||
|
if (usersToAdd == null)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
Project project = await GetProjectByIdAsync(id);
|
||||||
|
project.AddMembers(usersToAdd);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateException /* ex */)
|
||||||
|
{
|
||||||
|
//Log the error (uncomment ex variable name and write a log.)
|
||||||
|
ModelState.AddModelError("", "Unable to save changes. " +
|
||||||
|
"Try again, and if the problem persists, " +
|
||||||
|
"see your system administrator.");
|
||||||
|
}
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}/removeMembers")]
|
||||||
|
public async Task<ActionResult<Project>> RemoveMembersFromProject(int id, List<AppUser> usersToRemove)
|
||||||
|
{
|
||||||
|
Project project = await GetProjectByIdAsync(id);
|
||||||
|
project.RemoveMembers(usersToRemove);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateException /* ex */)
|
||||||
|
{
|
||||||
|
//Log the error (uncomment ex variable name and write a log.)
|
||||||
|
ModelState.AddModelError("", "Unable to save changes. " +
|
||||||
|
"Try again, and if the problem persists, " +
|
||||||
|
"see your system administrator.");
|
||||||
|
}
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
private bool ProjectExists(int id)
|
private bool ProjectExists(int id)
|
||||||
{
|
{
|
||||||
|
|
@ -166,5 +262,26 @@ namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
return _context.Assignments.Any(e => e.ProjectId == id);
|
return _context.Assignments.Any(e => e.ProjectId == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<ActionResult<IEnumerable<Project>>> GetAllProjectsAsync()
|
||||||
|
{
|
||||||
|
return await makeProjectsQueryAsync()
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
private async Task<Project> GetProjectByIdAsync(int id)
|
||||||
|
{
|
||||||
|
return await makeProjectsQueryAsync()
|
||||||
|
.FirstOrDefaultAsync(p => p.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<Project> makeProjectsQueryAsync()
|
||||||
|
{
|
||||||
|
return _context.Projects
|
||||||
|
.Include(p => p.Assignments)
|
||||||
|
.ThenInclude(a => a.User)
|
||||||
|
.Include(p => p.Tickets)
|
||||||
|
.Include(p => p.Manager)
|
||||||
|
.Include(p => p.Files);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ using TicketManager.Models;
|
||||||
|
|
||||||
namespace TicketManager.Controllers
|
namespace TicketManager.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/[controller]")]
|
[Route("api/v1/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class TicketsController : ControllerBase
|
public class TicketsController : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
@ -25,25 +25,14 @@ namespace TicketManager.Controllers
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<IEnumerable<Ticket>>> GetTickets()
|
public async Task<ActionResult<IEnumerable<Ticket>>> GetTickets()
|
||||||
{
|
{
|
||||||
return await _context.Tickets
|
return await getAllTicketsAsync();
|
||||||
.Include(t => t.Creator)
|
|
||||||
.Include(p => p.Notes)
|
|
||||||
.Include(p => p.Edits)
|
|
||||||
.Include(p => p.Files)
|
|
||||||
.AsNoTracking()
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/Tickets/5
|
// GET: api/Tickets/5
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<ActionResult<Ticket>> GetTicket(int id)
|
public async Task<ActionResult<Ticket>> GetTicket(int id)
|
||||||
{
|
{
|
||||||
var ticket = await _context.Tickets
|
var ticket = await getTicketByIdAsync(id);
|
||||||
.Include(t => t.Creator)
|
|
||||||
.Include(p => p.Notes)
|
|
||||||
.Include(p => p.Edits)
|
|
||||||
.Include(p => p.Files)
|
|
||||||
.FirstOrDefaultAsync(t => t.Id == id);
|
|
||||||
|
|
||||||
if (ticket == null)
|
if (ticket == null)
|
||||||
{
|
{
|
||||||
|
|
@ -113,9 +102,47 @@ namespace TicketManager.Controllers
|
||||||
return ticket;
|
return ticket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/assignees")]
|
||||||
|
public async Task<ActionResult<List<AppUser>>> GetTicketAssignees(int id)
|
||||||
|
{
|
||||||
|
Ticket ticket = await getTicketByIdAsync(id);
|
||||||
|
return ticket.GetAssignees();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}/closed")]
|
||||||
|
public async Task<ActionResult> CloseTicket(int id)
|
||||||
|
{
|
||||||
|
Ticket ticket = await getTicketByIdAsync(id);
|
||||||
|
ticket.Close();
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
private bool TicketExists(int id)
|
private bool TicketExists(int id)
|
||||||
{
|
{
|
||||||
return _context.Tickets.Any(e => e.Id == id);
|
return _context.Tickets.Any(e => e.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IQueryable<Ticket> ticketQuery() // problem with link
|
||||||
|
{
|
||||||
|
return _context.Tickets
|
||||||
|
.Include(p => p.Project)
|
||||||
|
.ThenInclude(a => a.Assignments)
|
||||||
|
.ThenInclude(p => p.User)
|
||||||
|
// .Include(p => p.Edits)
|
||||||
|
// .Include(p => p.Notes)
|
||||||
|
// .Include(p => p.Files)
|
||||||
|
.Include(p => p.Creator)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ActionResult<IEnumerable<Ticket>>> getAllTicketsAsync()
|
||||||
|
{
|
||||||
|
return await ticketQuery().ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Ticket> getTicketByIdAsync(int id)
|
||||||
|
{
|
||||||
|
return await ticketQuery().FirstOrDefaultAsync(a => a.Id == id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace TicketManager.Data
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public DbSet<Project> Projects { get; set; }
|
public DbSet<Project> Projects { get; set; }
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<AppUser> AppUsers { get; set; }
|
||||||
public DbSet<Ticket> Tickets { get; set; }
|
public DbSet<Ticket> Tickets { get; set; }
|
||||||
public DbSet<Assignment> Assignments { get; set; }
|
public DbSet<Assignment> Assignments { get; set; }
|
||||||
public DbSet<History> Edits { get; set; }
|
public DbSet<History> Edits { get; set; }
|
||||||
|
|
@ -20,8 +20,6 @@ namespace TicketManager.Data
|
||||||
{
|
{
|
||||||
base.OnModelCreating(builder);
|
base.OnModelCreating(builder);
|
||||||
builder.Entity<Assignment>().HasKey(a => new { a.ProjectId, a.UserId });
|
builder.Entity<Assignment>().HasKey(a => new { a.ProjectId, a.UserId });
|
||||||
builder.Entity<Assignment>().HasOne(a => a.Project).WithMany(p => p.Assignments).HasForeignKey(a => a.ProjectId);
|
|
||||||
builder.Entity<Assignment>().HasOne(a => a.User).WithMany(u => u.Assignments).HasForeignKey(a => a.UserId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
70
Models/AppUser.cs
Normal file
70
Models/AppUser.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace TicketManager.Models
|
||||||
|
{
|
||||||
|
public class AppUser
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(50)]
|
||||||
|
[Display(Name = "First Name")]
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(50)]
|
||||||
|
[Display(Name = "Last Name")]
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Full Name")]
|
||||||
|
public string FullName => $"{FirstName} {LastName}";
|
||||||
|
|
||||||
|
[StringLength(200)]
|
||||||
|
[Display(Name = "Bio")]
|
||||||
|
public string Presentation { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.EmailAddress)]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.PhoneNumber)]
|
||||||
|
public string Phone { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Date)]
|
||||||
|
[Display(Name = "Member since"), DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
|
||||||
|
public DateTime Created_at { get; private set; } = DateTime.Now;
|
||||||
|
|
||||||
|
// [Display(Name = "Avatar")]
|
||||||
|
// public byte[] Picture { get; set; }
|
||||||
|
// public Role Role { get; set; }
|
||||||
|
|
||||||
|
public List<Assignment> Assignments { get; set; } = new List<Assignment>();
|
||||||
|
|
||||||
|
[Display(Name = "Activity")]
|
||||||
|
public List<History> Edits { get; set; } = new List<History>();
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
public List<Project> GetProjects()
|
||||||
|
{
|
||||||
|
return Assignments.Select(a => a.Project).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Project GetProject(int id)
|
||||||
|
// {
|
||||||
|
// return Assignments.Single(a => a.Project.Id == id).Project;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public List<AppUser> GetProjectMembers(int id)
|
||||||
|
// {
|
||||||
|
// return GetProject(id).GetMembers();
|
||||||
|
// }
|
||||||
|
public List<Ticket> GetTickets()
|
||||||
|
{
|
||||||
|
List<Ticket> tickets = new List<Ticket>();
|
||||||
|
GetProjects().ForEach(p => tickets.Concat(p.Tickets));
|
||||||
|
return tickets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,8 @@ namespace TicketManager.Models
|
||||||
{
|
{
|
||||||
public class Assignment
|
public class Assignment
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
// public int Id { get; set; }
|
||||||
public User User { get; set; }
|
public AppUser User { get; set; }
|
||||||
public Guid UserId { get; set; }
|
public Guid UserId { get; set; }
|
||||||
public Project Project { get; set; }
|
public Project Project { get; set; }
|
||||||
public int ProjectId { get; set; }
|
public int ProjectId { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,27 @@ namespace TicketManager.Models
|
||||||
{
|
{
|
||||||
public class File
|
public class File
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
|
||||||
public string Location { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public int Size { get; set; }
|
|
||||||
public string Format { get; set; }
|
|
||||||
|
|
||||||
public User AddedBy { get; set; }
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
private string _location;
|
||||||
|
public string Location
|
||||||
|
{
|
||||||
|
get { return _location; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
string filesUrl = "";
|
||||||
|
_location = $"{filesUrl}/{FileName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Description { get; set; }
|
||||||
|
public int Size { get; set; } // deduce auto from FileName
|
||||||
|
public string Format { get; set; } // deduce auto from FileName
|
||||||
|
|
||||||
|
public AppUser AddedBy { get; set; }
|
||||||
public int UserId { get; set; }
|
public int UserId { get; set; }
|
||||||
// public ITask AddedTo { get; set; }
|
// public ITask AddedTo { get; set; }
|
||||||
// public int ITaskId { get; set; }
|
// public int ITaskId { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,10 @@ namespace TicketManager.Models
|
||||||
public class History
|
public class History
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Title { get; set; }
|
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public DateTime UpdateDate { get; } = DateTime.Now;
|
public DateTime UpdateDate { get; set; } = DateTime.Now;
|
||||||
public ActivityType ActivityType { get; set; } = ActivityType.Undefined;
|
public ActivityType ActivityType { get; set; } = ActivityType.Undefined;
|
||||||
|
public AppUser User { get; set; }
|
||||||
public User User { get; set; }
|
|
||||||
public int UserId { get; set; }
|
public int UserId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,28 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace TicketManager.Models
|
namespace TicketManager.Models
|
||||||
{
|
{
|
||||||
public interface ITask
|
// public interface ITask
|
||||||
|
public abstract class ITask
|
||||||
{
|
{
|
||||||
int Id { get; set; }
|
int Id { get; set; }
|
||||||
string Title { get; set; }
|
string Title { get; set; }
|
||||||
string Description { get; set; }
|
string Description { get; set; }
|
||||||
DateTime CreatedAt { get; }
|
DateTime CreatedAt { get; }
|
||||||
DateTime PlannedEnding { get; set; }
|
DateTime PlannedEnding { get; set; }
|
||||||
|
List<History> Edits { get; set; }
|
||||||
|
|
||||||
|
public virtual void AddLogEntry(string description)//, User user)
|
||||||
|
{
|
||||||
|
History Edit = new History()
|
||||||
|
{
|
||||||
|
Description = description,
|
||||||
|
ActivityType = ActivityType.Undefined,
|
||||||
|
// User = user,
|
||||||
|
UpdateDate = DateTime.Now
|
||||||
|
};
|
||||||
|
Edits.Add(Edit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ namespace TicketManager.Models
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public DateTime Created_at { get; } = DateTime.Now;
|
public DateTime Created_at { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
public Ticket Ticket { get; set; }
|
public Ticket Ticket { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,68 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace TicketManager.Models
|
namespace TicketManager.Models
|
||||||
{
|
{
|
||||||
public class Project : ITask
|
public class Project : ITask
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(50)]
|
||||||
|
[Display(Name = "Title")]
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
[StringLength(200)]
|
||||||
|
[Display(Name = "Short Description")]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public DateTime CreatedAt { get; } = DateTime.Now;
|
|
||||||
|
[DataType(DataType.Date)]
|
||||||
|
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = false)]
|
||||||
|
public DateTime CreatedAt { get; private set; } = DateTime.Now;
|
||||||
|
|
||||||
|
[DataType(DataType.Date)]
|
||||||
|
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
|
||||||
public DateTime PlannedEnding { get; set; }
|
public DateTime PlannedEnding { get; set; }
|
||||||
public float Progression
|
|
||||||
|
private decimal _progression;
|
||||||
|
[Display(Name = "Progress")]
|
||||||
|
public decimal Progression
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return this.Tickets.Count() == 0 ? 0 : (float)this.Tickets.
|
return _progression;
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_progression = Tickets.Count() == 0 ? 0 :
|
||||||
|
(decimal)this.Tickets.
|
||||||
Where(t => t.Status == Status.Done).Count()
|
Where(t => t.Status == Status.Done).Count()
|
||||||
/ this.Tickets.Count()
|
/ this.Tickets.Count() * 100;
|
||||||
* 100;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Display(Name = "Project Status")]
|
||||||
public Status Status { get; set; } = Status.ToDo;
|
public Status Status { get; set; } = Status.ToDo;
|
||||||
|
|
||||||
public User Manager { get; set; }
|
[Display(Name = "Project Manager")]
|
||||||
public Guid ManagerId { get; set; }
|
public AppUser Manager { get; set; }
|
||||||
private List<Assignment> _assignments;
|
|
||||||
public List<Assignment> Assignments
|
public List<Assignment> Assignments { get; set; } = new List<Assignment>();
|
||||||
{
|
|
||||||
get
|
public List<Ticket> Tickets { get; set; } = new List<Ticket>();
|
||||||
{ return _assignments ?? new List<Assignment>(); }
|
|
||||||
set
|
public List<History> Edits { get; set; } = new List<History>();
|
||||||
{ _assignments = value; }
|
|
||||||
}
|
public List<File> Files { get; set; } = new List<File>();
|
||||||
private List<Ticket> _tickets;
|
|
||||||
public List<Ticket> Tickets
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{ return _tickets ?? new List<Ticket>(); }
|
|
||||||
set { _tickets = value; }
|
|
||||||
}
|
|
||||||
// private List<History> _edits;
|
|
||||||
// public List<History> Edits
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// { return _edits ?? new List<History>(); }
|
|
||||||
// set { _edits = value; }
|
|
||||||
// }
|
|
||||||
private List<File> _files;
|
|
||||||
public List<File> Files
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{ return _files ?? new List<File>(); }
|
|
||||||
set { _files = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
public List<User> GetMembers()
|
public List<AppUser> GetMembers()
|
||||||
{
|
{
|
||||||
return this.Assignments.Select(a => a.User).ToList();
|
return this.Assignments.Select(a => a.User).ToList();
|
||||||
}
|
}
|
||||||
public void AddMembers(List<User> usersToAdd)
|
public void AddMembers(List<AppUser> usersToAdd)
|
||||||
{
|
{
|
||||||
var projectUsers = new List<Guid>
|
var projectUsers = new List<Guid>
|
||||||
(this.Assignments.Select(a => a.UserId));
|
(this.Assignments.Select(a => a.UserId));
|
||||||
|
|
@ -76,13 +78,37 @@ namespace TicketManager.Models
|
||||||
UserId = user.Id
|
UserId = user.Id
|
||||||
};
|
};
|
||||||
this.Assignments.Add(newAssign);
|
this.Assignments.Add(newAssign);
|
||||||
|
// AddLogEntry(this, " joined the project.");
|
||||||
}
|
}
|
||||||
// return this.Assignments;
|
// return this.Assignments;
|
||||||
}
|
}
|
||||||
public void RemoveMembers(List<User> membersToRemove)
|
public void RemoveMembers(List<AppUser> membersToRemove)
|
||||||
{
|
{
|
||||||
this.Assignments.RemoveAll(a => membersToRemove.Contains(a.User));
|
this.Assignments.RemoveAll(a => membersToRemove.Contains(a.User));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetMembers(List<AppUser> projectMembers)
|
||||||
|
{
|
||||||
|
var currentProjectMembers = this.GetMembers();
|
||||||
|
if (currentProjectMembers != null)
|
||||||
|
{
|
||||||
|
var membersToRemove = currentProjectMembers
|
||||||
|
.FindAll(
|
||||||
|
cp => !projectMembers.Contains(cp)
|
||||||
|
);
|
||||||
|
this.RemoveMembers(membersToRemove);
|
||||||
|
|
||||||
|
var membersToAdd = projectMembers.FindAll(
|
||||||
|
pm => !currentProjectMembers.Contains(pm)
|
||||||
|
);
|
||||||
|
this.AddMembers(membersToAdd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.AddMembers(projectMembers);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
public int GetMembersCount() => this.GetMembers().Count();
|
public int GetMembersCount() => this.GetMembers().Count();
|
||||||
public void GetTicketsCount() => this.Tickets.Count();
|
public void GetTicketsCount() => this.Tickets.Count();
|
||||||
public void GetTicketsUpdates()
|
public void GetTicketsUpdates()
|
||||||
|
|
@ -91,5 +117,17 @@ namespace TicketManager.Models
|
||||||
{
|
{
|
||||||
this.Status = Status.Done;
|
this.Status = Status.Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private void AddLogEntry(string description)//, User user)
|
||||||
|
// {
|
||||||
|
// History Edit = new History()
|
||||||
|
// {
|
||||||
|
// Description = description,
|
||||||
|
// ActivityType = ActivityType.Undefined,
|
||||||
|
// // User = user,
|
||||||
|
// UpdateDate = DateTime.Now
|
||||||
|
// };
|
||||||
|
// this.Edits.Add(Edit);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,27 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace TicketManager.Models
|
namespace TicketManager.Models
|
||||||
{
|
{
|
||||||
public class Ticket : ITask
|
public class Ticket : ITask
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100)]
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
[StringLength(100)]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public DateTime CreatedAt { get; } = DateTime.Now;
|
|
||||||
|
[DataType(DataType.Date)]
|
||||||
|
[Display(Name = "Creation Date"), DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
|
||||||
|
public DateTime CreatedAt { get; private set; } = DateTime.Now;
|
||||||
|
|
||||||
|
[DataType(DataType.Date)]
|
||||||
|
[Display(Name = "Estimated Ending Date"), DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
|
||||||
public DateTime PlannedEnding { get; set; }
|
public DateTime PlannedEnding { get; set; }
|
||||||
|
|
||||||
public Status Status { get; set; } = Status.ToDo;
|
public Status Status { get; set; } = Status.ToDo;
|
||||||
|
|
@ -16,42 +29,28 @@ namespace TicketManager.Models
|
||||||
public Difficulty Difficulty { get; set; } = Difficulty.Undefined;
|
public Difficulty Difficulty { get; set; } = Difficulty.Undefined;
|
||||||
public Category Category { get; set; } = Category.Undefined;
|
public Category Category { get; set; } = Category.Undefined;
|
||||||
|
|
||||||
public User Creator { get; set; }
|
[Display(Name = "Created By")]
|
||||||
|
public AppUser Creator { get; set; }
|
||||||
public Guid CreatorId { get; set; }
|
public Guid CreatorId { get; set; }
|
||||||
// public Project Project { get; set; }
|
|
||||||
// public int ProjectId { get; set; }
|
[Display(Name = "Project")]
|
||||||
private List<Note> _notes;
|
public Project Project { get; set; }
|
||||||
public List<Note> Notes
|
public int ProjectId { get; set; }
|
||||||
{
|
public List<Note> Notes = new List<Note>();
|
||||||
get
|
|
||||||
{
|
public List<History> Edits = new List<History>();
|
||||||
return _notes ?? new List<Note>();
|
|
||||||
}
|
public List<File> Files = new List<File>();
|
||||||
set { _notes = value; }
|
|
||||||
}
|
|
||||||
private List<History> _edits;
|
|
||||||
public List<History> Edits
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _edits ?? new List<History>();
|
|
||||||
}
|
|
||||||
set { _edits = value; }
|
|
||||||
}
|
|
||||||
private List<File> _files;
|
|
||||||
public List<File> Files
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _files ?? new List<File>();
|
|
||||||
}
|
|
||||||
set { _files = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
public void GetAssignees() { throw new NotImplementedException("Not Implemented"); }
|
public List<AppUser> GetAssignees()
|
||||||
|
{
|
||||||
|
return Project.Assignments.Select(a => a.User).ToList();
|
||||||
|
}
|
||||||
public void GetLastUpdateTime() { throw new NotImplementedException("Not Implemented"); }
|
public void GetLastUpdateTime() { throw new NotImplementedException("Not Implemented"); }
|
||||||
public void Close() { throw new NotImplementedException("Not Implemented"); }
|
public void Close()
|
||||||
public void AddFile() { throw new NotImplementedException("Not Implemented"); }
|
{
|
||||||
|
Status = Status.Done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace TicketManager
|
namespace TicketManager
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CS1591
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
|
|
@ -23,4 +24,5 @@ namespace TicketManager
|
||||||
webBuilder.UseStartup<Startup>();
|
webBuilder.UseStartup<Startup>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CS1591
|
||||||
}
|
}
|
||||||
|
|
|
||||||
37
README.md
37
README.md
|
|
@ -4,7 +4,42 @@
|
||||||
|
|
||||||
- [Follow the link](https://docs.google.com/presentation/d/1Gunf5MRJ_KcoFwo0x_vV8YVHnf9l0V8n7BiJGz6p4cI/edit?usp=sharing)
|
- [Follow the link](https://docs.google.com/presentation/d/1Gunf5MRJ_KcoFwo0x_vV8YVHnf9l0V8n7BiJGz6p4cI/edit?usp=sharing)
|
||||||
|
|
||||||
## ToDo
|
## API Documentation
|
||||||
|
|
||||||
|
### v1
|
||||||
|
|
||||||
|
- [Internal Link. Don't forget to update](https://localhost:5001/swagger)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
## Supports
|
||||||
|
|
||||||
|
- Web
|
||||||
|
- Progressive Web App
|
||||||
|
- Mobile
|
||||||
|
|
||||||
|
## Technical Stack
|
||||||
|
|
||||||
|
- `React` client on the front-end (TypeScript)
|
||||||
|
- [Materialize](https://materializecss.com) CSS librairy for styling
|
||||||
|
- API: Newtonsoft.Json, to avoid cycle errors
|
||||||
|
- Hosting: ?
|
||||||
|
- Authentication : [Auth0](https://auth0.com/)
|
||||||
|
- Analytics : Google Analytics & Mixpanel
|
||||||
|
|
||||||
|
## Versions
|
||||||
|
|
||||||
|
### Features in v.0.1
|
||||||
|
|
||||||
|
## TO DO
|
||||||
|
|
||||||
|
- Write API tests using Postman: request + test, environment variables, mock server
|
||||||
|
- Annotate API request in controllers
|
||||||
|
- Annotate Properties in Models
|
||||||
- Write backend tests
|
- Write backend tests
|
||||||
- Have a Look at typeahead component
|
- Have a Look at typeahead component
|
||||||
|
- Ensure Tickets Edits belong to Project Edits
|
||||||
|
- Ensure Tickets Files belong to Project Files
|
||||||
|
- Write a query class to refactor code and optimize perf on get queries (AsNoTracking)
|
||||||
|
- Async model methods ?
|
||||||
|
- setMembers & removeMembers from project api not working
|
||||||
|
|
|
||||||
26
Startup.cs
26
Startup.cs
|
|
@ -17,7 +17,10 @@ using System.Reflection;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TicketManager.Data;
|
using TicketManager.Data;
|
||||||
using TicketManager.Models;
|
using TicketManager.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
[assembly: ApiController]
|
||||||
namespace TicketManager
|
namespace TicketManager
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
|
|
@ -34,7 +37,12 @@ namespace TicketManager
|
||||||
{
|
{
|
||||||
services.AddDbContext<AppDbContext>(options =>
|
services.AddDbContext<AppDbContext>(options =>
|
||||||
options.UseSqlite(Configuration.GetConnectionString("Sqlite")));
|
options.UseSqlite(Configuration.GetConnectionString("Sqlite")));
|
||||||
services.AddControllers();
|
services.AddControllers()
|
||||||
|
.AddNewtonsoftJson(options =>
|
||||||
|
{
|
||||||
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // avoid cycle ref errors
|
||||||
|
});
|
||||||
|
|
||||||
services.AddSpaStaticFiles(configuration =>
|
services.AddSpaStaticFiles(configuration =>
|
||||||
{
|
{
|
||||||
configuration.RootPath = "client/build";
|
configuration.RootPath = "client/build";
|
||||||
|
|
@ -45,7 +53,7 @@ namespace TicketManager
|
||||||
{
|
{
|
||||||
Version = "v1",
|
Version = "v1",
|
||||||
Title = "Ticket Manager API",
|
Title = "Ticket Manager API",
|
||||||
Description = "A simple example ASP.NET Core Web API",
|
Description = "Ticket Manger API for Teams",
|
||||||
Contact = new OpenApiContact
|
Contact = new OpenApiContact
|
||||||
{
|
{
|
||||||
Name = "Ruidy Nemausat",
|
Name = "Ruidy Nemausat",
|
||||||
|
|
@ -53,12 +61,12 @@ namespace TicketManager
|
||||||
Url = new Uri("https://ruidywebsite.herokuapp.com/"),
|
Url = new Uri("https://ruidywebsite.herokuapp.com/"),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the comments path for the Swagger JSON and UI.
|
// Set the comments path for the Swagger JSON and UI.
|
||||||
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||||
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
||||||
c.IncludeXmlComments(xmlPath);
|
c.IncludeXmlComments(xmlPath);
|
||||||
});
|
});
|
||||||
|
services.AddSwaggerGenNewtonsoftSupport(); // explicit opt-in - needs to be placed after AddSwaggerGen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -70,19 +78,23 @@ namespace TicketManager
|
||||||
{
|
{
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
app.UseDefaultFiles();
|
app.UseDefaultFiles();
|
||||||
app.UseStaticFiles();
|
|
||||||
|
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
|
|
||||||
app.UseSwaggerUI(c =>
|
app.UseSwaggerUI(c =>
|
||||||
{
|
{
|
||||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ticket Manager API V1");
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ticket Manager API v1");
|
||||||
});
|
});
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
app.UseSpaStaticFiles();
|
app.UseSpaStaticFiles();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="3.1.0" />
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.0" />
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
|
||||||
|
|
@ -27,14 +28,10 @@
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.OpenApi" Version="1.1.4" />
|
<PackageReference Include="Microsoft.OpenApi" Version="1.1.4" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc5" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="client\" />
|
<Folder Include="client\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="WeatherForecast.cs" />
|
|
||||||
<Compile Remove="Controllers\WeatherForecastController.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"https_port": 443,
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue