diff --git a/AzureDevopsTracker/DTOs/Create/CreateWorkItemDTO.cs b/AzureDevopsTracker/DTOs/Create/CreateWorkItemDTO.cs index bcaa31e..e19f527 100644 --- a/AzureDevopsTracker/DTOs/Create/CreateWorkItemDTO.cs +++ b/AzureDevopsTracker/DTOs/Create/CreateWorkItemDTO.cs @@ -9,15 +9,4 @@ public class CreateWorkItemDTO [JsonProperty("resource")] public Resource Resource { get; set; } } - - public class Resource - { - [JsonPropertyName("id")] - [JsonProperty("id")] - public string Id { get; set; } - - [JsonPropertyName("fields")] - [JsonProperty("fields")] - public Fields Fields { get; set; } - } } \ No newline at end of file diff --git a/AzureDevopsTracker/DTOs/Delete/DeleteWorkItemDTO.cs b/AzureDevopsTracker/DTOs/Delete/DeleteWorkItemDTO.cs new file mode 100644 index 0000000..6b2e982 --- /dev/null +++ b/AzureDevopsTracker/DTOs/Delete/DeleteWorkItemDTO.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace AzureDevopsTracker.DTOs.Delete +{ + public class DeleteWorkItemDTO + { + [JsonPropertyName("resource")] + [JsonProperty("resource")] + public Resource Resource { get; set; } + } +} diff --git a/AzureDevopsTracker/DTOs/Resource.cs b/AzureDevopsTracker/DTOs/Resource.cs new file mode 100644 index 0000000..7127dd6 --- /dev/null +++ b/AzureDevopsTracker/DTOs/Resource.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace AzureDevopsTracker.DTOs +{ + public class Resource + { + [JsonPropertyName("id")] + [JsonProperty("id")] + public string Id { get; set; } + + [JsonPropertyName("fields")] + [JsonProperty("fields")] + public Fields Fields { get; set; } + } +} diff --git a/AzureDevopsTracker/DTOs/Restore/RestoreWorkItemDTO.cs b/AzureDevopsTracker/DTOs/Restore/RestoreWorkItemDTO.cs new file mode 100644 index 0000000..c5d2890 --- /dev/null +++ b/AzureDevopsTracker/DTOs/Restore/RestoreWorkItemDTO.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace AzureDevopsTracker.DTOs.Restore +{ + public class RestoreWorkItemDTO + { + [JsonPropertyName("resource")] + [JsonProperty("resource")] + public Resource Resource { get; set; } + } +} diff --git a/AzureDevopsTracker/Entities/WorkItem.cs b/AzureDevopsTracker/Entities/WorkItem.cs index 87b6ca6..35c1b98 100644 --- a/AzureDevopsTracker/Entities/WorkItem.cs +++ b/AzureDevopsTracker/Entities/WorkItem.cs @@ -21,6 +21,7 @@ public class WorkItem : Entity public string WorkItemParentId { get; private set; } public string Activity { get; private set; } public string Lancado { get; private set; } + public bool Deleted { get; private set; } public ChangeLogItem ChangeLogItem { get; private set; } @@ -73,6 +74,16 @@ public void Update(string title, Lancado = lancado; } + public void Restore() + { + Deleted = false; + } + + public void Delete() + { + Deleted = true; + } + public void Validate() { if (Id.IsNullOrEmpty()) diff --git a/AzureDevopsTracker/Interfaces/IAzureDevopsTrackerService.cs b/AzureDevopsTracker/Interfaces/IAzureDevopsTrackerService.cs index 0d2e1fa..83dfe78 100644 --- a/AzureDevopsTracker/Interfaces/IAzureDevopsTrackerService.cs +++ b/AzureDevopsTracker/Interfaces/IAzureDevopsTrackerService.cs @@ -1,5 +1,7 @@ using AzureDevopsTracker.DTOs; using AzureDevopsTracker.DTOs.Create; +using AzureDevopsTracker.DTOs.Delete; +using AzureDevopsTracker.DTOs.Restore; using AzureDevopsTracker.DTOs.Update; using System.Threading.Tasks; @@ -9,6 +11,8 @@ public interface IAzureDevopsTrackerService { Task Create(CreateWorkItemDTO createDto, bool addWorkItemChange = true); Task Update(UpdatedWorkItemDTO updateDto); + Task Delete(DeleteWorkItemDTO deleteDto); + Task Restore(RestoreWorkItemDTO restoreDto); Task GetByWorkItemId(string workItemId); } } \ No newline at end of file diff --git a/AzureDevopsTracker/Migrations/20220607220158_WorkItem_AddDeleted.Designer.cs b/AzureDevopsTracker/Migrations/20220607220158_WorkItem_AddDeleted.Designer.cs new file mode 100644 index 0000000..1933b45 --- /dev/null +++ b/AzureDevopsTracker/Migrations/20220607220158_WorkItem_AddDeleted.Designer.cs @@ -0,0 +1,253 @@ +// +using System; +using AzureDevopsTracker.Data; +using AzureDevopsTracker.Data.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace AzureDevopsTracker.Migrations +{ + [DbContext(typeof(AzureDevopsTrackerContext))] + [Migration("20220607220158_WorkItem_AddDeleted")] + partial class WorkItem_AddDeleted + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema(DataBaseConfig.SchemaName) + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.10") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("AzureDevopsTracker.Entities.ChangeLog", b => + { + b.Property("Id") + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Number") + .HasColumnType("varchar(200)"); + + b.Property("Response") + .HasColumnType("varchar(max)"); + + b.Property("Revision") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("ChangeLogs"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.ChangeLogItem", b => + { + b.Property("Id") + .HasColumnType("varchar(200)"); + + b.Property("ChangeLogId") + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("varchar(max)"); + + b.Property("Title") + .HasColumnType("varchar(200)"); + + b.Property("WorkItemId") + .HasColumnType("varchar(200)"); + + b.Property("WorkItemType") + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("ChangeLogId"); + + b.HasIndex("WorkItemId") + .IsUnique() + .HasFilter("[WorkItemId] IS NOT NULL"); + + b.ToTable("ChangeLogItems"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.TimeByState", b => + { + b.Property("Id") + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("State") + .HasColumnType("varchar(200)"); + + b.Property("TotalTime") + .HasColumnType("float"); + + b.Property("TotalWorkedTime") + .HasColumnType("float"); + + b.Property("WorkItemId") + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("WorkItemId"); + + b.ToTable("TimeByStates"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.WorkItem", b => + { + b.Property("Id") + .HasColumnType("varchar(200)"); + + b.Property("Activity") + .HasColumnType("varchar(200)"); + + b.Property("AreaPath") + .HasColumnType("varchar(200)"); + + b.Property("AssignedTo") + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("varchar(200)"); + + b.Property("Deleted") + .HasColumnType("bit"); + + b.Property("Effort") + .HasColumnType("varchar(200)"); + + b.Property("IterationPath") + .HasColumnType("varchar(200)"); + + b.Property("Lancado") + .HasColumnType("varchar(200)"); + + b.Property("OriginalEstimate") + .HasColumnType("varchar(200)"); + + b.Property("StoryPoints") + .HasColumnType("varchar(200)"); + + b.Property("Tags") + .HasColumnType("varchar(200)"); + + b.Property("TeamProject") + .HasColumnType("varchar(200)"); + + b.Property("Title") + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasColumnType("varchar(200)"); + + b.Property("WorkItemParentId") + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.ToTable("WorkItems"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.WorkItemChange", b => + { + b.Property("Id") + .HasColumnType("varchar(200)"); + + b.Property("ChangedBy") + .HasColumnType("varchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IterationPath") + .HasColumnType("varchar(200)"); + + b.Property("NewDate") + .HasColumnType("datetime2"); + + b.Property("NewState") + .HasColumnType("varchar(200)"); + + b.Property("OldDate") + .HasColumnType("datetime2"); + + b.Property("OldState") + .HasColumnType("varchar(200)"); + + b.Property("TotalWorkedTime") + .HasColumnType("float"); + + b.Property("WorkItemId") + .HasColumnType("varchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("WorkItemId"); + + b.ToTable("WorkItemsChange"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.ChangeLogItem", b => + { + b.HasOne("AzureDevopsTracker.Entities.ChangeLog", "ChangeLog") + .WithMany("ChangeLogItems") + .HasForeignKey("ChangeLogId"); + + b.HasOne("AzureDevopsTracker.Entities.WorkItem", null) + .WithOne("ChangeLogItem") + .HasForeignKey("AzureDevopsTracker.Entities.ChangeLogItem", "WorkItemId"); + + b.Navigation("ChangeLog"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.TimeByState", b => + { + b.HasOne("AzureDevopsTracker.Entities.WorkItem", "WorkItem") + .WithMany("TimeByStates") + .HasForeignKey("WorkItemId"); + + b.Navigation("WorkItem"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.WorkItemChange", b => + { + b.HasOne("AzureDevopsTracker.Entities.WorkItem", "WorkItem") + .WithMany("WorkItemsChanges") + .HasForeignKey("WorkItemId"); + + b.Navigation("WorkItem"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.ChangeLog", b => + { + b.Navigation("ChangeLogItems"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.WorkItem", b => + { + b.Navigation("ChangeLogItem"); + + b.Navigation("TimeByStates"); + + b.Navigation("WorkItemsChanges"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AzureDevopsTracker/Migrations/20220607220158_WorkItem_AddDeleted.cs b/AzureDevopsTracker/Migrations/20220607220158_WorkItem_AddDeleted.cs new file mode 100644 index 0000000..335a9ba --- /dev/null +++ b/AzureDevopsTracker/Migrations/20220607220158_WorkItem_AddDeleted.cs @@ -0,0 +1,27 @@ +using AzureDevopsTracker.Data; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace AzureDevopsTracker.Migrations +{ + public partial class WorkItem_AddDeleted : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Deleted", + schema: DataBaseConfig.SchemaName, + table: "WorkItems", + type: "bit", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Deleted", + schema: DataBaseConfig.SchemaName, + table: "WorkItems"); + } + } +} diff --git a/AzureDevopsTracker/Migrations/AzureDevopsStateTrackerContextModelSnapshot.cs b/AzureDevopsTracker/Migrations/AzureDevopsStateTrackerContextModelSnapshot.cs index f41b26c..0978b18 100644 --- a/AzureDevopsTracker/Migrations/AzureDevopsStateTrackerContextModelSnapshot.cs +++ b/AzureDevopsTracker/Migrations/AzureDevopsStateTrackerContextModelSnapshot.cs @@ -17,8 +17,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema(DataBaseConfig.SchemaName) - .HasAnnotation("ProductVersion", "3.1.21") .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.10") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("AzureDevopsTracker.Entities.ChangeLog", b => @@ -124,6 +124,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedBy") .HasColumnType("varchar(200)"); + b.Property("Deleted") + .HasColumnType("bit"); + b.Property("Effort") .HasColumnType("varchar(200)"); @@ -207,6 +210,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("AzureDevopsTracker.Entities.WorkItem", null) .WithOne("ChangeLogItem") .HasForeignKey("AzureDevopsTracker.Entities.ChangeLogItem", "WorkItemId"); + + b.Navigation("ChangeLog"); }); modelBuilder.Entity("AzureDevopsTracker.Entities.TimeByState", b => @@ -214,6 +219,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("AzureDevopsTracker.Entities.WorkItem", "WorkItem") .WithMany("TimeByStates") .HasForeignKey("WorkItemId"); + + b.Navigation("WorkItem"); }); modelBuilder.Entity("AzureDevopsTracker.Entities.WorkItemChange", b => @@ -221,6 +228,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("AzureDevopsTracker.Entities.WorkItem", "WorkItem") .WithMany("WorkItemsChanges") .HasForeignKey("WorkItemId"); + + b.Navigation("WorkItem"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.ChangeLog", b => + { + b.Navigation("ChangeLogItems"); + }); + + modelBuilder.Entity("AzureDevopsTracker.Entities.WorkItem", b => + { + b.Navigation("ChangeLogItem"); + + b.Navigation("TimeByStates"); + + b.Navigation("WorkItemsChanges"); }); #pragma warning restore 612, 618 } diff --git a/AzureDevopsTracker/Services/AzureDevopsTrackerService.cs b/AzureDevopsTracker/Services/AzureDevopsTrackerService.cs index e0e07e4..d52ead2 100644 --- a/AzureDevopsTracker/Services/AzureDevopsTrackerService.cs +++ b/AzureDevopsTracker/Services/AzureDevopsTrackerService.cs @@ -1,5 +1,7 @@ using AzureDevopsTracker.DTOs; using AzureDevopsTracker.DTOs.Create; +using AzureDevopsTracker.DTOs.Delete; +using AzureDevopsTracker.DTOs.Restore; using AzureDevopsTracker.DTOs.Update; using AzureDevopsTracker.Entities; using AzureDevopsTracker.Extensions; @@ -55,14 +57,14 @@ public async Task Create(CreateWorkItemDTO create, bool addWorkItemChange = true await _workItemRepository.SaveChangesAsync(); } - public async Task Create(UpdatedWorkItemDTO updateDto) + public async Task Create(string workItemId, Fields fields) { var createDto = new CreateWorkItemDTO() { - Resource = new DTOs.Create.Resource() + Resource = new DTOs.Resource() { - Fields = updateDto.Resource.Revision.Fields, - Id = updateDto.Resource.WorkItemId, + Fields = fields, + Id = workItemId, } }; @@ -72,7 +74,7 @@ public async Task Create(UpdatedWorkItemDTO updateDto) public async Task Update(UpdatedWorkItemDTO update) { if (!_workItemRepository.Exist(update.Resource.WorkItemId).Result) - await Create(update); + await Create(update.Resource.WorkItemId, update.Resource.Revision.Fields); var workItem = await _workItemRepository.GetByWorkItemId(update.Resource.WorkItemId); if (workItem is null) @@ -101,6 +103,66 @@ public async Task Update(UpdatedWorkItemDTO update) await _workItemRepository.SaveChangesAsync(); } + public async Task Delete(DeleteWorkItemDTO delete) + { + if (!_workItemRepository.Exist(delete.Resource.Id).Result) + await Create(delete.Resource.Id, delete.Resource.Fields); + + var workItem = await _workItemRepository.GetByWorkItemId(delete.Resource.Id); + if (workItem is null) + return; + + workItem.Delete(); + + workItem.Update(delete.Resource.Fields.Title, + delete.Resource.Fields.TeamProject, + delete.Resource.Fields.AreaPath, + delete.Resource.Fields.IterationPath, + delete.Resource.Fields.Type, + delete.Resource.Fields.CreatedBy.ExtractEmail(), + delete.Resource.Fields.AssignedTo.ExtractEmail(), + delete.Resource.Fields.Tags, + delete.Resource.Fields.Parent, + delete.Resource.Fields.Effort, + delete.Resource.Fields.StoryPoints, + delete.Resource.Fields.OriginalEstimate, + delete.Resource.Fields.Activity, + delete.Resource.Fields.Lancado); + + _workItemRepository.Update(workItem); + await _workItemRepository.SaveChangesAsync(); + } + + public async Task Restore(RestoreWorkItemDTO restore) + { + if (!_workItemRepository.Exist(restore.Resource.Id).Result) + await Create(restore.Resource.Id, restore.Resource.Fields); + + var workItem = await _workItemRepository.GetByWorkItemId(restore.Resource.Id); + if (workItem is null) + return; + + workItem.Restore(); + + workItem.Update(restore.Resource.Fields.Title, + restore.Resource.Fields.TeamProject, + restore.Resource.Fields.AreaPath, + restore.Resource.Fields.IterationPath, + restore.Resource.Fields.Type, + restore.Resource.Fields.CreatedBy.ExtractEmail(), + restore.Resource.Fields.AssignedTo.ExtractEmail(), + restore.Resource.Fields.Tags, + restore.Resource.Fields.Parent, + restore.Resource.Fields.Effort, + restore.Resource.Fields.StoryPoints, + restore.Resource.Fields.OriginalEstimate, + restore.Resource.Fields.Activity, + restore.Resource.Fields.Lancado); + + _workItemRepository.Update(workItem); + await _workItemRepository.SaveChangesAsync(); + } + public async Task GetByWorkItemId(string workItemId) { var workItem = await _workItemRepository.GetByWorkItemId(workItemId);