Commit 16167618 authored by Andres Käver's avatar Andres Käver

animals

parent c37a2887
......@@ -9,12 +9,15 @@ using Microsoft.EntityFrameworkCore;
using DAL.App.EF;
using Domain;
using Extensions;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using PublicApi.DTO.v1;
namespace WebApp.ApiControllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class AnimalsController : ControllerBase
{
private readonly IAppUnitOfWork _uow;
......@@ -26,14 +29,14 @@ namespace WebApp.ApiControllers
// GET: api/Animals
[HttpGet]
public async Task<ActionResult<IEnumerable<Animal>>> GetAnimals()
public async Task<ActionResult<IEnumerable<AnimalDTO>>> GetAnimals()
{
return Ok(await _uow.Animals.DTOAllAsync(User.UserGuidId()));
}
// GET: api/Animals/5
[HttpGet("{id}")]
public async Task<ActionResult<Animal>> GetAnimal(Guid id)
public async Task<ActionResult<AnimalDTO>> GetAnimal(Guid id)
{
var animalDTO = await _uow.Animals.DTOFirstOrDefaultAsync(id, User.UserGuidId());
......@@ -87,13 +90,15 @@ namespace WebApp.ApiControllers
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task<ActionResult<Animal>> PostAnimal(AnimalCreateDTO animalCreateDTO)
public async Task<ActionResult<AnimalEditDTO>> PostAnimal(AnimalCreateDTO animalCreateDTO)
{
var animal = new Animal();
animal.AnimalName = animalCreateDTO.AnimalName;
animal.BirthYear = animalCreateDTO.BirthYear;
animal.AppUserId = User.UserGuidId();
_uow.Animals.Add(animal);
await _uow.SaveChangesAsync();
......
......@@ -7,11 +7,14 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using DAL.App.EF;
using Domain;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
namespace WebApp.ApiControllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class OwnerAnimalsController : ControllerBase
{
private readonly AppDbContext _context;
......
......@@ -29,6 +29,11 @@ export class App {
{ route: ['owners/create'], name: 'owners-create', moduleId: PLATFORM.moduleName('views/owners/create'), nav: false, title: 'Owners Create' },
{ route: ['animals', 'animals/index'], name: 'animals-index', moduleId: PLATFORM.moduleName('views/animals/index'), nav: true, title: 'Animals' },
{ route: ['animals/details/:id?'], name: 'animals-details', moduleId: PLATFORM.moduleName('views/animals/details'), nav: false, title: 'Animals Details' },
{ route: ['animals/edit/:id?'], name: 'animals-edit', moduleId: PLATFORM.moduleName('views/animals/edit'), nav: false, title: 'Animals Edit' },
{ route: ['animals/delete/:id?'], name: 'animals-delete', moduleId: PLATFORM.moduleName('views/animals/delete'), nav: false, title: 'Animals Delete' },
{ route: ['animals/create'], name: 'animals-create', moduleId: PLATFORM.moduleName('views/animals/create'), nav: false, title: 'Animals Create' },
{ route: ['owneranimals', 'owneranimals/index'], name: 'owneranimals-index', moduleId: PLATFORM.moduleName('views/owneranimals/index'), nav: true, title: 'OwnerAnimals' },
]
......
export interface IAnimal {
id: string;
animalName: string;
birthYear: number | null;
ownerCount: number;
}
export interface IAnimalCreate {
animalName: string;
birthYear: number | null;
}
export interface IAnimalEdit {
id: string;
animalName: string;
birthYear: number | null;
}
import { autoinject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-fetch-client';
import { AppState } from 'state/app-state';
import { IFetchResponse } from 'types/IFetchResponse';
import { IAnimal } from 'domain/IAnimal';
import { IAnimalCreate } from 'domain/IAnimalCreate';
import { IAnimalEdit } from 'domain/IAnimalEdit';
@autoinject
export class AnimalService {
constructor(private appState: AppState, private httpClient: HttpClient) {
this.httpClient.baseUrl = this.appState.baseUrl;
}
private readonly _baseUrl = 'animals';
async getAnimals(): Promise<IFetchResponse<IAnimal[]>> {
try {
const response = await this.httpClient
.fetch(this._baseUrl, {
cache: "no-store",
headers: {
authorization: "Bearer " + this.appState.jwt
}
});
// happy case
if (response.status >= 200 && response.status < 300) {
const data = (await response.json()) as IAnimal[];
console.log(data);
return {
statusCode: response.status,
data: data
}
}
// something went wrong
return {
statusCode: response.status,
errorMessage: response.statusText
}
} catch (reason) {
return {
statusCode: 0,
errorMessage: JSON.stringify(reason)
}
}
}
async getAnimal(id: string): Promise<IFetchResponse<IAnimal>> {
try {
const response = await this.httpClient
.fetch(this._baseUrl + '/' + id, {
cache: "no-store",
headers: {
authorization: "Bearer " + this.appState.jwt
}
});
if (response.status >= 200 && response.status < 300) {
const data = (await response.json()) as IAnimal;
return {
statusCode: response.status,
data: data
}
}
return {
statusCode: response.status,
errorMessage: response.statusText
}
} catch (reason) {
return {
statusCode: 0,
errorMessage: JSON.stringify(reason)
}
}
}
async createAnimal(animal: IAnimalCreate): Promise<IFetchResponse<string>> {
try {
const response = await this.httpClient
.post(this._baseUrl, JSON.stringify(animal), {
cache: 'no-store',
headers: {
authorization: "Bearer " + this.appState.jwt
}
})
if (response.status >= 200 && response.status < 300) {
console.log('response', response);
return {
statusCode: response.status
// no data
}
}
return {
statusCode: response.status,
errorMessage: response.statusText
}
}
catch (reason) {
return {
statusCode: 0,
errorMessage: JSON.stringify(reason)
}
}
}
async updateAnimal(animal: IAnimalEdit): Promise<IFetchResponse<string>> {
try {
const response = await this.httpClient
.put(this._baseUrl + '/' + animal.id, JSON.stringify(animal), {
cache: 'no-store',
headers: {
authorization: "Bearer " + this.appState.jwt
}
});
if (response.status >= 200 && response.status < 300) {
return {
statusCode: response.status
// no data
}
}
return {
statusCode: response.status,
errorMessage: response.statusText
}
}
catch (reason) {
return {
statusCode: 0,
errorMessage: JSON.stringify(reason)
}
}
}
async deleteAnimal(id: string): Promise<IFetchResponse<string>> {
try {
const response = await this.httpClient
.delete(this._baseUrl + '/' + id, null, {
cache: 'no-store',
headers: {
authorization: "Bearer " + this.appState.jwt
}
});
if (response.status >= 200 && response.status < 300) {
return {
statusCode: response.status
// no data
}
}
return {
statusCode: response.status,
errorMessage: response.statusText
}
}
catch (reason) {
return {
statusCode: 0,
errorMessage: JSON.stringify(reason)
}
}
}
}
<template>
<require from="../../components/alert"></require>
<alert alert-data.bind="_alert"></alert>
<h1>Create</h1>
<h4>Animal</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form submit.trigger="onSubmit($event)">
<div class="form-group">
<label class="control-label" for="AnimalName">AnimalName</label>
<input class="form-control" type="text" id="AnimalName" maxlength="64" value.bind="_animalName" />
</div>
<div class="form-group">
<label class="control-label" for="BirthYear">BirthYear</label>
<input class="form-control" type="text" id="BirthYear" maxlength="64" value.bind="_birthYear" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a route-href="route: animals-index; params.bind: { }">Back to List</a>
</div>
</template>
import { AnimalService } from 'service/animal-service';
import { autoinject } from 'aurelia-framework';
import { RouteConfig, NavigationInstruction, Router } from 'aurelia-router';
import { IAlertData } from 'types/IAlertData';
import { AlertType } from 'types/AlertType';
@autoinject
export class AnimalsCreate {
private _alert: IAlertData | null = null;
_animalName = "";
_birthYear = "";
constructor(private animalService: AnimalService, private router: Router) {
}
attached() {
}
activate(params: any, routeConfig: RouteConfig, navigationInstruction: NavigationInstruction) {
}
onSubmit(event: Event) {
console.log(event);
this.animalService
.createAnimal({ animalName: this._animalName, birthYear: this._birthYear.length == 0 ? null : Number(this._birthYear) })
.then(
response => {
if (response.statusCode >= 200 && response.statusCode < 300) {
this._alert = null;
this.router.navigateToRoute('animals-index', {});
} else {
// show error message
this._alert = {
message: response.statusCode.toString() + ' - ' + response.errorMessage,
type: AlertType.Danger,
dismissable: true,
}
}
}
);
event.preventDefault();
}
}
<template>
<require from="../../components/alert"></require>
<alert alert-data.bind="_alert"></alert>
<h1>Delete</h1>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Animal</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
AnimalName
</dt>
<dd class="col-sm-10">
${_animal.animalName}
</dd>
<dt class="col-sm-2">
BirthYear
</dt>
<dd class="col-sm-10">
${_animal.birthYear}
</dd>
<dt class="col-sm-2">
OwnerCount
</dt>
<dd class="col-sm-10">
${_animal.ownerCount}
</dd>
</dl>
<form submit.trigger="onSubmit($event)">
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a route-href="route: animals-index; params.bind: { }">Back to List</a>
</form>
</div>
</template>
import { AnimalService } from 'service/animal-service';
import { autoinject } from 'aurelia-framework';
import { RouteConfig, NavigationInstruction, Router } from 'aurelia-router';
import { IAlertData } from 'types/IAlertData';
import { AlertType } from 'types/AlertType';
import { IAnimal } from 'domain/IAnimal';
@autoinject
export class OwnersDelete {
private _alert: IAlertData | null = null;
private _animal?: IAnimal;
constructor(private animalService: AnimalService, private router: Router) {
}
attached() {
}
activate(params: any, routeConfig: RouteConfig, navigationInstruction: NavigationInstruction) {
console.log(params);
if (params.id && typeof (params.id) == 'string') {
this.animalService.getAnimal(params.id).then(
response => {
if (response.statusCode >= 200 && response.statusCode < 300) {
this._alert = null;
this._animal = response.data!;
} else {
// show error message
this._alert = {
message: response.statusCode.toString() + ' - ' + response.errorMessage,
type: AlertType.Danger,
dismissable: true,
};
this._animal = undefined;
}
}
);
}
}
onSubmit(event: Event) {
this.animalService
.deleteAnimal(this._animal!.id)
.then(
response => {
if (response.statusCode >= 200 && response.statusCode < 300) {
this._alert = null;
this.router.navigateToRoute('animals-index', {});
} else {
// show error message
this._alert = {
message: response.statusCode.toString() + ' - ' + response.errorMessage,
type: AlertType.Danger,
dismissable: true,
}
}
}
);
event.preventDefault();
}
}
<template>
<require from="../../components/alert"></require>
<alert alert-data.bind="_alert"></alert>
<h1>Details</h1>
<div>
<h4>Animal</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
AnimalName
</dt>
<dd class="col-sm-10">
${_animal.animalName}
</dd>
<dt class="col-sm-2">
BirthYear
</dt>
<dd class="col-sm-10">
${_animal.birthYear}
</dd>
<dt class="col-sm-2">
OwnerCount
</dt>
<dd class="col-sm-10">
${_animal.ownerCount}
</dd>
</dl>
</div>
<div>
<a route-href="route: animals-edit; params.bind: { id: _animal.id }">Edit</a> |
<a route-href="route: animals-index; params.bind: { }">Back to List</a>
</div>
</template>
import { AnimalService } from 'service/animal-service';
import { autoinject } from 'aurelia-framework';
import { RouteConfig, NavigationInstruction } from 'aurelia-router';
import { IAlertData } from 'types/IAlertData';
import { AlertType } from 'types/AlertType';
import { IAnimal } from 'domain/IAnimal';
@autoinject
export class OwnersDetails {
private _animal?: IAnimal;
private _alert: IAlertData | null = null;
constructor(private animalService: AnimalService) {
}
attached() {
}
activate(params: any, routeConfig: RouteConfig, navigationInstruction: NavigationInstruction) {
console.log(params);
if (params.id && typeof (params.id) == 'string') {
this.animalService.getAnimal(params.id).then(
response => {
if (response.statusCode >= 200 && response.statusCode < 300) {
this._alert = null;
this._animal = response.data!;
} else {
// show error message
this._alert = {
message: response.statusCode.toString() + ' - ' + response.errorMessage,
type: AlertType.Danger,
dismissable: true,
}
this._animal = undefined;
}
}
);
}
}
}
<template>
<require from="../../components/alert"></require>
<alert alert-data.bind="_alert"></alert>
<h1>Edit</h1>
<h4>Animal</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form submit.trigger="onSubmit($event)">
<div class="form-group">
<label class="control-label" for="AnimalName">AnimalName</label>
<input class="form-control" type="text" id="AnimalName" maxlength="64"
value.bind="_animal.animalName" />
</div>
<div class="form-group">
<label class="control-label" for="BirthYear">BirthYear</label>
<input class="form-control" type="text" id="BirthYear" maxlength="64" value.bind="_animal.birthYear" />
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a route-href="route: animals-index; params.bind: { }">Back to List</a>
</div>
</template>
import { AnimalService } from 'service/animal-service';
import { autoinject } from 'aurelia-framework';
import { RouteConfig, NavigationInstruction, Router } from 'aurelia-router';
import { IAlertData } from 'types/IAlertData';
import { AlertType } from 'types/AlertType';
import { IAnimalEdit } from 'domain/IAnimalEdit';
@autoinject
export class OwnersEdit {
private _alert: IAlertData | null = null;
private _animal?: IAnimalEdit;
constructor(private animalService: AnimalService, private router: Router) {
}
attached() {
}
activate(params: any, routeConfig: RouteConfig, navigationInstruction: NavigationInstruction) {
if (params.id && typeof (params.id) == 'string') {
this.animalService.getAnimal(params.id).then(
response => {
if (response.statusCode >= 200 && response.statusCode < 300) {
this._alert = null;
this._animal = response.data!;
} else {
// show error message
this._alert = {
message: response.statusCode.toString() + ' - ' + response.errorMessage,
type: AlertType.Danger,
dismissable: true,
}
}
}
);
}
}
onSubmit(event: Event) {
let inputValue = <string | null>this._animal!.birthYear;
// input is string
this._animal!.birthYear = inputValue == null || inputValue.length == 0 ? null : Number(inputValue);
this.animalService
.updateAnimal(this._animal!)
.then(
response => {
if (response.statusCode >= 200 && response.statusCode < 300) {
this._alert = null;
this.router.navigateToRoute('animals-index', {});
} else {
// show error message
this._alert = {
message: response.statusCode.toString() + ' - ' + response.errorMessage,
type: AlertType.Danger,
dismissable: true,
}
}
}
);
event.preventDefault();
}
}
<template>
Animals index
<require from="../../components/alert"></require>
<alert alert-data.bind="_alert"></alert>
<h1>Index</h1>
<p>
<a route-href="route: animals-create; params.bind: {}">Create New</a>
</p>
<table class="table">
<thead>
<tr>