applying dto pattern

This commit is contained in:
Ruidy Nemausat 2020-02-26 22:45:52 +01:00
parent eec20793bc
commit deb3492475
18 changed files with 102 additions and 117 deletions

View file

@ -70,14 +70,13 @@ namespace TicketManager.Controllers
.ThenInclude(a => a.Project)
.Include(u => u.Activities)
.AsNoTracking()
.Select(u => new AppUserDTO(u))
.FirstOrDefaultAsync(u => u.Id == id);
if (user == null)
{
return NotFound();
}
return user;
return new AppUserDTO(user);
}
/// <summary>

View file

@ -41,7 +41,7 @@ namespace TicketManager.Controllers
{
return await _context.Projects
.Include(p => p.Assignments)
.ThenInclude(a => a.User)
.ThenInclude(a => a.User)
.Include(p => p.Tickets)
.Include(p => p.Manager)
.Include(p => p.Files)
@ -76,14 +76,13 @@ namespace TicketManager.Controllers
.Include(p => p.Files)
.Include(p => p.Activities)
.AsNoTracking()
.Select(p => new ProjectDTO(p))
.FirstOrDefaultAsync(p => p.Id == id);
if (project == null)
{
return NotFound();
}
return project;
return new ProjectDTO(project);
}
/// <summary>
@ -247,7 +246,7 @@ namespace TicketManager.Controllers
public async Task<ActionResult<Project>> SetProjectMembers(int id, List<AppUser> projectMembers)
{
Project project = await _context.Projects
.Include(p => p.Assignments)
// .Include(p => p.Assignments)
.FirstOrDefaultAsync(p => p.Id == id);
if (project == null)

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using TicketManager.Models;
namespace TicketManager.DTO
@ -15,11 +16,11 @@ namespace TicketManager.DTO
Presentation = user.Presentation;
Email = user.Email;
Phone = user.Phone;
Created_at = user.Created_at;
CreationDate = user.CreationDate;
Picture = user.Picture;
Activities = user.Activities;
Projects = user.GetProjects();
Tickets = user.GetTickets();
Projects = user.GetProjects().Select(u => new ProjectDTO(u)).ToList();
Tickets = user.GetTickets().Select(u => new TicketDTO(u)).ToList();
}
public Guid Id { get; set; }
@ -39,14 +40,14 @@ namespace TicketManager.DTO
public string Phone { get; set; }
[DataType(DataType.Date)]
public DateTime Created_at { get; private set; } = DateTime.Now;
public DateTime CreationDate { get; private set; } = DateTime.Now;
public string Picture { get; set; }
public List<Activity> Activities { get; set; } = new List<Activity>();
public List<Project> Projects { get; set; } = new List<Project>();
public List<ProjectDTO> Projects { get; set; } = new List<ProjectDTO>();
public List<Ticket> Tickets { get; set; } = new List<Ticket>();
public List<TicketDTO> Tickets { get; set; } = new List<TicketDTO>();
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;
using TicketManager.Models;
@ -11,12 +12,13 @@ namespace TicketManager.DTO
Id = project.Id;
Title = project.Title;
Description = project.Description;
CreatedAt = project.CreatedAt;
CreationDate = project.CreationDate;
EndingDate = project.EndingDate;
Progression = project.Progression;
Status = project.Status.ToString();
Manager = project.Manager;
Users = project.GetMembers();
Tickets = project.Tickets;
// Manager = project.Manager != null ? new AppUserDTO(project.Manager) : null;
Users = project.GetMembers().Select(u => new AppUserDTO(u)).ToList();
Tickets = project.Tickets.Select(t => new TicketDTO(t)).ToList();
Activities = project.Activities;
Files = project.Files;
}
@ -27,19 +29,19 @@ namespace TicketManager.DTO
public string Description { get; set; }
public DateTime CreatedAt { get; private set; } = DateTime.Now;
public DateTime CreationDate { get; private set; } = DateTime.Now;
public DateTime PlannedEnding { get; set; }
public DateTime EndingDate { get; set; }
public decimal Progression { get; set; }
public string Status { get; set; }
public AppUser Manager { get; set; }
public AppUserDTO Manager { get; set; }
public List<AppUser> Users { get; set; } = new List<AppUser>();
public List<AppUserDTO> Users { get; set; } = new List<AppUserDTO>();
public List<Ticket> Tickets { get; set; } = new List<Ticket>();
public List<TicketDTO> Tickets { get; set; } = new List<TicketDTO>();
public List<Activity> Activities { get; set; } = new List<Activity>();

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using TicketManager.Models;
namespace TicketManager.DTO
@ -12,19 +13,20 @@ namespace TicketManager.DTO
Id = ticket.Id;
Title = ticket.Title;
Description = ticket.Description;
CreatedAt = ticket.CreatedAt;
PlannedEnding = ticket.PlannedEnding;
CreationDate = ticket.CreationDate;
EndingDate = ticket.EndingDate;
Status = ticket.Status.ToString();
Impact = ticket.Impact.ToString();
Difficulty = ticket.Difficulty.ToString();
Category = ticket.Category.ToString();
CreatorId = ticket.CreatorId;
Project = ticket.Project;
Project = new ProjectDTO(ticket.Project);
Notes = ticket.Notes;
Activities = ticket.Activities;
Files = ticket.Files;
Users = ticket.GetAssignees();
Users = ticket.GetAssignees().Select(u => new AppUserDTO(u)).ToList();
}
public int Id { get; set; }
public string Title { get; set; }
@ -32,10 +34,10 @@ namespace TicketManager.DTO
public string Description { get; set; }
[DataType(DataType.Date)]
public DateTime CreatedAt { get; private set; }
public DateTime CreationDate { get; private set; }
[DataType(DataType.Date)]
public DateTime PlannedEnding { get; set; }
public DateTime EndingDate { get; set; }
public string Status { get; set; }
@ -47,7 +49,7 @@ namespace TicketManager.DTO
public Guid CreatorId { get; set; }
public Project Project { get; set; }
public ProjectDTO Project { get; set; }
public List<Note> Notes { get; set; } = new List<Note>();
@ -55,6 +57,6 @@ namespace TicketManager.DTO
public List<File> Files { get; set; } = new List<File>();
public List<AppUser> Users { get; set; } = new List<AppUser>();
public List<AppUserDTO> Users { get; set; } = new List<AppUserDTO>();
}
}

View file

@ -34,7 +34,7 @@ namespace TicketManager.Models
[DataType(DataType.Date)]
[Display(Name = "Member since"), DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Created_at { get; private set; } = DateTime.Now;
public DateTime CreationDate { get; private set; } = DateTime.Now;
[Display(Name = "Avatar")]
public string Picture { get; set; }

View file

@ -20,11 +20,11 @@ namespace TicketManager.Models
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = false)]
public DateTime CreatedAt { get; private set; } = DateTime.Now;
public DateTime CreationDate { get; private set; } = DateTime.Now;
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime PlannedEnding { get; set; }
public DateTime EndingDate { get; set; }
[Display(Name = "Progress")]
public decimal Progression

View file

@ -18,11 +18,11 @@ namespace TicketManager.Models
[DataType(DataType.Date)]
[Display(Name = "Creation Date"), DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime CreatedAt { get; private set; } = DateTime.Now;
public DateTime CreationDate { 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 EndingDate { get; set; }
public Status Status { get; set; } = Status.ToDo;
public Impact Impact { get; set; } = Impact.Undefined;

View file

@ -48,3 +48,4 @@
- [ ] error page redirect when offline.
- [ ] ticket/files/activities list placeholders when empty
- [ ] think about public/private DTO's constructor, getters and setters
- [ ] write dtos without circular dependencies

View file

@ -1,47 +0,0 @@
{
"activities" : [],
"plannedEnding" : "0001-01-01T00:00:00",
"id" : 1,
"title" : "Secret Project",
"createdAt" : "2020-02-24T10:34:18.428046",
"users" : [
{
"firstName" : "Thomas",
"phone" : "0198237645",
"lastName" : "Price",
"created_at" : "2020-02-25T09:42:54.462374",
"presentation" : "New Team?!",
"email" : "tp@mail.com",
"picture" : null,
"activities" : [],
"id" : "357727fd-5262-4522-b8a3-38271d43de84",
"fullName" : "Thomas Price",
"assignments" : [
{
"project" : {
"assignments" : [],
"createdAt" : "2020-02-24T10:34:18.428046",
"title" : "Secret Project",
"id" : 1,
"plannedEnding" : "2020-02-17T15:51:02.787373",
"activities" : [],
"description" : "Shhttt Don't tell anyone",
"status" : 1,
"files" : [],
"tickets" : [],
"progression" : 0,
"manager" : null
},
"userId" : "357727fd-5262-4522-b8a3-38271d43de84",
"projectId" : 1
}
]
}
],
"manager" : null,
"progression" : 0,
"tickets" : [],
"files" : [],
"status" : "ToDo",
"description" : "Shhttt Don't tell anyone"
}

View file

@ -37,9 +37,6 @@ namespace TicketManager
options.EnableSensitiveDataLogging(true); //Remove in production.
}
);
// services.AddScoped<IProjectRepository, ProjectRepository>();
// services.AddScoped<IAppUserRepository, AppUserRepository>();
// services.AddScoped<ITicketRepository, TicketRepository>();
services.AddAuthentication(options =>
{
@ -49,16 +46,14 @@ namespace TicketManager
{
options.Authority = "https://dev-fyjrvohx.auth0.com/";
options.Audience = "https://localhost:5001/api/V1/";
//options.Authority = $"https://{Configuration["Auth0:Domain"]}/";
//options.Audience = Configuration["Auth0:Audience"];
});
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // avoid cycle ref errors
}
);
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // avoid cycle ref errors
}
);
services.AddSpaStaticFiles(configuration =>
{

View file

@ -128,13 +128,13 @@ namespace TicketManager.Tests
Id = 1,
Title = "Top Secret Project",
Description = "Shht Don't Ask don't tell",
PlannedEnding = new DateTime(2020, 7, 21)
EndingDate = new DateTime(2020, 7, 21)
}
);
// Should Update
Assert.Equal("Top Secret Project", context.Projects.Find(1).Title);
Assert.Equal(new DateTime(2020, 7, 21), context.Projects.Find(1).PlannedEnding);
Assert.Equal(new DateTime(2020, 7, 21), context.Projects.Find(1).EndingDate);
Assert.IsType<NoContentResult>(result);
@ -144,13 +144,13 @@ namespace TicketManager.Tests
Id = 1,
Title = "Top Secret Project",
Description = "Shht Don't Ask don't tell",
PlannedEnding = new DateTime(2020, 7, 21)
EndingDate = new DateTime(2020, 7, 21)
}
);
// Should Return BadRequest
Assert.NotEqual("Top Secret Project", context.Projects.Find(2).Title);
Assert.NotEqual(new DateTime(2020, 7, 21), context.Projects.Find(2).CreatedAt);
Assert.NotEqual(new DateTime(2020, 7, 21), context.Projects.Find(2).CreationDate);
Assert.IsType<BadRequestResult>(result);
// Delete updated project
@ -163,14 +163,14 @@ namespace TicketManager.Tests
Id = 1,
Title = "Top Secret Project",
Description = "Shht Don't Ask don't tell",
PlannedEnding = new DateTime(2020, 7, 21)
EndingDate = new DateTime(2020, 7, 21)
}
);
// Should Throw
Assert.IsType<NotFoundResult>(result);
Assert.Equal("Top Secret Project", context.Projects.Find(1).Title);
Assert.Equal(new DateTime(2020, 7, 21), context.Projects.Find(1).PlannedEnding);
Assert.Equal(new DateTime(2020, 7, 21), context.Projects.Find(1).EndingDate);
}
}
@ -209,7 +209,7 @@ namespace TicketManager.Tests
{
Title = "The Third",
Description = "Thrice in a row",
PlannedEnding = DateTime.Now
EndingDate = DateTime.Now
};
var controller = new ProjectsController(context);
@ -237,14 +237,14 @@ namespace TicketManager.Tests
Id = 1,
Title = "Secret Project",
Description = "Shht Don't Ask don't tell",
PlannedEnding = new DateTime(2021, 7, 21)
EndingDate = new DateTime(2021, 7, 21)
},
new Project()
{
Id = 2,
Title = "Public Project",
Description = "It's quite obvious, isn't it?!",
PlannedEnding = new DateTime(2036, 6, 16)
EndingDate = new DateTime(2036, 6, 16)
});
context.SaveChanges();
}

View file

@ -9,32 +9,40 @@ export default class ProjectVM {
public id: number;
public title: string;
public description: string;
public value: number;
public tickets: Ticket[];
public creationDate: string;
public endingDate: string;
public progression: number;
public status: string;
public manager: User;
public users: User[];
public tickets: Ticket[];
public files: AppFile[];
public activities: Activity[];
public allUsers: User[];
public ticketsTotalCount: number;
public ticketsDone: number;
public remainingDays: number;
public files: AppFile[];
public activities: Activity[];
public constructor(project: Project, allUsers: User[]) {
this.id = project.id;
this.title = project.title;
this.description = project.description;
this.creationDate = project.creationDate;
this.endingDate = project.endingDate;
this.progression = project.progression;
this.status = project.status;
this.manager = project.manager;
this.users = project.users;
this.allUsers = allUsers;
this.value = project.progression;
this.tickets = project.tickets;
this.files = project.files;
this.activities = project.activities;
this.allUsers = allUsers;
this.ticketsTotalCount =
this.tickets === undefined ? 0 : this.tickets.length;
this.ticketsDone =
this.tickets === undefined
? 0
: this.tickets.filter(t => t.status === "Done").length;
this.files = project.files;
this.activities = project.activities;
this.remainingDays = getRemainingdays(project.plannedEnding);
this.remainingDays = getRemainingdays(project.endingDate);
}
}

View file

@ -4,7 +4,7 @@ import { AvatarList } from "./AvatarList";
import { User } from "../types/User";
import { FilterBar } from "./FilterBar";
import { HttpResponse } from "../types/HttpResponse";
import { get, put } from "../utils/http";
import { get, put, patch } from "../utils/http";
import { Constants } from "../utils/Constants";
import { UsersModalEntry } from "./UsersModalEntry";
import { useParams } from "react-router-dom";
@ -38,12 +38,13 @@ export const UsersModal: FC<IProps> = ({
) => {
e.preventDefault();
const response: HttpResponse<User[]> = await put<User[]>(
const response: HttpResponse<User[]> = await patch<User[]>(
`${Constants.projectsURI}/${id}/members`,
members
);
console.log(response);
};
console.log(allUsers);
return (
<Modal show={show} handleClose={handleClose}>

View file

@ -17,7 +17,7 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
description,
users,
allUsers,
value,
progression,
tickets,
ticketsDone,
ticketsTotalCount,
@ -49,7 +49,7 @@ export const ProjectPage: FC<IProps> = ({ viewModel }) => {
/>
</div>
<ProgressBar
value={value}
value={progression}
tasksDone={ticketsDone}
tasksTotalCount={ticketsTotalCount}
remainingDays={remainingDays}

View file

@ -7,8 +7,8 @@ export interface Project {
id: number;
title: string;
description: string;
createdAt: string;
plannedEnding: string;
creationDate: string;
endingDate: string;
progression: number;
status: string;
manager: User;

View file

@ -1,6 +1,18 @@
import { Activity } from "./Activity";
import { Project } from "./Project";
import { Ticket } from "./Ticket";
export interface User {
id: string;
picture: string;
firstName: string;
fullName?: string;
lastName: string;
fullName: string;
presentation: string;
email: string;
phone: string;
createdAt: string;
picture: string;
activities: Activity[];
projects: Project[];
tickets: Ticket[];
}

View file

@ -42,6 +42,18 @@ export async function put<T>(
return await http<T>(new Request(path, args));
}
export async function patch<T>(
path: string,
body: any,
args: RequestInit = {
method: "patch",
headers: headers,
body: JSON.stringify(body)
}
): Promise<HttpResponse<T>> {
return await http<T>(new Request(path, args));
}
const headers: Headers = new Headers({
Accept: "application/json",
"Content-Type": "application/json"