diff --git a/.gitignore b/.gitignore index 41ffa34..26e1057 100644 --- a/.gitignore +++ b/.gitignore @@ -229,3 +229,6 @@ _Pvt_Extensions # FAKE - F# Make .fake/ + +/ClientApp/.angular/cache/14.0.3 + diff --git a/ajgre-technical-interview.csproj b/API/API.csproj similarity index 92% rename from ajgre-technical-interview.csproj rename to API/API.csproj index 55a2501..4630bd0 100644 --- a/ajgre-technical-interview.csproj +++ b/API/API.csproj @@ -1,10 +1,10 @@ - + net6.0 enable false - ClientApp\ +..\ClientApp\ https://localhost:44409 npm start ajgre_technical_interview @@ -22,6 +22,10 @@ + + + + diff --git a/API/Controllers/SanctionedEntitiesController.cs b/API/Controllers/SanctionedEntitiesController.cs new file mode 100644 index 0000000..f69ab2b --- /dev/null +++ b/API/Controllers/SanctionedEntitiesController.cs @@ -0,0 +1,50 @@ +using ajgre_technical_interview.Core.Entities; +using ajgre_technical_interview.Core.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ajgre_technical_interview.API.Controllers +{ + [ApiController] + [Route("api/sanctioned-entities")] + public class SanctionedEntitiesController : ControllerBase + { + private readonly ISanctionedEntityRepository _sanctionedEntityRepository; + + public SanctionedEntitiesController(ISanctionedEntityRepository repository) + { + _sanctionedEntityRepository = repository; + + } + + [HttpGet] + public async Task GetSanctionedEntities() + { + try + { + var entities = await _sanctionedEntityRepository.GetSanctionedEntities(); + return Ok(entities); + } + catch (Exception ex) + { + return Problem(ex.Message); + } + + } + + [HttpPost] + public async Task> Create(SanctionedEntity entity) + { + if (_sanctionedEntityRepository.Exists(entity.Name, entity.Domicile)) + { + return Conflict(new + { + message = $"A sanctioned entity with name '{entity.Name}' and domicile '{entity.Domicile}' already exists." + }); + } + var created = await _sanctionedEntityRepository.CreateSanctionedEntity(entity); + + return StatusCode(201, created); + } + + } +} diff --git a/Program.cs b/API/Program.cs similarity index 58% rename from Program.cs rename to API/Program.cs index c43b257..ab0919e 100644 --- a/Program.cs +++ b/API/Program.cs @@ -1,11 +1,24 @@ -using ajgre_technical_interview.Services; + +using ajgre_technical_interview.Core.Interfaces; +using ajgre_technical_interview.Infrastructure.Data; var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddCors(opt => +{ + opt.AddPolicy("CorsPolicy", policy => + { + policy.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:4409", "https://localhost:4409"); + }); +}); + + + // Add services to the container. builder.Services.AddControllersWithViews(); -builder.Services.AddSingleton(); +builder.Services.AddSingleton(); var app = builder.Build(); @@ -18,8 +31,12 @@ app.UseHttpsRedirection(); app.UseStaticFiles(); + + app.UseRouting(); +app.UseCors("CorsPolicy"); + app.MapControllerRoute( name: "default", diff --git a/API/Properties/launchSettings.json b/API/Properties/launchSettings.json new file mode 100644 index 0000000..280d5ff --- /dev/null +++ b/API/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "API": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" + }, + "applicationUrl": "https://localhost:65063;http://localhost:65064" + } + } +} \ No newline at end of file diff --git a/appsettings.Development.json b/API/appsettings.Development.json similarity index 100% rename from appsettings.Development.json rename to API/appsettings.Development.json diff --git a/appsettings.json b/API/appsettings.json similarity index 100% rename from appsettings.json rename to API/appsettings.json diff --git a/ClientApp/src/app/app.module.ts b/ClientApp/src/app/app.module.ts index f4fdf93..b587d98 100644 --- a/ClientApp/src/app/app.module.ts +++ b/ClientApp/src/app/app.module.ts @@ -3,13 +3,13 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { RouterModule } from '@angular/router'; - import { AppComponent } from './app.component'; import { NavMenuComponent } from './components/nav-menu/nav-menu.component'; import { HomeComponent } from './components/home/home.component'; import { CounterComponent } from './components/counter/counter.component'; -import { SanctionedEntitiesComponent } from './components/sanctioned-entities/sanctioned-entities.component'; import { JumbotronCounterComponent } from './components/jumbotron-counter/jumbotron-counter.component'; +import { SanctionedEntitiesComponent } from './features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component'; +import { SanctionedEntitiesModule } from './features/sanctioned-entities/sanctioned-entities.module'; @NgModule({ @@ -18,13 +18,13 @@ import { JumbotronCounterComponent } from './components/jumbotron-counter/jumbot NavMenuComponent, HomeComponent, CounterComponent, - SanctionedEntitiesComponent, - JumbotronCounterComponent + JumbotronCounterComponent, ], imports: [ BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), HttpClientModule, FormsModule, + SanctionedEntitiesModule, RouterModule.forRoot([ { path: '', component: HomeComponent, pathMatch: 'full' }, { path: 'counter', component: CounterComponent }, diff --git a/ClientApp/src/app/app.server.module.ts b/ClientApp/src/app/app.server.module.ts index cfb0e02..316380b 100644 --- a/ClientApp/src/app/app.server.module.ts +++ b/ClientApp/src/app/app.server.module.ts @@ -1,11 +1,10 @@ import { NgModule } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; -import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; import { AppComponent } from './app.component'; import { AppModule } from './app.module'; @NgModule({ - imports: [AppModule, ServerModule, ModuleMapLoaderModule], + imports: [AppModule, ServerModule], bootstrap: [AppComponent] }) export class AppServerModule { } diff --git a/ClientApp/src/app/components/counter/counter.component.html b/ClientApp/src/app/components/counter/counter.component.html index 89b9c80..92e1a94 100644 --- a/ClientApp/src/app/components/counter/counter.component.html +++ b/ClientApp/src/app/components/counter/counter.component.html @@ -1,7 +1,5 @@

Counter

-

This is a simple example of an Angular component.

- -

Current count: {{ currentCount }}

+

Current count: {{ count$ | async}}

diff --git a/ClientApp/src/app/components/counter/counter.component.spec.ts b/ClientApp/src/app/components/counter/counter.component.spec.ts deleted file mode 100644 index 09b3336..0000000 --- a/ClientApp/src/app/components/counter/counter.component.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CounterComponent } from './counter.component'; - -describe('CounterComponent', () => { - let component: CounterComponent; - let fixture: ComponentFixture; - - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [ CounterComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(CounterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should display a title', () => { - const titleText = fixture.nativeElement.querySelector('h1').textContent; - expect(titleText).toEqual('Counter'); - }); - - it('should start with count 0, then increments by 1 when clicked', () => { - const countElement = fixture.nativeElement.querySelector('strong'); - expect(countElement.textContent).toEqual('0'); - - const incrementButton = fixture.nativeElement.querySelector('button'); - incrementButton.click(); - fixture.detectChanges(); - expect(countElement.textContent).toEqual('1'); - }); -}); diff --git a/ClientApp/src/app/components/counter/counter.component.ts b/ClientApp/src/app/components/counter/counter.component.ts index 1f336aa..dbce56a 100644 --- a/ClientApp/src/app/components/counter/counter.component.ts +++ b/ClientApp/src/app/components/counter/counter.component.ts @@ -1,13 +1,16 @@ import { Component } from '@angular/core'; +import { CounterService } from 'src/app/services/counter.service'; @Component({ selector: 'app-counter-component', templateUrl: './counter.component.html' }) export class CounterComponent { - public currentCount = 0; + constructor(public counterService: CounterService) { } + count$ = this.counterService.count$; - public incrementCounter() { - this.currentCount++; + incrementCounter() { + this.counterService.incrementCounter(); } + } diff --git a/ClientApp/src/app/components/home/home.component.html b/ClientApp/src/app/components/home/home.component.html index 8c2e45b..cf50317 100644 --- a/ClientApp/src/app/components/home/home.component.html +++ b/ClientApp/src/app/components/home/home.component.html @@ -1,4 +1,3 @@ -

Hello, world!

Welcome to the technical interview for the position of Software Engineer at Gallagher Re.

This is a simple ASP.Net Core application with Angular. It includes following tabs:

    @@ -25,4 +24,4 @@

    Hello, world!

- + diff --git a/ClientApp/src/app/components/home/home.component.ts b/ClientApp/src/app/components/home/home.component.ts index 2747b30..bd3231f 100644 --- a/ClientApp/src/app/components/home/home.component.ts +++ b/ClientApp/src/app/components/home/home.component.ts @@ -1,8 +1,15 @@ import { Component } from '@angular/core'; +import { Observable } from 'rxjs'; +import { CounterService } from 'src/app/services/counter.service'; @Component({ selector: 'app-home', templateUrl: './home.component.html', }) export class HomeComponent { +count$!: Observable; + + constructor(private counterService: CounterService) { + this.count$ = this.counterService.count$; + } } diff --git a/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.html b/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.html index 4d39017..852a2c5 100644 --- a/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.html +++ b/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.html @@ -1,4 +1,3 @@
-

Current count:

-

Please include the counter here

+

Current count: {{count}}

diff --git a/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.ts b/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.ts index 6884723..684a731 100644 --- a/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.ts +++ b/ClientApp/src/app/components/jumbotron-counter/jumbotron-counter.component.ts @@ -1,8 +1,10 @@ -import { Component } from '@angular/core'; +import { Component, Input } from '@angular/core'; +import { Observable } from 'rxjs'; @Component({ selector: 'app-jumbotron-counter', templateUrl: './jumbotron-counter.component.html' }) export class JumbotronCounterComponent { + @Input() count!: number | null; } diff --git a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.ts b/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.ts deleted file mode 100644 index ca699d1..0000000 --- a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component } from '@angular/core'; -import { SanctionedEntity } from '../../models/sanctioned-entity'; -import { SanctionedEntitiesService } from '../../services/sanctioned-entities.service'; - -@Component({ - selector: 'app-sanctioned-entities', - templateUrl: './sanctioned-entities.component.html' -}) -export class SanctionedEntitiesComponent { - public entities: SanctionedEntity[] = []; - - constructor(private entitiesService: SanctionedEntitiesService) { - entitiesService.getSanctionedEntities().subscribe(entities => { - this.entities = entities; - }); - } -} diff --git a/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities.module.ts b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities.module.ts new file mode 100644 index 0000000..6e90002 --- /dev/null +++ b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SanctionedEntitiesComponent } from './sanctioned-entities/sanctioned-entities.component'; +import { AddSanctionedEntityComponent } from './sanctioned-entity-form/sanctioned-entity-form.component'; +import { ReactiveFormsModule } from '@angular/forms'; + + + +@NgModule({ + declarations: [ + SanctionedEntitiesComponent, + AddSanctionedEntityComponent + ], + imports: [ + CommonModule, + ReactiveFormsModule + ] +}) +export class SanctionedEntitiesModule { } diff --git a/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.css b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.css new file mode 100644 index 0000000..e69de29 diff --git a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.html b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.html similarity index 77% rename from ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.html rename to ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.html index 9699666..8b3968e 100644 --- a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.html +++ b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.html @@ -11,7 +11,7 @@

Sanctioned Entities

- + {{ entity.name }} {{ entity.domicile }} Accepted @@ -19,3 +19,5 @@

Sanctioned Entities

+ + diff --git a/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.ts b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.ts new file mode 100644 index 0000000..8bbcc40 --- /dev/null +++ b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entities/sanctioned-entities.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { take } from 'rxjs'; +import { SanctionedEntity } from 'src/app/models/sanctioned-entity'; +import { SanctionedEntitiesService } from 'src/app/services/sanctioned-entities.service'; + +@Component({ + selector: 'app-sanctioned-entities', + templateUrl: './sanctioned-entities.component.html', + styleUrls: ['./sanctioned-entities.component.css'] +}) +export class SanctionedEntitiesComponent implements OnInit { + public entities: SanctionedEntity[] = []; + + constructor(private entitiesService: SanctionedEntitiesService) { } + + ngOnInit(): void { + this.loadEntities(); + } + + loadEntities(): void { + this.entitiesService.getSanctionedEntities().pipe(take(1)).subscribe(entities => { + this.entities = entities; + }); + } + onEntityAdded(entity: SanctionedEntity) { + this.entities.push(entity); + } + + trackByEntityId(index: number, entity: SanctionedEntity): string { + return entity.id; +} + +} diff --git a/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.css b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.css new file mode 100644 index 0000000..e69de29 diff --git a/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.html b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.html new file mode 100644 index 0000000..410322c --- /dev/null +++ b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.html @@ -0,0 +1,34 @@ +
+

Add Sanctioned Entity

+ +
+ + +
+ Name is required. + Name must be at least 2 characters. +
+
+ +
+ + +
+ Domicile is required. +
+
+ +
+ + +
+ + + + +
+ {{ outcomeMessage }} +
+
\ No newline at end of file diff --git a/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.ts b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.ts new file mode 100644 index 0000000..fbc687b --- /dev/null +++ b/ClientApp/src/app/features/sanctioned-entities/sanctioned-entity-form/sanctioned-entity-form.component.ts @@ -0,0 +1,52 @@ +import { Component, Output, EventEmitter } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { SanctionedEntitiesService } from '../../../services/sanctioned-entities.service'; +import { SanctionedEntity } from 'src/app/models/sanctioned-entity'; + +@Component({ + selector: 'app-sanctioned-entity-form', + templateUrl: './sanctioned-entity-form.component.html', + styleUrls: ['./sanctioned-entity-form.component.css'] +}) +export class AddSanctionedEntityComponent { +@Output() entityAdded = new EventEmitter(); + form!: FormGroup; + submitted = false; + outcomeMessage = ''; + + constructor(private fb: FormBuilder, private service: SanctionedEntitiesService) {} + + ngOnInit(): void { + this.form = this.fb.group({ + name: ['', [Validators.required, Validators.minLength(2)]], + domicile: ['', Validators.required], + accepted: [false] + }); + } + + submit(): void { + this.submitted = true; + if (this.form.invalid) return; + + this.service.addSanctionedEntity(this.form.value).subscribe({ + next: entity => { + this.outcomeMessage = 'Entity added successfully!'; + this.form.reset({ accepted: false }); + this.submitted = false; + this.entityAdded.emit(entity); + }, + error: err => { + if (err.status === 409 && err.error?.message) { + this.outcomeMessage = err.error?.message; + } else { + this.outcomeMessage = 'Failed to add entity.'; + } + this.submitted = false; + } + }); + } + + get f() { + return this.form.controls; + } +} \ No newline at end of file diff --git a/ClientApp/src/app/services/counter.service.ts b/ClientApp/src/app/services/counter.service.ts new file mode 100644 index 0000000..1e4febe --- /dev/null +++ b/ClientApp/src/app/services/counter.service.ts @@ -0,0 +1,16 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class CounterService { + + private countSubject = new BehaviorSubject(0); + readonly count$ = this.countSubject.asObservable(); + + incrementCounter(): void { + this.countSubject.next(this.countSubject.value + 1); + } +} diff --git a/ClientApp/src/app/services/sanctioned-entities.service.ts b/ClientApp/src/app/services/sanctioned-entities.service.ts index 1a90e11..de73ebc 100644 --- a/ClientApp/src/app/services/sanctioned-entities.service.ts +++ b/ClientApp/src/app/services/sanctioned-entities.service.ts @@ -19,4 +19,11 @@ export class SanctionedEntitiesService { const url = this.apiUrl + this.path; return this.http.get(url); } + + public addSanctionedEntity(entity: SanctionedEntity): Observable { + const url = this.apiUrl + this.path; + return this.http.post(url, entity); + } + + } diff --git a/Controllers/SanctionedEntitiesController.cs b/Controllers/SanctionedEntitiesController.cs deleted file mode 100644 index e6946c6..0000000 --- a/Controllers/SanctionedEntitiesController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using ajgre_technical_interview.Services; -using Microsoft.AspNetCore.Mvc; - -namespace ajgre_technical_interview.Controllers -{ - [ApiController] - [Route("api/sanctioned-entities")] - public class SanctionedEntitiesController : ControllerBase - { - private readonly IDatabaseService _databaseService; - - public SanctionedEntitiesController(IDatabaseService databaseService) - { - _databaseService = databaseService; - } - - - [HttpGet] - public async Task GetSanctionedEntities() - { - try - { - var entities = await _databaseService.GetSanctionedEntitiesAsync(); - return Ok(entities); - } - catch (Exception ex) - { - return Problem(ex.Message); - } - - } - } -} diff --git a/Core/Core.csproj b/Core/Core.csproj new file mode 100644 index 0000000..817fc9b --- /dev/null +++ b/Core/Core.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + 8.0 + disable + enable + + + + + + + diff --git a/Models/SanctionedEntity.cs b/Core/Entities/SanctionedEntity.cs similarity index 50% rename from Models/SanctionedEntity.cs rename to Core/Entities/SanctionedEntity.cs index ab343c5..926afc7 100644 --- a/Models/SanctionedEntity.cs +++ b/Core/Entities/SanctionedEntity.cs @@ -1,9 +1,16 @@ -namespace ajgre_technical_interview.Models +using System; +using System.ComponentModel.DataAnnotations; + +namespace ajgre_technical_interview.Core.Entities { public class SanctionedEntity { - public Guid Id => Guid.NewGuid(); + public Guid Id { get; set; } = Guid.NewGuid(); + + [Required] public string Name { get; set; } = string.Empty; + + [Required] public string Domicile { get; set; } = string.Empty; public bool Accepted { get; set; } } diff --git a/Core/Interfaces/ISanctionedEntityRepository.cs b/Core/Interfaces/ISanctionedEntityRepository.cs new file mode 100644 index 0000000..8c0139b --- /dev/null +++ b/Core/Interfaces/ISanctionedEntityRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using ajgre_technical_interview.Core.Entities; + +namespace ajgre_technical_interview.Core.Interfaces +{ + public interface ISanctionedEntityRepository + { + Task> GetSanctionedEntities(); + + Task GetSanctionedEntityById(Guid id); + + Task CreateSanctionedEntity(SanctionedEntity sanctionedEntity); + + bool Exists(string name, string domicile); + } +} diff --git a/Infrastructure/Data/SanctionedEntityRepository.cs b/Infrastructure/Data/SanctionedEntityRepository.cs new file mode 100644 index 0000000..3b9d7cc --- /dev/null +++ b/Infrastructure/Data/SanctionedEntityRepository.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ajgre_technical_interview.Core.Entities; +using ajgre_technical_interview.Core.Interfaces; + +namespace ajgre_technical_interview.Infrastructure.Data +{ + public class SanctionedEntityRepository : ISanctionedEntityRepository + { + private static readonly IList SanctionedEntities = new List + { + new SanctionedEntity { Name = "Forbidden Company", Domicile = "Mars", Accepted = false }, + new SanctionedEntity { Name = "Allowed Company", Domicile = "Venus", Accepted = true }, + new SanctionedEntity { Name = "Good Ltd", Domicile = "Saturn", Accepted = true }, + new SanctionedEntity { Name = "Evil Plc", Domicile = "Venus", Accepted = false } + }; + + public Task> GetSanctionedEntities() + { + IList entities = SanctionedEntities + .OrderBy(e => e.Name) + .ThenBy(e => e.Domicile) + .ToList(); + + return Task.FromResult(entities); + } + public Task GetSanctionedEntityById(Guid id) + { + return Task.FromResult(SanctionedEntities.First(e => e.Id.Equals(id))); + } + + public Task CreateSanctionedEntity(SanctionedEntity entity) + { + entity.Id = Guid.NewGuid(); + SanctionedEntities.Add(entity); + return Task.FromResult(entity); + + } + public bool Exists(string name, string domicile) + { + return SanctionedEntities.Any(e => + string.Equals(e.Name, name, StringComparison.OrdinalIgnoreCase) && + string.Equals(e.Domicile, domicile, StringComparison.OrdinalIgnoreCase)); + } + } +} + + diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj new file mode 100644 index 0000000..eed9dbb --- /dev/null +++ b/Infrastructure/Infrastructure.csproj @@ -0,0 +1,14 @@ + + + + + + + + netstandard2.0 + 8.0 + disable + enable + + + diff --git a/Pages/Error.cshtml b/Pages/Error.cshtml deleted file mode 100644 index 6f92b95..0000000 --- a/Pages/Error.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@page -@model ErrorModel -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -@if (Model.ShowRequestId) -{ -

- Request ID: @Model.RequestId -

-} - -

Development Mode

-

- Swapping to the Development environment displays detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

diff --git a/Pages/Error.cshtml.cs b/Pages/Error.cshtml.cs deleted file mode 100644 index 58ff5dd..0000000 --- a/Pages/Error.cshtml.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using System.Diagnostics; - -namespace ajgre_technical_interview.Pages -{ - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public class ErrorModel : PageModel - { - private readonly ILogger _logger; - - public ErrorModel(ILogger logger) - { - _logger = logger; - } - - public string? RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } - } -} \ No newline at end of file diff --git a/Pages/_ViewImports.cshtml b/Pages/_ViewImports.cshtml deleted file mode 100644 index fa37fb1..0000000 --- a/Pages/_ViewImports.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@using ajgre_technical_interview -@namespace ajgre_technical_interview.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs deleted file mode 100644 index 330647d..0000000 --- a/Services/DatabaseService.cs +++ /dev/null @@ -1,36 +0,0 @@ -using ajgre_technical_interview.Models; - -namespace ajgre_technical_interview.Services -{ - public class DatabaseService : IDatabaseService - { - private static readonly IList SanctionedEntities = new List - { - new SanctionedEntity { Name = "Forbidden Company", Domicile = "Mars", Accepted = false }, - new SanctionedEntity { Name = "Allowed Company", Domicile = "Venus", Accepted = true }, - new SanctionedEntity { Name = "Good Ltd", Domicile = "Saturn", Accepted = true }, - new SanctionedEntity { Name = "Evil Plc", Domicile = "Venus", Accepted = false } - }; - - public async Task> GetSanctionedEntitiesAsync() - { - var entities = SanctionedEntities - .OrderBy(e => e.Name) - .ThenBy(e => e.Domicile) - .ToList(); - - return await Task.FromResult(entities); - } - - public async Task GetSanctionedEntityByIdAsync(Guid id) - { - return await Task.FromResult(SanctionedEntities.First(e => e.Id.Equals(id))); - } - - public async Task CreateSanctionedEntityAsync(SanctionedEntity sanctionedEntity) - { - SanctionedEntities.Add(sanctionedEntity); - return await Task.FromResult(sanctionedEntity); - } - } -} \ No newline at end of file diff --git a/Services/IDatabaseService.cs b/Services/IDatabaseService.cs deleted file mode 100644 index 2dfff96..0000000 --- a/Services/IDatabaseService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using ajgre_technical_interview.Models; - -namespace ajgre_technical_interview.Services -{ - public interface IDatabaseService - { - Task> GetSanctionedEntitiesAsync(); - - Task GetSanctionedEntityByIdAsync(Guid id); - - Task CreateSanctionedEntityAsync(SanctionedEntity sanctionedEntity); - } -} \ No newline at end of file diff --git a/ajgre-technical-interview.sln b/ajgre-technical-interview.sln index 9eee50e..852fb76 100644 --- a/ajgre-technical-interview.sln +++ b/ajgre-technical-interview.sln @@ -3,18 +3,58 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.33627.172 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ajgre-technical-interview", "ajgre-technical-interview.csproj", "{4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{D2BAC694-9D2D-43A1-838C-7820F0B701B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Debug|x64.ActiveCfg = Debug|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Debug|x64.Build.0 = Debug|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Debug|x86.ActiveCfg = Debug|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Debug|x86.Build.0 = Debug|Any CPU {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Release|Any CPU.ActiveCfg = Release|Any CPU {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Release|Any CPU.Build.0 = Release|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Release|x64.ActiveCfg = Release|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Release|x64.Build.0 = Release|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Release|x86.ActiveCfg = Release|Any CPU + {4C0BCE9E-A4A4-4A97-BB77-3EE7699F037A}.Release|x86.Build.0 = Release|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Debug|x64.Build.0 = Debug|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Debug|x86.Build.0 = Debug|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Release|Any CPU.Build.0 = Release|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Release|x64.ActiveCfg = Release|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Release|x64.Build.0 = Release|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Release|x86.ActiveCfg = Release|Any CPU + {D2BAC694-9D2D-43A1-838C-7820F0B701B8}.Release|x86.Build.0 = Release|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Debug|x64.ActiveCfg = Debug|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Debug|x64.Build.0 = Debug|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Debug|x86.ActiveCfg = Debug|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Debug|x86.Build.0 = Debug|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Release|Any CPU.Build.0 = Release|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Release|x64.ActiveCfg = Release|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Release|x64.Build.0 = Release|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Release|x86.ActiveCfg = Release|Any CPU + {3B5EC94C-3FE0-4B34-9BA9-9DB364E546CE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE