travail sur les TU
continuous-integration/drone/push Build is passing Details

master
PATRICK 1 year ago
parent da15b2befb
commit 2cdfc6b367

@ -21,12 +21,12 @@ namespace API.Controllers
} }
[HttpGet] [HttpGet]
public async Task<ActionResult<IEnumerable<LangueDTO>>> GetLangues(int index, int count) public async Task<ActionResult<PageResponse<LangueDTO>>> GetLangues(int index, int count)
{ {
try { try {
_logger.LogInformation("Getting langues "); _logger.LogInformation("Getting langues ");
var groups = await _service.Gets(index,count); var groups = await _service.Gets(index,count);
return Ok(groups); return groups;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -44,7 +44,7 @@ namespace API.Controllers
try { try {
_logger.LogInformation("Getting a langue with name {name}",name); _logger.LogInformation("Getting a langue with name {name}",name);
var group = await _service.GetById(name); var group = await _service.GetById(name);
return Ok(group); return group;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -55,7 +55,8 @@ namespace API.Controllers
return StatusCode(400, ex.Message); return StatusCode(400, ex.Message);
} }
} }
//On ne peut pas changer la langue car son nom est son Id
/*
[HttpPut] [HttpPut]
public async Task<ActionResult<LangueDTO>> UpdateLangue([FromQuery]LangueDTO langue) public async Task<ActionResult<LangueDTO>> UpdateLangue([FromQuery]LangueDTO langue)
{ {
@ -63,7 +64,7 @@ namespace API.Controllers
{ {
_logger.LogInformation("Updating a langue with name : {name}", langue.name); _logger.LogInformation("Updating a langue with name : {name}", langue.name);
var updatedGroup = await _service.Update(langue); var updatedGroup = await _service.Update(langue);
return Ok(updatedGroup); return updatedGroup;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -73,7 +74,7 @@ namespace API.Controllers
// Retourner une réponse d'erreur // Retourner une réponse d'erreur
return StatusCode(400, ex.Message); return StatusCode(400, ex.Message);
} }
} }*/
[HttpPost] [HttpPost]
public async Task<ActionResult<LangueDTO>> AddLangue([FromQuery]LangueDTO langue) public async Task<ActionResult<LangueDTO>> AddLangue([FromQuery]LangueDTO langue)
@ -81,16 +82,8 @@ namespace API.Controllers
try try
{ {
_logger.LogInformation("Adding a langue with name : {name}", langue.name); _logger.LogInformation("Adding a langue with name : {name}", langue.name);
if (langue.name == null)
{
return BadRequest("Name is required");
}
if (_service.GetById(langue.name) != null)
{
return BadRequest("Name already exists");
}
var newGroup = await _service.Add(langue); var newGroup = await _service.Add(langue);
return Ok(newGroup); return newGroup;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -109,7 +102,7 @@ namespace API.Controllers
{ {
_logger.LogInformation("Deleting a langue with name : {name}", name); _logger.LogInformation("Deleting a langue with name : {name}", name);
var group = await _service.Delete(name); var group = await _service.Delete(name);
return Ok(group); return group;
} }
catch (Exception ex) catch (Exception ex)
{ {

@ -20,13 +20,13 @@ namespace API.Controllers
} }
[HttpGet] [HttpGet]
public async Task<ActionResult<IEnumerable<RoleDTO>>> GetRoles(int index, int count) public async Task<ActionResult<PageResponse<RoleDTO>>> GetRoles(int index, int count)
{ {
try try
{ {
_logger.LogInformation("Getting Roles "); _logger.LogInformation("Getting Roles ");
var groups = await _service.Gets(index, count); var groups = await _service.Gets(index, count);
return Ok(groups); return groups;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -45,7 +45,7 @@ namespace API.Controllers
{ {
_logger.LogInformation("Getting a role with id {id}", id); _logger.LogInformation("Getting a role with id {id}", id);
var group = await _service.GetById(id); var group = await _service.GetById(id);
return Ok(group); return group;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -63,7 +63,7 @@ namespace API.Controllers
try { try {
_logger.LogInformation("Updating a role with id : {id}", role.Id); _logger.LogInformation("Updating a role with id : {id}", role.Id);
var updatedGroup = await _service.Update(role); var updatedGroup = await _service.Update(role);
return Ok(updatedGroup); return updatedGroup;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -83,7 +83,7 @@ namespace API.Controllers
_logger.LogInformation("Adding a role with id : {id}", role.Id); _logger.LogInformation("Adding a role with id : {id}", role.Id);
role.Id = _service.Gets(0, 0).Result.TotalCount + 1; role.Id = _service.Gets(0, 0).Result.TotalCount + 1;
var newGroup = await _service.Add(role); var newGroup = await _service.Add(role);
return Ok(newGroup); return newGroup;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -101,7 +101,7 @@ namespace API.Controllers
try { try {
_logger.LogInformation("Deleting a role with id : {id}", id); _logger.LogInformation("Deleting a role with id : {id}", id);
var group = await _service.Delete(id); var group = await _service.Delete(id);
return Ok(group); return group;
} }
catch (Exception ex) catch (Exception ex)
{ {

@ -10,6 +10,7 @@ using DTOToEntity;
using DTO; using DTO;
using System; using System;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
@ -24,8 +25,7 @@ builder.Services.AddApiVersioning(o =>
o.AssumeDefaultVersionWhenUnspecified = true; o.AssumeDefaultVersionWhenUnspecified = true;
o.ReportApiVersions = true; o.ReportApiVersions = true;
}); });
builder.Services.AddScoped<IGroupService, GroupService>();
builder.Services.AddScoped<IGroupService,GroupService>();
builder.Services.AddScoped<IService<LangueDTO>,LangueService>(); builder.Services.AddScoped<IService<LangueDTO>,LangueService>();
builder.Services.AddScoped<IService<RoleDTO>,RoleService>(); builder.Services.AddScoped<IService<RoleDTO>,RoleService>();
builder.Services.AddScoped<IService<TranslateDTO>,TranslateService>(); builder.Services.AddScoped<IService<TranslateDTO>,TranslateService>();
@ -33,7 +33,6 @@ builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IVocabularyService, VocabularyService>(); builder.Services.AddScoped<IVocabularyService, VocabularyService>();
builder.Services.AddScoped<IVocabularyListService, VocabularyListService>(); builder.Services.AddScoped<IVocabularyListService, VocabularyListService>();
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {

@ -8,7 +8,8 @@ namespace DTO
{ {
public class LangueDTO public class LangueDTO
{ {
public string name { get; set; } public string
name { get; set; }
//public ICollection<VocabularyDTO> vocabularys { get; set; } = new List<VocabularyDTO>(); //public ICollection<VocabularyDTO> vocabularys { get; set; } = new List<VocabularyDTO>();
} }

@ -88,10 +88,22 @@ namespace DTOToEntity
public async Task<GroupDTO> Update(GroupDTO group) public async Task<GroupDTO> Update(GroupDTO group)
{ {
var groupEntity = group.ToEntity(); if(group == null)
var res = context.Groups.Update(groupEntity); {
throw new ArgumentNullException();
}
var existingGroup = await context.Groups.FindAsync(group.Id);
if (existingGroup == null)
{
throw new Exception("Group not found");
}
existingGroup.year = group.Year;
existingGroup.sector = group.sector;
existingGroup.Num = group.Num;
await context.SaveChangesAsync(); await context.SaveChangesAsync();
return res.Entity.ToDTO(); return existingGroup.ToDTO();
} }
} }
} }

@ -14,6 +14,11 @@ namespace DTOToEntity
{ {
private readonly StubbedContext _context = new StubbedContext(); private readonly StubbedContext _context = new StubbedContext();
public LangueService() { } public LangueService() { }
public LangueService(StubbedContext context)
{
this._context = context;
}
public async Task<LangueDTO> Add(LangueDTO langue) public async Task<LangueDTO> Add(LangueDTO langue)
{ {
var langueEntity = langue.ToEntity(); var langueEntity = langue.ToEntity();

@ -13,6 +13,12 @@ namespace DTOToEntity
public class RoleService : IService<RoleDTO> public class RoleService : IService<RoleDTO>
{ {
private readonly StubbedContext _context = new StubbedContext(); private readonly StubbedContext _context = new StubbedContext();
public RoleService() { }
public RoleService(StubbedContext context)
{
_context = context;
}
public async Task<RoleDTO> Add(RoleDTO role) public async Task<RoleDTO> Add(RoleDTO role)
{ {
var roleEntity = role.ToEntity(); var roleEntity = role.ToEntity();
@ -32,9 +38,9 @@ namespace DTOToEntity
{ {
throw new Exception("Role not found"); throw new Exception("Role not found");
} }
_context.Roles.Remove(role); var res = _context.Roles.Remove(role);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return role.ToDTO(); return res.Entity.ToDTO();
} }
public async Task<RoleDTO> GetById(object id) public async Task<RoleDTO> GetById(object id)
@ -55,13 +61,16 @@ namespace DTOToEntity
public async Task<RoleDTO> Update(RoleDTO role) public async Task<RoleDTO> Update(RoleDTO role)
{ {
RoleEntity? roleEntity = await _context.Roles.FirstOrDefaultAsync(r => r.Id == role.Id);
if (role == null) if (role == null)
{ {
throw new Exception("Role not found"); throw new ArgumentNullException();
}
var roleEntity = await _context.Roles.FindAsync(role.Id);
if (roleEntity != null)
{
throw new Exception("role not found");
} }
roleEntity= role.ToEntity(); roleEntity.Name = role.Name;
_context.Roles.Update(roleEntity);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
return roleEntity.ToDTO(); return roleEntity.ToDTO();
} }

@ -2,13 +2,11 @@ using DbContextLib;
using Entities; using Entities;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using StubbedContextLib; using StubbedContextLib;
using API.Controllers; using API.Controllers;
using DTOToEntity; using DTOToEntity;
using DTO; using DTO;
using Microsoft.AspNetCore.Mvc;
using Moq; using Moq;
namespace TU namespace TU
@ -16,9 +14,6 @@ namespace TU
[TestClass] [TestClass]
public class GroupTU public class GroupTU
{ {
private static ILogger<GroupController> _logger = new NullLogger<GroupController>();
private static IGroupService _booksService = new GroupService();
private GroupController _controller = new GroupController(_booksService, _logger);
@ -156,12 +151,12 @@ namespace TU
{ {
context.Database.EnsureCreated(); context.Database.EnsureCreated();
GroupEntity g1 = new GroupEntity { Id = 4, Num = 4, sector = "sect3", year = 2020 }; GroupEntity g1 = new GroupEntity { Id = 4, Num = 4, sector = "sect3", year = 2020 };
GroupDTO updatedGroupDTO = new GroupDTO { Id = 4, Num = 2, sector = "sect4", Year = 2021 };
await context.Groups.AddAsync(g1); await context.Groups.AddAsync(g1);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
var mockLogger = new Mock<ILogger<GroupController>>(); var mockLogger = new Mock<ILogger<GroupController>>();
var controller = new GroupController(new GroupService(context), mockLogger.Object); var controller = new GroupController(new GroupService(context), mockLogger.Object);
var updatedGroupDTO = new GroupDTO { Id = 4, Num = 2, sector = "sect4", Year = 2021 };
var result = await controller.UpdateGroup(updatedGroupDTO); var result = await controller.UpdateGroup(updatedGroupDTO);

@ -8,15 +8,13 @@ using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using StubbedContextLib; using StubbedContextLib;
using DTO; using DTO;
using Moq;
namespace TU namespace TU
{ {
[TestClass] [TestClass]
public class LangueTU public class LangueTU
{ {
private static ILogger<LangueController> _logger = new NullLogger<LangueController>();
private static IService<LangueDTO> _langueService = new LangueService();
private LangueController _controller = new LangueController(_langueService,_logger);
[TestMethod] [TestMethod]
public async Task TestAddLangue() public async Task TestAddLangue()
@ -26,19 +24,24 @@ namespace TU
var options = new DbContextOptionsBuilder<LibraryContext>() var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection) .UseSqlite(connection)
.Options; .Options;
using (var context = new StubbedContext(options)) using (var context = new StubbedContext(options))
{ {
context.Database.EnsureCreated(); context.Database.EnsureCreated();
var vocab = new VocabularyEntity { word = "test", Langue = null };
var newLangue = new LangueDTO { name = "français" };
var res = await _controller.AddLangue(newLangue); // Créer un mock pour le logger
Assert.IsNotNull(res); var mockLogger = new Mock<ILogger<LangueController>>();
Assert.AreEqual("français", res.Value.name);
var controller = new LangueController(new LangueService(context), mockLogger.Object);
var newLangue = new LangueDTO { name = "test" };
var result = await controller.AddLangue(newLangue);
Assert.IsNotNull(result.Value);
Assert.AreEqual(newLangue.name, result.Value.name);
} }
} }
[TestMethod] [TestMethod]
public async Task TestDeleteLangue() public async Task TestDeleteLangue()
@ -48,26 +51,29 @@ namespace TU
var options = new DbContextOptionsBuilder<LibraryContext>() var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection) .UseSqlite(connection)
.Options; .Options;
using (var context = new StubbedContext(options)) using (var context = new StubbedContext(options))
{ {
context.Database.EnsureCreated(); context.Database.EnsureCreated();
var vocab = new VocabularyEntity { word = "test", Langue = null }; var newLangue = new LangueDTO { name = "test" };
var newLangue = new LangueEntity { name = "français", vocabularys=[vocab] }; await context.Langues.AddAsync(newLangue.ToEntity());
vocab.Langue = newLangue; // Créer un mock pour le logger
await context.Langues.AddAsync(newLangue); var mockLogger = new Mock<ILogger<LangueController>>();
await context.SaveChangesAsync();
var controller = new LangueController(new LangueService(context), mockLogger.Object);
var langue = await context.Langues.FirstOrDefaultAsync(b => b.name == "français");
Assert.IsNotNull(langue);
Assert.AreEqual("français", langue.name); var result = await controller.DeleteLangue("test");
Assert.AreEqual(vocab, langue.vocabularys.First()); Assert.IsNotNull(result.Value);
Assert.AreEqual(newLangue.name, result.Value.name);
context.Langues.Remove(newLangue); var res = await context.Langues.FirstOrDefaultAsync(l => l.name == "test");
await context.SaveChangesAsync(); Assert.IsNull(res);
var langue2 = await context.Langues.FirstOrDefaultAsync(b => b.name == "français");
Assert.IsNull(langue2);
} }
} }
/*
[TestMethod] [TestMethod]
public async Task TestUpdateLangue() public async Task TestUpdateLangue()
{ {
@ -76,29 +82,76 @@ namespace TU
var options = new DbContextOptionsBuilder<LibraryContext>() var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection) .UseSqlite(connection)
.Options; .Options;
using (var context = new StubbedContext(options)) using (var context = new StubbedContext(options))
{ {
context.Database.EnsureCreated(); context.Database.EnsureCreated();
var newLangue = new LangueDTO { name = "test" };
await context.Langues.AddAsync(newLangue.ToEntity());
// Créer un mock pour le logger
var mockLogger = new Mock<ILogger<LangueController>>();
var controller = new LangueController(new LangueService(context), mockLogger.Object);
newLangue.
var result = await controller.UpdateLangue(new);
Assert.IsNotNull(result.Value);
Assert.AreEqual(newLangue.name, result.Value.name);
var res = await context.Langues.FirstOrDefaultAsync(l => l.name == "test");
Assert.IsNull(res);
}
}*/
[TestMethod]
public async Task TestGetById()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection)
.Options;
using (var context = new StubbedContext(options))
{
context.Database.EnsureCreated();
var newLangue = new LangueDTO { name = "test" };
await context.Langues.AddAsync(newLangue.ToEntity());
// Créer un mock pour le logger
var mockLogger = new Mock<ILogger<LangueController>>();
var controller = new LangueController(new LangueService(context), mockLogger.Object);
var result = await controller.GetLangue("test");
Assert.IsNotNull(result.Value);
Assert.AreEqual(newLangue.name, result.Value.name);
}
}
[TestMethod]
public async Task TestGetGroups()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection)
.Options;
using (var context = new StubbedContext(options))
{
context.Database.EnsureCreated();
var mockLogger = new Mock<ILogger<LangueController>>();
var controller = new LangueController(new LangueService(context), mockLogger.Object);
var result = await controller.GetLangues(0, 5);
Assert.IsNotNull(result.Value);
Assert.AreEqual(2, result.Value.TotalCount);
Assert.AreEqual("English", result.Value.Items.ToList()[0].name);
Assert.AreEqual("French", result.Value.Items.ToList()[1].name);
var newBook = new GroupEntity { Id = 2, year = 2, sector = "medecin" };
await context.Groups.AddAsync(newBook);
await context.SaveChangesAsync();
var group1 = await context.Groups.FirstOrDefaultAsync(b => b.sector == "medecin");
Assert.IsNotNull(group1);
Assert.AreEqual("medecin", group1.sector);
Assert.AreEqual(2, group1.year);
Assert.AreEqual(2, group1.Id);
group1.sector = "informatique";
group1.year = 3;
context.Groups.Update(group1);
await context.SaveChangesAsync();
var group2 = await context.Groups.FirstOrDefaultAsync(b => b.sector == "informatique");
Assert.IsNotNull(group2);
Assert.AreEqual("informatique", group2.sector);
Assert.AreEqual(3, group2.year);
Assert.AreEqual(2, group2.Id);
} }
} }
} }

@ -1,7 +1,12 @@
using API.Controllers;
using DbContextLib; using DbContextLib;
using DTO;
using DTOToEntity;
using Entities; using Entities;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using StubbedContextLib; using StubbedContextLib;
namespace TU namespace TU
@ -17,18 +22,23 @@ namespace TU
var options = new DbContextOptionsBuilder<LibraryContext>() var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection) .UseSqlite(connection)
.Options; .Options;
using (var context = new StubbedContext(options)) using (var context = new StubbedContext(options))
{ {
context.Database.EnsureCreated(); context.Database.EnsureCreated();
var user = new UserEntity { Id = 1, Name = "name", UserName = "username", NickName = "nickname", ExtraTime = true, GroupId = 1, Password = "1234", Email = "" };
var newRole = new RoleEntity { Id=4, Name = "user" , Users = [user] };
await context.Roles.AddAsync(newRole);
await context.SaveChangesAsync();
var role = await context.Roles.FirstOrDefaultAsync(b => b.Name == "user"); var mockLogger = new Mock<ILogger<RoleController>>();
Assert.IsNotNull(role);
Assert.AreEqual("user", role.Name); var controller = new RoleController(new RoleService(context), mockLogger.Object);
Assert.AreEqual(user, role.Users.First());
var newRole = new RoleDTO { Id = 100, Name = "test" };
var result = await controller.AddRole(newRole);
Assert.IsNotNull(result.Value);
//ici on met 4 pour verifier que le Id n'est pas celui que l'on donne mais bien : CountList + 1
Assert.AreEqual(4, result.Value.Id);
Assert.AreEqual("test", result.Value.Name);
} }
} }
[TestMethod] [TestMethod]
@ -39,26 +49,23 @@ namespace TU
var options = new DbContextOptionsBuilder<LibraryContext>() var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection) .UseSqlite(connection)
.Options; .Options;
using (var context = new StubbedContext(options)) using (var context = new StubbedContext(options))
{ {
context.Database.EnsureCreated(); context.Database.EnsureCreated();
var user = new UserEntity { Id = 4, Name = "name", UserName = "username", NickName = "nickname", ExtraTime = true, GroupId = 1, Password = "1234", Email = "", RoleId=5 };
var user1 = new UserEntity { Id = 5, Name = "name2", UserName = "username2", NickName = "nickname2", ExtraTime = true, GroupId = 2, Password = "1234", Email = "", RoleId=5 };
var newRole = new RoleEntity { Id=5,Name = "user" };
newRole.Users.Add(user);
await context.Roles.AddAsync(newRole);
await context.SaveChangesAsync();
var role = await context.Roles.FirstOrDefaultAsync(b => b.Name == "user"); var mockLogger = new Mock<ILogger<RoleController>>();
Assert.AreEqual(newRole, role);
role.Name = "admin";
context.Roles.Update(role);
await context.SaveChangesAsync(); var controller = new RoleController(new RoleService(context), mockLogger.Object);
var role1 = await context.Roles.FirstOrDefaultAsync(b => b. Name == "admin");
Assert.IsNotNull(role1); var newRole = new RoleDTO { Id = 4, Name = "test" };
Assert.AreEqual("admin", role1.Name); await controller.AddRole(newRole);
Assert.AreEqual(user, role1.Users.First()); var newRole2 = new RoleDTO { Id = 4, Name = "modifié" };
await controller.UpdateRole(newRole2);
var res = await context.Roles.FirstOrDefaultAsync(r =>r.Id == 4);
Assert.IsNotNull(res);
Assert.AreEqual(4, res.Id);
Assert.AreEqual("modifié", res.Name);
} }
} }
@ -70,21 +77,24 @@ namespace TU
var options = new DbContextOptionsBuilder<LibraryContext>() var options = new DbContextOptionsBuilder<LibraryContext>()
.UseSqlite(connection) .UseSqlite(connection)
.Options; .Options;
using (var context = new StubbedContext(options)) using (var context = new StubbedContext(options))
{ {
context.Database.EnsureCreated(); context.Database.EnsureCreated();
var newRole = new RoleEntity { Name = "user" }; var mockLogger = new Mock<ILogger<RoleController>>();
await context.Roles.AddAsync(newRole);
await context.SaveChangesAsync();
var role = await context.Roles.FirstOrDefaultAsync(b => b.Name == "user"); var controller = new RoleController(new RoleService(context), mockLogger.Object);
context.Roles.Remove(role);
var newRole = new RoleDTO { Id = 4, Name = "test" };
await context.Roles.AddAsync(newRole.ToEntity());
await context.SaveChangesAsync(); await context.SaveChangesAsync();
var role1 = await context.Roles.FirstOrDefaultAsync(b => b.Name == "user"); var result = await controller.DeleteRole(4);
Assert.IsNull(role1); Assert.IsNotNull(result.Value);
Assert.AreEqual(4, result.Value.Id);
Assert.AreEqual("test", result.Value.Name);
} }
} }
}
}
} }
Loading…
Cancel
Save