diff --git a/ClientApp/src/app/app.module.ts b/ClientApp/src/app/app.module.ts index f4fdf93..61cdaf8 100644 --- a/ClientApp/src/app/app.module.ts +++ b/ClientApp/src/app/app.module.ts @@ -10,6 +10,7 @@ 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 { CreateSanctionedEntityComponent } from './components/create-sanctioned-entity/create-sanctioned-entity.component'; @NgModule({ @@ -19,7 +20,8 @@ import { JumbotronCounterComponent } from './components/jumbotron-counter/jumbot HomeComponent, CounterComponent, SanctionedEntitiesComponent, - JumbotronCounterComponent + JumbotronCounterComponent, + CreateSanctionedEntityComponent ], imports: [ BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), @@ -29,6 +31,7 @@ import { JumbotronCounterComponent } from './components/jumbotron-counter/jumbot { path: '', component: HomeComponent, pathMatch: 'full' }, { path: 'counter', component: CounterComponent }, { path: 'sanctioned-entities', component: SanctionedEntitiesComponent }, + { path: 'create-sanction-entity', component: CreateSanctionedEntityComponent } ]) ], providers: [], diff --git a/ClientApp/src/app/components/counter/counter.component.html b/ClientApp/src/app/components/counter/counter.component.html index 89b9c80..3dd9ffa 100644 --- a/ClientApp/src/app/components/counter/counter.component.html +++ b/ClientApp/src/app/components/counter/counter.component.html @@ -2,6 +2,6 @@

Counter

This is a simple example of an Angular component.

-

Current count: {{ currentCount }}

+

Current count: {{ counter$ | async}}

diff --git a/ClientApp/src/app/components/counter/counter.component.ts b/ClientApp/src/app/components/counter/counter.component.ts index 1f336aa..94df8bc 100644 --- a/ClientApp/src/app/components/counter/counter.component.ts +++ b/ClientApp/src/app/components/counter/counter.component.ts @@ -1,13 +1,21 @@ -import { Component } from '@angular/core'; +import { Component, Input, OnDestroy } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { CounterService } from 'src/app/services/counter.service'; @Component({ selector: 'app-counter-component', templateUrl: './counter.component.html' }) export class CounterComponent { - public currentCount = 0; + public counter$: Observable; + + @Input('minimalistMode') minimalistMode: boolean = false; + + constructor(private counterService: CounterService) { + this.counter$ = this.counterService.getCounter(); + } public incrementCounter() { - this.currentCount++; + this.counterService.increaseCounter(); } } diff --git a/ClientApp/src/app/components/create-sanctioned-entity/create-sanctioned-entity.component.html b/ClientApp/src/app/components/create-sanctioned-entity/create-sanctioned-entity.component.html new file mode 100644 index 0000000..04791c6 --- /dev/null +++ b/ClientApp/src/app/components/create-sanctioned-entity/create-sanctioned-entity.component.html @@ -0,0 +1,31 @@ +

Create a sanction

+ +
+
+ + + +
Please provide an entity name.
+
+
+
+ + + +
Please provide a domicile.
+
+
+
+ + +
+
+
+ + + +
+
\ No newline at end of file diff --git a/ClientApp/src/app/components/create-sanctioned-entity/create-sanctioned-entity.component.ts b/ClientApp/src/app/components/create-sanctioned-entity/create-sanctioned-entity.component.ts new file mode 100644 index 0000000..7bb44d5 --- /dev/null +++ b/ClientApp/src/app/components/create-sanctioned-entity/create-sanctioned-entity.component.ts @@ -0,0 +1,41 @@ +import { Component } from '@angular/core'; +import { SanctionedEntity } from '../../models/sanctioned-entity'; +import { SanctionedEntitiesService } from '../../services/sanctioned-entities.service'; +import { NgForm } from '@angular/forms'; +import { Router } from '@angular/router'; +import { catchError, throwError } from 'rxjs'; + +@Component({ + selector: 'app-sanctioned-entities', + templateUrl: './create-sanctioned-entity.component.html' +}) +export class CreateSanctionedEntityComponent { + public newEntity: SanctionedEntity; + public serverError: string|null = null; + + constructor(private entitiesService: SanctionedEntitiesService, private router: Router) { + this.newEntity = { id: '', accepted: false, domicile: '', name: ''}; + } + + public save(form: NgForm): void { + + if(form.valid && form.enabled){ + this.entitiesService.createNewEntity(this.newEntity) + .pipe( + catchError(response => { + this.serverError = response.error.detail; + + return throwError(response); + }) + ) + .subscribe(newEntity => { + + if(newEntity){ + this.router.navigate(['/sanctioned-entities']); + } + + }); + } + } + +} 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..f00c4f1 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,15 @@

Current count:

-

Please include the counter here

+

{{ counter$ | async}}

+ + + + + +
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..7d407f2 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,16 @@ import { Component } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { CounterService } from 'src/app/services/counter.service'; @Component({ selector: 'app-jumbotron-counter', templateUrl: './jumbotron-counter.component.html' }) export class JumbotronCounterComponent { + public counter$: Observable; + + constructor(private counterService: CounterService){ + this.counter$ = this.counterService.getCounter(); + } + } diff --git a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.html b/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.html index 9699666..c012fdd 100644 --- a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.html +++ b/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.html @@ -1,7 +1,9 @@

Sanctioned Entities

+

Loading...

+ diff --git a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.ts b/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.ts index ca699d1..3dbe241 100644 --- a/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.ts +++ b/ClientApp/src/app/components/sanctioned-entities/sanctioned-entities.component.ts @@ -14,4 +14,5 @@ export class SanctionedEntitiesComponent { this.entities = entities; }); } + } diff --git a/ClientApp/src/app/services/counter.service.ts b/ClientApp/src/app/services/counter.service.ts new file mode 100644 index 0000000..a0a4fe0 --- /dev/null +++ b/ClientApp/src/app/services/counter.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class CounterService { + + private counterSubject: BehaviorSubject = new BehaviorSubject(0); + private counter$ = this.counterSubject.asObservable(); + + constructor() { + + } + + public getCounter(): Observable { + return this.counter$; + } + + public increaseCounter() : void{ + this.counterSubject.next(this.counterSubject.value + 1); + } +} diff --git a/ClientApp/src/app/services/sanctioned-entities.service.ts b/ClientApp/src/app/services/sanctioned-entities.service.ts index 1a90e11..ea5e744 100644 --- a/ClientApp/src/app/services/sanctioned-entities.service.ts +++ b/ClientApp/src/app/services/sanctioned-entities.service.ts @@ -19,4 +19,10 @@ export class SanctionedEntitiesService { const url = this.apiUrl + this.path; return this.http.get(url); } + + public createNewEntity(newEntity: SanctionedEntity): Observable { + const url = this.apiUrl + this.path; + + return this.http.post(url, newEntity); + } } diff --git a/Controllers/SanctionedEntitiesController.cs b/Controllers/SanctionedEntitiesController.cs index e6946c6..089d709 100644 --- a/Controllers/SanctionedEntitiesController.cs +++ b/Controllers/SanctionedEntitiesController.cs @@ -1,4 +1,5 @@ -using ajgre_technical_interview.Services; +using ajgre_technical_interview.Models; +using ajgre_technical_interview.Services; using Microsoft.AspNetCore.Mvc; namespace ajgre_technical_interview.Controllers @@ -8,10 +9,12 @@ namespace ajgre_technical_interview.Controllers public class SanctionedEntitiesController : ControllerBase { private readonly IDatabaseService _databaseService; + private readonly ILogger _logger; - public SanctionedEntitiesController(IDatabaseService databaseService) + public SanctionedEntitiesController(IDatabaseService databaseService, ILogger logger) { _databaseService = databaseService; + _logger = logger; } @@ -25,9 +28,27 @@ public async Task GetSanctionedEntities() } catch (Exception ex) { + //A little logging is always nice to have! + _logger.LogError(ex, $"Exception occured in route '{nameof(GetSanctionedEntities)}'"); return Problem(ex.Message); } } + + [HttpPost] + public async Task CreateSanctionEntity([FromBody] SanctionedEntity newSanctionEntity) + { + try + { + var newEntity = await _databaseService.CreateSanctionedEntityAsync(newSanctionEntity); + return Ok(newEntity); + } + catch (Exception ex) + { + //A little logging is always nice to have! + _logger.LogError(ex, $"Exception occured in route '{nameof(CreateSanctionEntity)}'"); + return Problem(ex.Message); + } + } } } diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs index 330647d..ab69938 100644 --- a/Services/DatabaseService.cs +++ b/Services/DatabaseService.cs @@ -29,6 +29,16 @@ public async Task GetSanctionedEntityByIdAsync(Guid id) public async Task CreateSanctionedEntityAsync(SanctionedEntity sanctionedEntity) { + //Validate that there is not a sanctioned entity with the same name and domicile already. + //Would normally seperate this validation into a step in a service sitting between DB and the controller. + var newEntityIsValid = !SanctionedEntities.Any(x => x.Name == sanctionedEntity.Name + && x.Domicile == sanctionedEntity.Domicile); + + if (!newEntityIsValid) + { + throw new Exception("A sanctioned entity with the same name and domicile already exists."); + } + SanctionedEntities.Add(sanctionedEntity); return await Task.FromResult(sanctionedEntity); }