From 49e3c2881ae144da5adaa3bbb69f65f4567ad089 Mon Sep 17 00:00:00 2001 From: Mark Bonner Date: Thu, 20 Jan 2022 10:46:41 +0000 Subject: [PATCH 1/5] completed rest service --- .../AareonTechnicalTest.Tests.csproj | 24 ++++ .../NotesServiceShould.cs | 102 ++++++++++++++ AareonTechnicalTest.sln | 12 +- .../AareonTechnicalTest.csproj | 17 ++- AareonTechnicalTest/ApplicationContext.cs | 3 + .../Controllers/NotesController.cs | 50 +++++++ .../Controllers/TicketsController.cs | 50 +++++++ .../20220119201548_AddNotes.Designer.cs | 80 +++++++++++ .../Migrations/20220119201548_AddNotes.cs | 33 +++++ .../ApplicationContextModelSnapshot.cs | 27 +++- AareonTechnicalTest/Models/Note.cs | 16 +++ AareonTechnicalTest/Models/NoteConfig.cs | 22 +++ AareonTechnicalTest/Models/Person.cs | 2 +- AareonTechnicalTest/Models/PersonConfig.cs | 6 + AareonTechnicalTest/Models/TicketConfig.cs | 6 + .../Repositories/INoteRepository.cs | 21 +++ .../Repositories/IPersonRepository.cs | 10 ++ .../Repositories/ITicketRepository.cs | 21 +++ .../Repositories/NoteRepository.cs | 50 +++++++ .../Repositories/PersonRepository.cs | 20 +++ .../Repositories/TicketRepository.cs | 50 +++++++ AareonTechnicalTest/Services/INoteService.cs | 20 +++ .../Services/ITicketService.cs | 20 +++ AareonTechnicalTest/Services/NoteService.cs | 130 ++++++++++++++++++ AareonTechnicalTest/Services/TicketService.cs | 110 +++++++++++++++ AareonTechnicalTest/Startup.cs | 9 ++ AareonTechnicalTest/Ticketing.db | Bin 24576 -> 28672 bytes 27 files changed, 899 insertions(+), 12 deletions(-) create mode 100644 AareonTechnicalTest.Tests/AareonTechnicalTest.Tests.csproj create mode 100644 AareonTechnicalTest.Tests/NotesServiceShould.cs create mode 100644 AareonTechnicalTest/Controllers/NotesController.cs create mode 100644 AareonTechnicalTest/Controllers/TicketsController.cs create mode 100644 AareonTechnicalTest/Migrations/20220119201548_AddNotes.Designer.cs create mode 100644 AareonTechnicalTest/Migrations/20220119201548_AddNotes.cs create mode 100644 AareonTechnicalTest/Models/Note.cs create mode 100644 AareonTechnicalTest/Models/NoteConfig.cs create mode 100644 AareonTechnicalTest/Repositories/INoteRepository.cs create mode 100644 AareonTechnicalTest/Repositories/IPersonRepository.cs create mode 100644 AareonTechnicalTest/Repositories/ITicketRepository.cs create mode 100644 AareonTechnicalTest/Repositories/NoteRepository.cs create mode 100644 AareonTechnicalTest/Repositories/PersonRepository.cs create mode 100644 AareonTechnicalTest/Repositories/TicketRepository.cs create mode 100644 AareonTechnicalTest/Services/INoteService.cs create mode 100644 AareonTechnicalTest/Services/ITicketService.cs create mode 100644 AareonTechnicalTest/Services/NoteService.cs create mode 100644 AareonTechnicalTest/Services/TicketService.cs diff --git a/AareonTechnicalTest.Tests/AareonTechnicalTest.Tests.csproj b/AareonTechnicalTest.Tests/AareonTechnicalTest.Tests.csproj new file mode 100644 index 0000000..760d523 --- /dev/null +++ b/AareonTechnicalTest.Tests/AareonTechnicalTest.Tests.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + diff --git a/AareonTechnicalTest.Tests/NotesServiceShould.cs b/AareonTechnicalTest.Tests/NotesServiceShould.cs new file mode 100644 index 0000000..d63e32b --- /dev/null +++ b/AareonTechnicalTest.Tests/NotesServiceShould.cs @@ -0,0 +1,102 @@ +using AareonTechnicalTest.Models; +using AareonTechnicalTest.Repositories; +using AareonTechnicalTest.Services; +using FluentAssertions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Tests +{ + [TestClass] + public class NotesServiceShould + { + private INoteRepository? _noteRepository; + private NoteService? _noteService; + private IPersonRepository? _personRepository; + + [TestInitialize] + public void Initialise() + { + _noteRepository = Substitute.For(); + _personRepository = Substitute.For(); + + _noteService = new NoteService(_noteRepository, _personRepository); + } + + [TestMethod] + public async Task FailDeleteNoteWhenNoteNotFound() + { + var personId = 1; + var noteId = 1; + + Note? noteMock = null; + _noteRepository!.FindNoteAsync(noteId).Returns(noteMock); + + var response = await _noteService!.DeleteNoteAsync(personId, noteId); + var x = response + .Should() + .BeOfType(); + } + + [TestMethod] + public async Task FailDeleteNoteWhenPersonNotFound() + { + var personId = 1; + var noteId = 1; + + Note? noteMock = new Note { Id = noteId }; + _noteRepository!.FindNoteAsync(noteId).Returns(noteMock); + + Person? personMock = null; + _personRepository!.FindPersonAsync(noteId).Returns(personMock); + + var response = await _noteService!.DeleteNoteAsync(personId, noteId); + var x = response + .Should() + .BeOfType(); + } + + [TestMethod] + public async Task FailDeleteNoteWhenPersonIsNotAdmin() + { + var personId = 1; + var noteId = 1; + + Note? noteMock = new Note { Id = noteId }; + _noteRepository!.FindNoteAsync(noteId).Returns(noteMock); + + Person? personMock = new Person { Id = personId, IsAdmin = false }; + _personRepository!.FindPersonAsync(noteId).Returns(personMock); + + var response = await _noteService!.DeleteNoteAsync(personId, noteId); + var x = response + .Should() + .BeOfType(); + + var problemDetails = x.Subject.Value.Should().BeOfType().Which; + + problemDetails.Detail.Should().Be($"The person with ID {personId} is not an administrator.", because: "The person was not an administrator"); + } + + + [TestMethod] + public async Task DeleteNoteWhenPersonIsAdmin() + { + var personId = 1; + var noteId = 1; + + Note? noteMock = new Note { Id = noteId }; + _noteRepository!.FindNoteAsync(noteId).Returns(noteMock); + + Person? personMock = new Person { Id = personId, IsAdmin = true }; + _personRepository!.FindPersonAsync(noteId).Returns(personMock); + + var response = await _noteService!.DeleteNoteAsync(personId, noteId); + var x = response + .Should() + .BeOfType(); + } + } +} \ No newline at end of file diff --git a/AareonTechnicalTest.sln b/AareonTechnicalTest.sln index c3ca477..22e7dc9 100644 --- a/AareonTechnicalTest.sln +++ b/AareonTechnicalTest.sln @@ -1,9 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31624.102 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AareonTechnicalTest", "AareonTechnicalTest\AareonTechnicalTest.csproj", "{1FB831BF-946F-4526-AB25-E5CAFC6A8E4F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AareonTechnicalTest", "AareonTechnicalTest\AareonTechnicalTest.csproj", "{1FB831BF-946F-4526-AB25-E5CAFC6A8E4F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AareonTechnicalTest.Tests", "AareonTechnicalTest.Tests\AareonTechnicalTest.Tests.csproj", "{E46C0B0C-69A8-432B-9ABB-086E0B62740A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {1FB831BF-946F-4526-AB25-E5CAFC6A8E4F}.Debug|Any CPU.Build.0 = Debug|Any CPU {1FB831BF-946F-4526-AB25-E5CAFC6A8E4F}.Release|Any CPU.ActiveCfg = Release|Any CPU {1FB831BF-946F-4526-AB25-E5CAFC6A8E4F}.Release|Any CPU.Build.0 = Release|Any CPU + {E46C0B0C-69A8-432B-9ABB-086E0B62740A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E46C0B0C-69A8-432B-9ABB-086E0B62740A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E46C0B0C-69A8-432B-9ABB-086E0B62740A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E46C0B0C-69A8-432B-9ABB-086E0B62740A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AareonTechnicalTest/AareonTechnicalTest.csproj b/AareonTechnicalTest/AareonTechnicalTest.csproj index 601dd80..5f6c11a 100644 --- a/AareonTechnicalTest/AareonTechnicalTest.csproj +++ b/AareonTechnicalTest/AareonTechnicalTest.csproj @@ -1,18 +1,23 @@ - net5.0 + net6.0 - - - - + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + diff --git a/AareonTechnicalTest/ApplicationContext.cs b/AareonTechnicalTest/ApplicationContext.cs index 2218ef2..13f61fb 100644 --- a/AareonTechnicalTest/ApplicationContext.cs +++ b/AareonTechnicalTest/ApplicationContext.cs @@ -18,6 +18,8 @@ public ApplicationContext(DbContextOptions options) public virtual DbSet Tickets { get; set; } + public virtual DbSet Notes { get; set; } + public string DatabasePath { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) @@ -29,6 +31,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { PersonConfig.Configure(modelBuilder); TicketConfig.Configure(modelBuilder); + NoteConfig.Configure(modelBuilder); } } } diff --git a/AareonTechnicalTest/Controllers/NotesController.cs b/AareonTechnicalTest/Controllers/NotesController.cs new file mode 100644 index 0000000..c324942 --- /dev/null +++ b/AareonTechnicalTest/Controllers/NotesController.cs @@ -0,0 +1,50 @@ +using AareonTechnicalTest.Models; +using AareonTechnicalTest.Services; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class NotesController : ControllerBase + { + private readonly INoteService _noteService; + + public NotesController(INoteService noteService) + { + _noteService = noteService; + } + + [HttpGet] + public async Task>> GetNotes() + { + return await _noteService.GetNotesAsync(); + } + + [HttpGet("{id}")] + public async Task> GetNote(int id) + { + return await _noteService.GetNoteAsync(id); + } + + [HttpPut("{id}")] + public async Task PutNote(int id, Note note) + { + return await _noteService.PutNoteAsync(id, note); + } + + [HttpPost] + public async Task> PostNote(Note note) + { + return await _noteService.PostNoteAsync(note); + } + + [HttpDelete("{id}")] + public async Task DeleteNote(int personId, int id) + { + return await _noteService.DeleteNoteAsync(personId, id); + } + } +} diff --git a/AareonTechnicalTest/Controllers/TicketsController.cs b/AareonTechnicalTest/Controllers/TicketsController.cs new file mode 100644 index 0000000..58b68c7 --- /dev/null +++ b/AareonTechnicalTest/Controllers/TicketsController.cs @@ -0,0 +1,50 @@ +using AareonTechnicalTest.Models; +using AareonTechnicalTest.Services; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class TicketsController : ControllerBase + { + private readonly ITicketService _ticketService; + + public TicketsController(ITicketService ticketService) + { + _ticketService = ticketService; + } + + [HttpGet] + public async Task>> GetTickets() + { + return await _ticketService.GetTicketsAsync(); + } + + [HttpGet("{id}")] + public async Task> GetTicket(int id) + { + return await _ticketService.GetTicketAsync(id); + } + + [HttpPut("{id}")] + public async Task PutTicket(int id, Ticket ticket) + { + return await _ticketService.PutTicketAsync(id, ticket); + } + + [HttpPost] + public async Task> PostTicket(Ticket ticket) + { + return await _ticketService.PostTicketAsync(ticket); + } + + [HttpDelete("{id}")] + public async Task DeleteTicket(int id) + { + return await _ticketService.DeleteTicketAsync(id); + } + } +} diff --git a/AareonTechnicalTest/Migrations/20220119201548_AddNotes.Designer.cs b/AareonTechnicalTest/Migrations/20220119201548_AddNotes.Designer.cs new file mode 100644 index 0000000..68275ed --- /dev/null +++ b/AareonTechnicalTest/Migrations/20220119201548_AddNotes.Designer.cs @@ -0,0 +1,80 @@ +// +using AareonTechnicalTest; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AareonTechnicalTest.Migrations +{ + [DbContext(typeof(ApplicationContext))] + [Migration("20220119201548_AddNotes")] + partial class AddNotes + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.1"); + + modelBuilder.Entity("AareonTechnicalTest.Models.Note", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("PersonId") + .HasColumnType("INTEGER"); + + b.Property("TicketId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Notes"); + }); + + modelBuilder.Entity("AareonTechnicalTest.Models.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Forename") + .HasColumnType("TEXT"); + + b.Property("IsAdmin") + .HasColumnType("INTEGER"); + + b.Property("Surname") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Persons"); + }); + + modelBuilder.Entity("AareonTechnicalTest.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Content") + .HasColumnType("TEXT"); + + b.Property("PersonId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AareonTechnicalTest/Migrations/20220119201548_AddNotes.cs b/AareonTechnicalTest/Migrations/20220119201548_AddNotes.cs new file mode 100644 index 0000000..3d96f22 --- /dev/null +++ b/AareonTechnicalTest/Migrations/20220119201548_AddNotes.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AareonTechnicalTest.Migrations +{ + public partial class AddNotes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Notes", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Description = table.Column(type: "TEXT", nullable: true), + PersonId = table.Column(type: "INTEGER", nullable: false), + TicketId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Notes", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Notes"); + } + } +} diff --git a/AareonTechnicalTest/Migrations/ApplicationContextModelSnapshot.cs b/AareonTechnicalTest/Migrations/ApplicationContextModelSnapshot.cs index 4d48a1b..4153b28 100644 --- a/AareonTechnicalTest/Migrations/ApplicationContextModelSnapshot.cs +++ b/AareonTechnicalTest/Migrations/ApplicationContextModelSnapshot.cs @@ -1,6 +1,10 @@ // +using AareonTechnicalTest; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable namespace AareonTechnicalTest.Migrations { @@ -10,8 +14,27 @@ partial class ApplicationContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "5.0.11"); + modelBuilder.HasAnnotation("ProductVersion", "6.0.1"); + + modelBuilder.Entity("AareonTechnicalTest.Models.Note", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("PersonId") + .HasColumnType("INTEGER"); + + b.Property("TicketId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Notes"); + }); modelBuilder.Entity("AareonTechnicalTest.Models.Person", b => { diff --git a/AareonTechnicalTest/Models/Note.cs b/AareonTechnicalTest/Models/Note.cs new file mode 100644 index 0000000..02c9e9a --- /dev/null +++ b/AareonTechnicalTest/Models/Note.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace AareonTechnicalTest.Models +{ + public class Note + { + [Key] + public int Id { get; set; } + + public string Description { get; set; } + + public int PersonId { get; set; } + + public int TicketId { get; set; } + } +} diff --git a/AareonTechnicalTest/Models/NoteConfig.cs b/AareonTechnicalTest/Models/NoteConfig.cs new file mode 100644 index 0000000..356662c --- /dev/null +++ b/AareonTechnicalTest/Models/NoteConfig.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; + +namespace AareonTechnicalTest.Models +{ + public static class NoteConfig + { + public static void Configure(ModelBuilder modelBuilder) + { + modelBuilder.Entity( + entity => + { + entity.HasKey(e => e.Id); + }); + + modelBuilder.Entity( + entity => + { + entity.HasKey(e => e.Id); + }); + } + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Models/Person.cs b/AareonTechnicalTest/Models/Person.cs index 3797241..50a846c 100644 --- a/AareonTechnicalTest/Models/Person.cs +++ b/AareonTechnicalTest/Models/Person.cs @@ -5,7 +5,7 @@ namespace AareonTechnicalTest.Models public class Person { [Key] - public int Id { get; } + public int Id { get; set; } public string Forename { get; set; } diff --git a/AareonTechnicalTest/Models/PersonConfig.cs b/AareonTechnicalTest/Models/PersonConfig.cs index a2ed728..602858d 100644 --- a/AareonTechnicalTest/Models/PersonConfig.cs +++ b/AareonTechnicalTest/Models/PersonConfig.cs @@ -11,6 +11,12 @@ public static void Configure(ModelBuilder modelBuilder) { entity.HasKey(e => e.Id); }); + + modelBuilder.Entity( + entity => + { + entity.HasKey(e => e.Id); + }); } } } \ No newline at end of file diff --git a/AareonTechnicalTest/Models/TicketConfig.cs b/AareonTechnicalTest/Models/TicketConfig.cs index 0f5b6b0..25bc4c2 100644 --- a/AareonTechnicalTest/Models/TicketConfig.cs +++ b/AareonTechnicalTest/Models/TicketConfig.cs @@ -11,6 +11,12 @@ public static void Configure(ModelBuilder modelBuilder) { entity.HasKey(e => e.Id); }); + + modelBuilder.Entity( + entity => + { + entity.HasKey(e => e.Id); + }); } } } diff --git a/AareonTechnicalTest/Repositories/INoteRepository.cs b/AareonTechnicalTest/Repositories/INoteRepository.cs new file mode 100644 index 0000000..834bc91 --- /dev/null +++ b/AareonTechnicalTest/Repositories/INoteRepository.cs @@ -0,0 +1,21 @@ +using AareonTechnicalTest.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Repositories +{ + public interface INoteRepository + { + Task> LoadNotesAsync(); + + Task FindNoteAsync(int id); + + Task UpdateNoteAsync(int id, Note note); + + Task NoteExistsAsync(int id); + + Task AddNoteAsync(Note note); + + Task RemoveNoteAsync(Note note); + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Repositories/IPersonRepository.cs b/AareonTechnicalTest/Repositories/IPersonRepository.cs new file mode 100644 index 0000000..56c2137 --- /dev/null +++ b/AareonTechnicalTest/Repositories/IPersonRepository.cs @@ -0,0 +1,10 @@ +using AareonTechnicalTest.Models; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Repositories +{ + public interface IPersonRepository + { + Task FindPersonAsync(int id); + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Repositories/ITicketRepository.cs b/AareonTechnicalTest/Repositories/ITicketRepository.cs new file mode 100644 index 0000000..6137945 --- /dev/null +++ b/AareonTechnicalTest/Repositories/ITicketRepository.cs @@ -0,0 +1,21 @@ +using AareonTechnicalTest.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Repositories +{ + public interface ITicketRepository + { + Task> LoadTicketsAsync(); + + Task FindTicketAsync(int id); + + Task UpdateTicketAsync(int id, Ticket ticket); + + Task TicketExistsAsync(int id); + + Task AddTicketAsync(Ticket ticket); + + Task RemoveTicketAsync(Ticket ticket); + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Repositories/NoteRepository.cs b/AareonTechnicalTest/Repositories/NoteRepository.cs new file mode 100644 index 0000000..f5c2cc6 --- /dev/null +++ b/AareonTechnicalTest/Repositories/NoteRepository.cs @@ -0,0 +1,50 @@ +using AareonTechnicalTest.Models; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Repositories +{ + public class NoteRepository : INoteRepository + { + private readonly ApplicationContext _context; + + public NoteRepository(ApplicationContext context) + { + _context = context; + } + + public async Task> LoadNotesAsync() + { + return await _context.Notes.ToListAsync(); + } + + public async Task FindNoteAsync(int id) + { + return await _context.Notes.FindAsync(id); + } + + public async Task UpdateNoteAsync(int id, Note note) + { + _context.Entry(note).State = EntityState.Modified; + await _context.SaveChangesAsync(); + } + + public async Task NoteExistsAsync(int id) + { + return await _context.Notes.AnyAsync(e => e.Id == id); + } + + public async Task AddNoteAsync(Note note) + { + _context.Notes.Add(note); + await _context.SaveChangesAsync(); + } + + public async Task RemoveNoteAsync(Note note) + { + _context.Notes.Remove(note); + await _context.SaveChangesAsync(); + } + } +} diff --git a/AareonTechnicalTest/Repositories/PersonRepository.cs b/AareonTechnicalTest/Repositories/PersonRepository.cs new file mode 100644 index 0000000..1773ea1 --- /dev/null +++ b/AareonTechnicalTest/Repositories/PersonRepository.cs @@ -0,0 +1,20 @@ +using AareonTechnicalTest.Models; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Repositories +{ + public class PersonRepository : IPersonRepository + { + private readonly ApplicationContext _context; + + public PersonRepository(ApplicationContext context) + { + _context = context; + } + + public async Task FindPersonAsync(int id) + { + return await _context.Persons.FindAsync(id); + } + } +} diff --git a/AareonTechnicalTest/Repositories/TicketRepository.cs b/AareonTechnicalTest/Repositories/TicketRepository.cs new file mode 100644 index 0000000..8036120 --- /dev/null +++ b/AareonTechnicalTest/Repositories/TicketRepository.cs @@ -0,0 +1,50 @@ +using AareonTechnicalTest.Models; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Repositories +{ + public class TicketRepository : ITicketRepository + { + private readonly ApplicationContext _context; + + public TicketRepository(ApplicationContext context) + { + _context = context; + } + + public async Task AddTicketAsync(Ticket ticket) + { + _context.Tickets.Add(ticket); + await _context.SaveChangesAsync(); + } + + public async Task FindTicketAsync(int id) + { + return await _context.Tickets.FindAsync(id); + } + + public async Task> LoadTicketsAsync() + { + return await _context.Tickets.ToListAsync(); + } + + public async Task RemoveTicketAsync(Ticket ticket) + { + _context.Tickets.Remove(ticket); + await _context.SaveChangesAsync(); + } + + public async Task TicketExistsAsync(int id) + { + return await _context.Tickets.AnyAsync(e => e.Id == id); + } + + public async Task UpdateTicketAsync(int id, Ticket ticket) + { + _context.Entry(ticket).State = EntityState.Modified; + await _context.SaveChangesAsync(); + } + } +} diff --git a/AareonTechnicalTest/Services/INoteService.cs b/AareonTechnicalTest/Services/INoteService.cs new file mode 100644 index 0000000..708cf0d --- /dev/null +++ b/AareonTechnicalTest/Services/INoteService.cs @@ -0,0 +1,20 @@ +using AareonTechnicalTest.Models; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Services +{ + public interface INoteService + { + Task>> GetNotesAsync(); + + Task> GetNoteAsync(int id); + + Task PutNoteAsync(int id, Note note); + + Task> PostNoteAsync(Note note); + + Task DeleteNoteAsync(int personId, int id); + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Services/ITicketService.cs b/AareonTechnicalTest/Services/ITicketService.cs new file mode 100644 index 0000000..4eb3a1c --- /dev/null +++ b/AareonTechnicalTest/Services/ITicketService.cs @@ -0,0 +1,20 @@ +using AareonTechnicalTest.Models; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Services +{ + public interface ITicketService + { + Task>> GetTicketsAsync(); + + Task> GetTicketAsync(int id); + + Task PutTicketAsync(int id, Ticket ticket); + + Task> PostTicketAsync(Ticket ticket); + + Task DeleteTicketAsync(int id); + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Services/NoteService.cs b/AareonTechnicalTest/Services/NoteService.cs new file mode 100644 index 0000000..6f37d3e --- /dev/null +++ b/AareonTechnicalTest/Services/NoteService.cs @@ -0,0 +1,130 @@ +using AareonTechnicalTest.Models; +using AareonTechnicalTest.Repositories; +using Audit.Core; +using Microsoft.AspNetCore.Mvc; +using StackExchange.Profiling; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Services +{ + public class NoteService : INoteService + { + private readonly INoteRepository _noteRepository; + private readonly IPersonRepository _personRepository; + + public NoteService(INoteRepository noteRepository, IPersonRepository personRepository) + { + _noteRepository = noteRepository; + _personRepository = personRepository; + } + + public async Task>> GetNotesAsync() + { + using (MiniProfiler.Current.Step(nameof(GetNotesAsync))) + { + var notes = await _noteRepository.LoadNotesAsync(); + var scope = AuditScope.Create($"NoteService:{GetNotesAsync}", () => notes); + + return new OkObjectResult(notes); + } + } + + public async Task> GetNoteAsync(int id) + { + using (MiniProfiler.Current.Step(nameof(GetNoteAsync))) + { + var note = await _noteRepository.FindNoteAsync(id); + var scope = AuditScope.Create($"Note:{GetNoteAsync}", () => note); + + if (note is null) + { + return new NotFoundResult(); + } + + return note; + } + } + + public async Task PutNoteAsync(int id, Note note) + { + using (MiniProfiler.Current.Step(nameof(PutNoteAsync))) + { + if (id != note.Id) + { + return new BadRequestResult(); + } + + try + { + await _noteRepository.UpdateNoteAsync(id, note); + var scope = AuditScope.Create($"Note:{PutNoteAsync}", () => note); + } + catch (Exception ex) + { + if (!await _noteRepository.NoteExistsAsync(id)) + { + return new NotFoundResult(); + } + else + { + return new UnprocessableEntityObjectResult(new ProblemDetails + { + Title = "Note update could not be processed.", + Detail = $"{ex.Message}" + }); + } + } + + return new NoContentResult(); + } + } + + public async Task> PostNoteAsync(Note note) + { + using (MiniProfiler.Current.Step(nameof(PostNoteAsync))) + { + await _noteRepository.AddNoteAsync(note); + var scope = AuditScope.Create($"Note:{PostNoteAsync}", () => note); + + return new OkObjectResult(note); + } + } + + public async Task DeleteNoteAsync(int personId, int id) + { + using (MiniProfiler.Current.Step(nameof(DeleteNoteAsync))) + { + var note = await _noteRepository.FindNoteAsync(id); + if (note is null) + { + return new NotFoundResult(); + } + + var person = await _personRepository.FindPersonAsync(personId); + if (person is null) + { + return new UnprocessableEntityObjectResult(new ProblemDetails + { + Title = "Note deletion could not be processed.", + Detail = $"The person with ID {personId} could not be found." + }); + } + else if (!person.IsAdmin) + { + return new UnprocessableEntityObjectResult(new ProblemDetails + { + Title = "Note deletion could not be processed.", + Detail = $"The person with ID {personId} is not an administrator." + }); + } + + await _noteRepository.RemoveNoteAsync(note); + var scope = AuditScope.Create($"Note:{DeleteNoteAsync}", () => note); + + return new NoContentResult(); + } + } + } +} diff --git a/AareonTechnicalTest/Services/TicketService.cs b/AareonTechnicalTest/Services/TicketService.cs new file mode 100644 index 0000000..8f911d6 --- /dev/null +++ b/AareonTechnicalTest/Services/TicketService.cs @@ -0,0 +1,110 @@ +using AareonTechnicalTest.Models; +using AareonTechnicalTest.Repositories; +using Audit.Core; +using Microsoft.AspNetCore.Mvc; +using StackExchange.Profiling; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AareonTechnicalTest.Services +{ + public class TicketService : ITicketService + { + private readonly ITicketRepository _ticketRepository; + + public TicketService(ITicketRepository ticketRepository) + { + _ticketRepository = ticketRepository; + } + + public async Task>> GetTicketsAsync() + { + using (MiniProfiler.Current.Step(nameof(GetTicketsAsync))) + { + var tickets = await _ticketRepository.LoadTicketsAsync(); + var scope = AuditScope.Create($"Ticket:{GetTicketsAsync}", () => tickets); + + return new OkObjectResult(tickets); + } + } + + public async Task> GetTicketAsync(int id) + { + using (MiniProfiler.Current.Step(nameof(GetTicketAsync))) + { + var note = await _ticketRepository.FindTicketAsync(id); + var scope = AuditScope.Create($"Ticket:{GetTicketAsync}", () => note); + + if (note is null) + { + return new NotFoundResult(); + } + + return note; + } + } + + public async Task PutTicketAsync(int id, Ticket ticket) + { + using (MiniProfiler.Current.Step(nameof(PutTicketAsync))) + { + if (id != ticket.Id) + { + return new BadRequestResult(); + } + + try + { + await _ticketRepository.UpdateTicketAsync(id, ticket); + var scope = AuditScope.Create($"Ticket:{PutTicketAsync}", () => ticket); + } + catch (Exception ex) + { + if (!await _ticketRepository.TicketExistsAsync(id)) + { + return new NotFoundResult(); + } + else + { + return new UnprocessableEntityObjectResult(new ProblemDetails + { + Title = "Ticket update could not be processed.", + Detail = $"{ex.Message}" + }); + } + } + + return new NoContentResult(); + } + } + + public async Task> PostTicketAsync(Ticket ticket) + { + using (MiniProfiler.Current.Step(nameof(PostTicketAsync))) + { + await _ticketRepository.AddTicketAsync(ticket); + var scope = AuditScope.Create($"Ticket:{PostTicketAsync}", () => ticket); + + return new OkObjectResult(ticket); + } + } + + public async Task DeleteTicketAsync(int id) + { + using (MiniProfiler.Current.Step(nameof(DeleteTicketAsync))) + { + var ticket = await _ticketRepository.FindTicketAsync(id); + if (ticket is null) + { + return new NotFoundResult(); + } + + await _ticketRepository.RemoveTicketAsync(ticket); + var scope = AuditScope.Create($"Ticket:{DeleteTicketAsync}", () => ticket); + + return new NoContentResult(); + } + } + } +} diff --git a/AareonTechnicalTest/Startup.cs b/AareonTechnicalTest/Startup.cs index 7b12082..c1cf75c 100644 --- a/AareonTechnicalTest/Startup.cs +++ b/AareonTechnicalTest/Startup.cs @@ -1,3 +1,5 @@ +using AareonTechnicalTest.Repositories; +using AareonTechnicalTest.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -20,6 +22,11 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddControllers(); services.AddDbContext(c => c.UseSqlite()); @@ -39,6 +46,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AareonTechnicalTest v1")); } + app.UseMiniProfiler(); + app.UseHttpsRedirection(); app.UseRouting(); diff --git a/AareonTechnicalTest/Ticketing.db b/AareonTechnicalTest/Ticketing.db index 63aad03d8fd98748c0cd84a67ab1cb48011e8084..8b16d795ae4b8e276a3af4a303102507047d38b0 100644 GIT binary patch delta 363 zcmZoTz}WDBae}lU2Ll5GI}pPF&qN(#Ne%`*|5{%D9}H|fObmRN_}B7&<6Xk@h=*xo zV-~j${}0AP;@aAbO{pb`Nja&hY4IhgxdrhdnaSCyCB>;}jj`=S?o*)#cOS8tvKG#igYgn?k{cLoJ0;?3;L5m^qmEcQEkZ zIXrTW_N?sU($b7gp(Tk)IjJF;$=RtT#ZZcE6E6!h8zcWs2L79y1s%@wPrfOy4dg#y h;D4}LFyI>h<_G#J0ziS!4E&#g0x$VDfA;4U000$zDIfp< From 9e3378122bc556493e95e9b9bf8238a1ed9696e0 Mon Sep 17 00:00:00 2001 From: Mark Bonner Date: Thu, 20 Jan 2022 11:14:25 +0000 Subject: [PATCH 2/5] pipeline created --- .../AareonTechnicalTest20220120110002.yml | 76 ++++++++++ .../apis1.arm.json | 131 ++++++++++++++++++ ...ies.AareonTechnicalTest20220120110002.json | 8 ++ .../Properties/serviceDependencies.json | 7 + 4 files changed, 222 insertions(+) create mode 100644 .github/workflows/AareonTechnicalTest20220120110002.yml create mode 100644 AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest20220120110002/apis1.arm.json create mode 100644 AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest20220120110002.json create mode 100644 AareonTechnicalTest/Properties/serviceDependencies.json diff --git a/.github/workflows/AareonTechnicalTest20220120110002.yml b/.github/workflows/AareonTechnicalTest20220120110002.yml new file mode 100644 index 0000000..4d98b25 --- /dev/null +++ b/.github/workflows/AareonTechnicalTest20220120110002.yml @@ -0,0 +1,76 @@ +name: Build and deploy .NET Core application to windows webapp AareonTechnicalTest20220120110002 with API Management Service AareonTechnicalTestapi +on: + push: + branches: + - completed_test +env: + AZURE_WEBAPP_NAME: AareonTechnicalTest20220120110002 + DOTNET_CORE_VERSION: 6.0.x + WORKING_DIRECTORY: AareonTechnicalTest + CONFIGURATION: Release + AZURE_WEBAPP_PACKAGE_PATH: AareonTechnicalTest/publish + AZURE_APIM_RESOURCE_PATH: / + AZURE_APIM_RESOURCEGROUP: AareonTechnicalTest20220120105445ResourceGroup + AZURE_APIM_SERVICENAME: AareonTechnicalTestapi + AZURE_APIM_API_ID: AareonTechnicalTest + AZURE_APIM_APPSERVICEURL: https://aareontechnicaltest20220120110002.azurewebsites.net + SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION: 5.6.3 + SWASHBUCKLE_DOTNET_CORE_VERSION: 3.1.x + API_IMPORT_SPECIFICATION_PATH: AareonTechnicalTest/publish/swagger.json + API_IMPORT_DLL: AareonTechnicalTest/publish/AareonTechnicalTest.dll + API_IMPORT_VERSION: v1 +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.DOTNET_CORE_VERSION }} + - name: Setup SwashBuckle .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.SWASHBUCKLE_DOTNET_CORE_VERSION }} + - name: Restore + run: dotnet restore ${{ env.WORKING_DIRECTORY }} + - name: Build + run: dotnet build ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-restore + - name: Test + run: dotnet test ${{ env.WORKING_DIRECTORY }} --no-build + - name: Publish + run: dotnet publish ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-build --output ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + - name: Install Swashbuckle CLI .NET Global Tool + run: dotnet tool install --global Swashbuckle.AspNetCore.Cli --version ${{ env.SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION }} + working-directory: ${{ env.WORKING_DIRECTORY }} + - name: Generate Open API Specification Document + run: swagger tofile --output "${{ env.API_IMPORT_SPECIFICATION_PATH }}" "${{ env.API_IMPORT_DLL }}" "${{ env.API_IMPORT_VERSION }}" + - name: Publish Artifacts + uses: actions/upload-artifact@v1.0.0 + with: + name: webapp + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + deploy: + runs-on: windows-latest + needs: build + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v2 + with: + name: webapp + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + - name: Deploy to Azure WebApp + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ env.AZURE_WEBAPP_NAME }} + package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AareonTechnicalTest20220120110002_a6c3 }} + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AareonTechnicalTestapi_spn }} + - name: Import API into Azure API Management + run: az apim api import --path "${{ env.AZURE_APIM_RESOURCE_PATH }}" --resource-group "${{ env.AZURE_APIM_RESOURCEGROUP }}" --service-name "${{ env.AZURE_APIM_SERVICENAME }}" --api-id "${{ env.AZURE_APIM_API_ID }}" --service-url "${{ env.AZURE_APIM_APPSERVICEURL }}" --specification-path "${{ env.API_IMPORT_SPECIFICATION_PATH }}" --specification-format OpenApi --subscription-required false + - name: logout + run: > + az logout diff --git a/AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest20220120110002/apis1.arm.json b/AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest20220120110002/apis1.arm.json new file mode 100644 index 0000000..c77c522 --- /dev/null +++ b/AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest20220120110002/apis1.arm.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceGroupName": { + "type": "string", + "defaultValue": "AareonTechnicalTest20220120105445ResourceGroup", + "metadata": { + "_parameterType": "resourceGroup", + "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." + } + }, + "resourceGroupLocation": { + "type": "string", + "defaultValue": "southcentralus", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource group. Resource groups could have different location than resources." + } + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[parameters('resourceGroupLocation')]", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('resourceGroupLocation')]", + "apiVersion": "2019-10-01" + }, + { + "type": "Microsoft.Resources/deployments", + "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat('AareonTechnicalTest', subscription().subscriptionId)))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2019-10-01", + "dependsOn": [ + "[parameters('resourceGroupName')]" + ], + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "name": "AareonTechnicalTestapi", + "type": "Microsoft.ApiManagement/service", + "location": "[parameters('resourceLocation')]", + "properties": { + "publisherEmail": "mark@mbsoftwareconsulting.co.uk", + "publisherName": "Mark Bonner", + "notificationSenderEmail": "apimgmt-noreply@mail.windowsazure.com", + "hostnameConfigurations": [ + { + "type": "Proxy", + "hostName": "aareontechnicaltestapi.azure-api.net", + "encodedCertificate": null, + "keyVaultId": null, + "certificatePassword": null, + "negotiateClientCertificate": false, + "certificate": null, + "defaultSslBinding": true + } + ], + "publicIPAddresses": null, + "privateIPAddresses": null, + "additionalLocations": null, + "virtualNetworkConfiguration": null, + "customProperties": { + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Protocols.Server.Http2": "False" + }, + "virtualNetworkType": "None", + "certificates": null, + "apiVersionConstraint": { + "minApiVersion": null + } + }, + "sku": { + "name": "Consumption", + "capacity": 0 + }, + "apiVersion": "2019-12-01" + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "name": "AareonTechnicalTestapi/AareonTechnicalTest", + "properties": { + "displayName": "AareonTechnicalTest", + "apiRevision": "1", + "description": null, + "subscriptionRequired": true, + "serviceUrl": null, + "path": "", + "protocols": [ + "https" + ], + "authenticationSettings": { + "oAuth2": null, + "openid": null + }, + "subscriptionKeyParameterNames": { + "header": "Ocp-Apim-Subscription-Key", + "query": "subscription-key" + }, + "isCurrent": true + }, + "apiVersion": "2019-12-01", + "dependsOn": [ + "AareonTechnicalTestapi" + ] + } + ] + } + } + } + ], + "metadata": { + "_dependencyType": "apis.azure" + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest20220120110002.json b/AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest20220120110002.json new file mode 100644 index 0000000..a512a76 --- /dev/null +++ b/AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest20220120110002.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "apis1": { + "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.ApiManagement/service/AareonTechnicalTestapi/apis/AareonTechnicalTest", + "type": "apis.azure" + } + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Properties/serviceDependencies.json b/AareonTechnicalTest/Properties/serviceDependencies.json new file mode 100644 index 0000000..e32266d --- /dev/null +++ b/AareonTechnicalTest/Properties/serviceDependencies.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "apis1": { + "type": "apis" + } + } +} \ No newline at end of file From c8b2f31387b0b63f2fca733c9d9d1ecd5243b1b2 Mon Sep 17 00:00:00 2001 From: Mark Bonner Date: Thu, 20 Jan 2022 19:43:51 +0000 Subject: [PATCH 3/5] updated deployment --- .../AareonTechnicalTest202201201100021.yml | 76 ++++++++++ .../apis1.arm.json | 132 ++++++++++++++++++ ...es.AareonTechnicalTest202201201100021.json | 8 ++ 3 files changed, 216 insertions(+) create mode 100644 .github/workflows/AareonTechnicalTest202201201100021.yml create mode 100644 AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest202201201100021/apis1.arm.json create mode 100644 AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest202201201100021.json diff --git a/.github/workflows/AareonTechnicalTest202201201100021.yml b/.github/workflows/AareonTechnicalTest202201201100021.yml new file mode 100644 index 0000000..530309e --- /dev/null +++ b/.github/workflows/AareonTechnicalTest202201201100021.yml @@ -0,0 +1,76 @@ +name: Build and deploy .NET Core application to windows webapp AareonTechnicalTest20220120110002 with API Management Service AareonTechnicalTestapi +on: + push: + branches: + - completed_test +env: + AZURE_WEBAPP_NAME: AareonTechnicalTest20220120110002 + DOTNET_CORE_VERSION: 6.0.x + WORKING_DIRECTORY: AareonTechnicalTest + CONFIGURATION: Release + AZURE_WEBAPP_PACKAGE_PATH: AareonTechnicalTest/publish + AZURE_APIM_RESOURCE_PATH: / + AZURE_APIM_RESOURCEGROUP: AareonTechnicalTest20220120105445ResourceGroup + AZURE_APIM_SERVICENAME: AareonTechnicalTestapi + AZURE_APIM_API_ID: AareonTechnicalTest + AZURE_APIM_APPSERVICEURL: https://aareontechnicaltest20220120110002.azurewebsites.net + SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION: 5.6.3 + SWASHBUCKLE_DOTNET_CORE_VERSION: 3.1.x + API_IMPORT_SPECIFICATION_PATH: AareonTechnicalTest/publish/swagger.json + API_IMPORT_DLL: AareonTechnicalTest/publish/AareonTechnicalTest.dll + API_IMPORT_VERSION: v1 +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.DOTNET_CORE_VERSION }} + - name: Setup SwashBuckle .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.SWASHBUCKLE_DOTNET_CORE_VERSION }} + - name: Restore + run: dotnet restore ${{ env.WORKING_DIRECTORY }} + - name: Build + run: dotnet build ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-restore + - name: Test + run: dotnet test ${{ env.WORKING_DIRECTORY }} --no-build + - name: Publish + run: dotnet publish ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-build --output ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + - name: Install Swashbuckle CLI .NET Global Tool + run: dotnet tool install --global Swashbuckle.AspNetCore.Cli --version ${{ env.SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION }} + working-directory: ${{ env.WORKING_DIRECTORY }} + - name: Generate Open API Specification Document + run: swagger tofile --output "${{ env.API_IMPORT_SPECIFICATION_PATH }}" "${{ env.API_IMPORT_DLL }}" "${{ env.API_IMPORT_VERSION }}" + - name: Publish Artifacts + uses: actions/upload-artifact@v1.0.0 + with: + name: webapp + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + deploy: + runs-on: windows-latest + needs: build + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v2 + with: + name: webapp + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + - name: Deploy to Azure WebApp + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ env.AZURE_WEBAPP_NAME }} + package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.AareonTechnicalTest20220120110002_4333 }} + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AareonTechnicalTestapi_spn }} + - name: Import API into Azure API Management + run: az apim api import --path "${{ env.AZURE_APIM_RESOURCE_PATH }}" --resource-group "${{ env.AZURE_APIM_RESOURCEGROUP }}" --service-name "${{ env.AZURE_APIM_SERVICENAME }}" --api-id "${{ env.AZURE_APIM_API_ID }}" --service-url "${{ env.AZURE_APIM_APPSERVICEURL }}" --specification-path "${{ env.API_IMPORT_SPECIFICATION_PATH }}" --specification-format OpenApi --subscription-required false + - name: logout + run: > + az logout diff --git a/AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest202201201100021/apis1.arm.json b/AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest202201201100021/apis1.arm.json new file mode 100644 index 0000000..24580ca --- /dev/null +++ b/AareonTechnicalTest/Properties/ServiceDependencies/AareonTechnicalTest202201201100021/apis1.arm.json @@ -0,0 +1,132 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceGroupName": { + "type": "string", + "defaultValue": "AareonTechnicalTest20220120105445ResourceGroup", + "metadata": { + "_parameterType": "resourceGroup", + "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." + } + }, + "resourceGroupLocation": { + "type": "string", + "defaultValue": "southcentralus", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource group. Resource groups could have different location than resources." + } + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[parameters('resourceGroupLocation')]", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('resourceGroupLocation')]", + "apiVersion": "2019-10-01" + }, + { + "type": "Microsoft.Resources/deployments", + "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat('AareonTechnicalTest', subscription().subscriptionId)))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2019-10-01", + "dependsOn": [ + "[parameters('resourceGroupName')]" + ], + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "name": "AareonTechnicalTestapi", + "type": "Microsoft.ApiManagement/service", + "location": "[parameters('resourceLocation')]", + "properties": { + "publisherEmail": "mark@mbsoftwareconsulting.co.uk", + "publisherName": "Mark Bonner", + "notificationSenderEmail": "apimgmt-noreply@mail.windowsazure.com", + "hostnameConfigurations": [ + { + "type": "Proxy", + "hostName": "aareontechnicaltestapi.azure-api.net", + "encodedCertificate": null, + "keyVaultId": null, + "certificatePassword": null, + "negotiateClientCertificate": false, + "certificate": null, + "defaultSslBinding": true + } + ], + "publicIPAddresses": null, + "privateIPAddresses": null, + "additionalLocations": null, + "virtualNetworkConfiguration": null, + "customProperties": { + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Protocols.Server.Http2": "False" + }, + "virtualNetworkType": "None", + "certificates": null, + "enableClientCertificate": true, + "apiVersionConstraint": { + "minApiVersion": null + } + }, + "sku": { + "name": "Consumption", + "capacity": 0 + }, + "apiVersion": "2019-12-01" + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "name": "AareonTechnicalTestapi/AareonTechnicalTest", + "properties": { + "displayName": "AareonTechnicalTest", + "apiRevision": "1", + "description": null, + "subscriptionRequired": true, + "serviceUrl": null, + "path": "", + "protocols": [ + "https" + ], + "authenticationSettings": { + "oAuth2": null, + "openid": null + }, + "subscriptionKeyParameterNames": { + "header": "Ocp-Apim-Subscription-Key", + "query": "subscription-key" + }, + "isCurrent": true + }, + "apiVersion": "2019-12-01", + "dependsOn": [ + "AareonTechnicalTestapi" + ] + } + ] + } + } + } + ], + "metadata": { + "_dependencyType": "apis.azure" + } +} \ No newline at end of file diff --git a/AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest202201201100021.json b/AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest202201201100021.json new file mode 100644 index 0000000..a512a76 --- /dev/null +++ b/AareonTechnicalTest/Properties/serviceDependencies.AareonTechnicalTest202201201100021.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "apis1": { + "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.ApiManagement/service/AareonTechnicalTestapi/apis/AareonTechnicalTest", + "type": "apis.azure" + } + } +} \ No newline at end of file From 89e414f160abfc34fade6684b63e52e61462fa10 Mon Sep 17 00:00:00 2001 From: buzzbonner Date: Thu, 20 Jan 2022 22:19:09 +0000 Subject: [PATCH 4/5] Update AareonTechnicalTest20220120110002.yml --- .github/workflows/AareonTechnicalTest20220120110002.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/AareonTechnicalTest20220120110002.yml b/.github/workflows/AareonTechnicalTest20220120110002.yml index 4d98b25..530309e 100644 --- a/.github/workflows/AareonTechnicalTest20220120110002.yml +++ b/.github/workflows/AareonTechnicalTest20220120110002.yml @@ -64,7 +64,7 @@ jobs: with: app-name: ${{ env.AZURE_WEBAPP_NAME }} package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - publish-profile: ${{ secrets.AareonTechnicalTest20220120110002_a6c3 }} + publish-profile: ${{ secrets.AareonTechnicalTest20220120110002_4333 }} - name: Azure Login uses: azure/login@v1 with: From 801a66668dc3a06d37c1f95cd89706ac07d6bda4 Mon Sep 17 00:00:00 2001 From: Mark Bonner Date: Thu, 20 Jan 2022 22:30:32 +0000 Subject: [PATCH 5/5] updated workflow --- .../AareonTechnicalTest20220120110002.yml | 2 +- .../AareonTechnicalTest202201201100021.yml | 76 ------------------- 2 files changed, 1 insertion(+), 77 deletions(-) delete mode 100644 .github/workflows/AareonTechnicalTest202201201100021.yml diff --git a/.github/workflows/AareonTechnicalTest20220120110002.yml b/.github/workflows/AareonTechnicalTest20220120110002.yml index 4d98b25..49938cc 100644 --- a/.github/workflows/AareonTechnicalTest20220120110002.yml +++ b/.github/workflows/AareonTechnicalTest20220120110002.yml @@ -64,7 +64,7 @@ jobs: with: app-name: ${{ env.AZURE_WEBAPP_NAME }} package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - publish-profile: ${{ secrets.AareonTechnicalTest20220120110002_a6c3 }} + publish-profile: ${{ secrets.AAREONTECHNICALTEST20220120110002_4333 }} - name: Azure Login uses: azure/login@v1 with: diff --git a/.github/workflows/AareonTechnicalTest202201201100021.yml b/.github/workflows/AareonTechnicalTest202201201100021.yml deleted file mode 100644 index 530309e..0000000 --- a/.github/workflows/AareonTechnicalTest202201201100021.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Build and deploy .NET Core application to windows webapp AareonTechnicalTest20220120110002 with API Management Service AareonTechnicalTestapi -on: - push: - branches: - - completed_test -env: - AZURE_WEBAPP_NAME: AareonTechnicalTest20220120110002 - DOTNET_CORE_VERSION: 6.0.x - WORKING_DIRECTORY: AareonTechnicalTest - CONFIGURATION: Release - AZURE_WEBAPP_PACKAGE_PATH: AareonTechnicalTest/publish - AZURE_APIM_RESOURCE_PATH: / - AZURE_APIM_RESOURCEGROUP: AareonTechnicalTest20220120105445ResourceGroup - AZURE_APIM_SERVICENAME: AareonTechnicalTestapi - AZURE_APIM_API_ID: AareonTechnicalTest - AZURE_APIM_APPSERVICEURL: https://aareontechnicaltest20220120110002.azurewebsites.net - SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION: 5.6.3 - SWASHBUCKLE_DOTNET_CORE_VERSION: 3.1.x - API_IMPORT_SPECIFICATION_PATH: AareonTechnicalTest/publish/swagger.json - API_IMPORT_DLL: AareonTechnicalTest/publish/AareonTechnicalTest.dll - API_IMPORT_VERSION: v1 -jobs: - build: - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ env.DOTNET_CORE_VERSION }} - - name: Setup SwashBuckle .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ env.SWASHBUCKLE_DOTNET_CORE_VERSION }} - - name: Restore - run: dotnet restore ${{ env.WORKING_DIRECTORY }} - - name: Build - run: dotnet build ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-restore - - name: Test - run: dotnet test ${{ env.WORKING_DIRECTORY }} --no-build - - name: Publish - run: dotnet publish ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-build --output ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - - name: Install Swashbuckle CLI .NET Global Tool - run: dotnet tool install --global Swashbuckle.AspNetCore.Cli --version ${{ env.SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION }} - working-directory: ${{ env.WORKING_DIRECTORY }} - - name: Generate Open API Specification Document - run: swagger tofile --output "${{ env.API_IMPORT_SPECIFICATION_PATH }}" "${{ env.API_IMPORT_DLL }}" "${{ env.API_IMPORT_VERSION }}" - - name: Publish Artifacts - uses: actions/upload-artifact@v1.0.0 - with: - name: webapp - path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - deploy: - runs-on: windows-latest - needs: build - steps: - - name: Download artifact from build job - uses: actions/download-artifact@v2 - with: - name: webapp - path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - - name: Deploy to Azure WebApp - uses: azure/webapps-deploy@v2 - with: - app-name: ${{ env.AZURE_WEBAPP_NAME }} - package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - publish-profile: ${{ secrets.AareonTechnicalTest20220120110002_4333 }} - - name: Azure Login - uses: azure/login@v1 - with: - creds: ${{ secrets.AareonTechnicalTestapi_spn }} - - name: Import API into Azure API Management - run: az apim api import --path "${{ env.AZURE_APIM_RESOURCE_PATH }}" --resource-group "${{ env.AZURE_APIM_RESOURCEGROUP }}" --service-name "${{ env.AZURE_APIM_SERVICENAME }}" --api-id "${{ env.AZURE_APIM_API_ID }}" --service-url "${{ env.AZURE_APIM_APPSERVICEURL }}" --specification-path "${{ env.API_IMPORT_SPECIFICATION_PATH }}" --specification-format OpenApi --subscription-required false - - name: logout - run: > - az logout