diff --git a/src/main/angular/src/app/app-routing.module.ts b/src/main/angular/src/app/app-routing.module.ts index 2a71deca534b4cf431058d778ba3f8f2a85ef33d..35154236bd97dd8f759c0b5f579758cdb971fd2c 100644 --- a/src/main/angular/src/app/app-routing.module.ts +++ b/src/main/angular/src/app/app-routing.module.ts @@ -23,6 +23,8 @@ import {UnauthenticatedGuard} from './guards/unauthenticated.guard'; import {AuthenticatedGuard} from './guards/authenticated.guard'; import {LoginPanelComponent} from './components/login-panel/login-panel.component'; import {MainPanelComponent} from './components/main-panel/main-panel.component'; +import {PetsFormComponent} from './modules/pets/components/pets-form/pets-form.component'; + const routes: Routes = [ { @@ -44,9 +46,29 @@ const routes: Routes = [ { path: 'people', loadChildren: () => import('./modules/people/people.module').then(m => m.PeopleModule) + }, + { + path: 'listPets/:person', + redirectTo: ':person', + pathMatch: 'full' + }, + { + path: ':person', + loadChildren: () => import('./modules/pets/pets.module').then(m => m.PetsModule) } ] - } + }, + + + + + + /*{ + path: 'pets/:person', + pathMatch: 'full', + component: PetsFormComponent, + canActivate: [AuthenticatedGuard] + }*/ ]; @NgModule({ diff --git a/src/main/angular/src/app/app.module.ts b/src/main/angular/src/app/app.module.ts index aa0b268b3a818b21278022450d8eefe49f07343e..90dfecca79bbc09119943741cd1332661e916a15 100644 --- a/src/main/angular/src/app/app.module.ts +++ b/src/main/angular/src/app/app.module.ts @@ -33,7 +33,7 @@ import {AuthenticationInterceptor} from './interceptors/authentication.intercept declarations: [ AppComponent, LoginPanelComponent, - MainPanelComponent + MainPanelComponent, ], imports: [ AppRoutingModule, diff --git a/src/main/angular/src/app/modules/people/components/people-list/people-list.component.html b/src/main/angular/src/app/modules/people/components/people-list/people-list.component.html index 2f37f4e3dbefeac56e7eb7be9ed52954e91cfbe2..56e08d267c90d9940b9da7e37628abfad1b85dc2 100644 --- a/src/main/angular/src/app/modules/people/components/people-list/people-list.component.html +++ b/src/main/angular/src/app/modules/people/components/people-list/people-list.component.html @@ -32,6 +32,7 @@ + diff --git a/src/main/angular/src/app/modules/people/components/people-list/people-list.component.ts b/src/main/angular/src/app/modules/people/components/people-list/people-list.component.ts index d63efccab96aac5b3c1c36132ddc30813be4cdf0..1627e66046849b5830ddbd09f39630900734233f 100644 --- a/src/main/angular/src/app/modules/people/components/people-list/people-list.component.ts +++ b/src/main/angular/src/app/modules/people/components/people-list/people-list.component.ts @@ -20,6 +20,7 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {PersonModel} from '../../models/person.model'; import {PeopleService} from '../../services/people.service'; +import {Router} from '@angular/router'; @Component({ selector: 'app-people-list', @@ -36,7 +37,7 @@ export class PeopleListComponent { @Output() public readonly delete: EventEmitter; - public constructor() { + public constructor(private readonly router: Router) { this.edit = new EventEmitter(); this.delete = new EventEmitter(); } @@ -48,4 +49,8 @@ export class PeopleListComponent { public onDelete(person: PersonModel) { this.delete.emit(person); } + + public pets(person: PersonModel){ + this.router.navigate(['//listPets',person.id]); + } } diff --git a/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.html b/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.html new file mode 100644 index 0000000000000000000000000000000000000000..c846cb2fb4bf4c67c3647d73d5ef269f6ae78eeb --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.html @@ -0,0 +1,22 @@ + +
+ + +
+
+ +
+ +
+ +
+ +
+ + +
+
+
+ diff --git a/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.scss b/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.spec.ts b/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..04c08170b67de8d62c035c6b1cf71647f2a08972 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PetsFormComponent } from './pets-form.component'; + +describe('PetsFormComponent', () => { + let component: PetsFormComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PetsFormComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PetsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.ts b/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..31f74d1524f097e821a9e319ffd3e3ce785af738 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-form/pets-form.component.ts @@ -0,0 +1,55 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import { PetModel } from '../../models/pet.model'; + +@Component({ + selector: 'app-pets-form', + templateUrl: './pets-form.component.html', + styleUrls: ['./pets-form.component.scss'] +}) +export class PetsFormComponent { + public activePet: PetModel; + @Input() + public owner: number; + + @Output() + public readonly clean: EventEmitter; + + @Output() + public readonly modify: EventEmitter; + + public name: string; + public kind: string; + + constructor( + private route: ActivatedRoute + ) { + this.clean = new EventEmitter(); + this.modify = new EventEmitter(); + } + + @Input() + public set pet(pet: PetModel) { + this.activePet = pet; + this.name = pet.name; + this.kind = pet.kind; + } + + public get pet(): PetModel { + return this.activePet; + } + + public onClean() { + this.clean.emit(); + } + + public onModify() { + this.modify.emit({ + id: this.pet.id, + name: this.name, + kind: this.kind, + owner: this.owner + }); + } + +} diff --git a/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.html b/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2159c31dadcceb772fc519296cbce2e2565fd940 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + +
NombreTipo 
{{pet.name}}{{pet.kind}} + + +
+ + diff --git a/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.scss b/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.spec.ts b/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0afdebefd5d38c373e23bab2f691882b351a50c9 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PetsListComponent } from './pets-list.component'; + +describe('PetsListComponent', () => { + let component: PetsListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PetsListComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PetsListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.ts b/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ef8d0642c2aca3645a15dec9b268c91f4fde91c4 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-list/pets-list.component.ts @@ -0,0 +1,34 @@ +import { Component, EventEmitter, OnInit, Input, Output } from '@angular/core'; +import { PetModel} from '../../models/pet.model'; +@Component({ + selector: 'app-pets-list', + templateUrl: './pets-list.component.html', + styleUrls: ['./pets-list.component.scss'] +}) +export class PetsListComponent { + + @Input() + public pets: PetModel[] = []; + + @Output() + public readonly delete: EventEmitter; + + @Output() + public readonly edit: EventEmitter; + + constructor() { + this.delete = new EventEmitter(); + this.edit = new EventEmitter(); + } + + public onDelete(pet: PetModel) { + this.delete.emit(pet); + } + + public onEdit(pet: PetModel) { + this.edit.emit(pet); + } + + + +} diff --git a/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.html b/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.html new file mode 100644 index 0000000000000000000000000000000000000000..1896fc4b6fde20a7b8a267172364206a8edab2a2 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.html @@ -0,0 +1,11 @@ + +

Mascotas

+ + + + + \ No newline at end of file diff --git a/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.scss b/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.spec.ts b/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e78e9a9c5741ed3a31c2cf06f1ac8580613b05a7 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PetsMainComponent } from './pets-main.component'; + +describe('PetsMainComponent', () => { + let component: PetsMainComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PetsMainComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PetsMainComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.ts b/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..2fc1acadb24aa005231843da241911f36df0cb26 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/components/pets-main/pets-main.component.ts @@ -0,0 +1,84 @@ +import { Component, OnInit } from '@angular/core'; +import {PetModel} from '../../models/pet.model'; +import {PetsService} from '../../services/pets.service'; +import {map, mergeMap} from 'rxjs/operators'; +import {ActivatedRoute, Router} from '@angular/router'; + + +@Component({ + selector: 'app-pets-main', + templateUrl: './pets-main.component.html', + styleUrls: ['./pets-main.component.scss'] +}) +export class PetsMainComponent implements OnInit { + public id : number + public activePet: PetModel; + public pets: PetModel[]; + + constructor( + private readonly petsService: PetsService, + private route: ActivatedRoute, + private readonly router: Router + ) { + this.pets = []; + this.clearActivePet() + } + + ngOnInit() { + this.id = this.route.snapshot.params.person; + this.petsService.list(this.id) + .subscribe(pets => this.pets = pets); + + } + + public onCleanForm(): void { + this.clearActivePet(); + } + + public clearActivePet():void{ + this.activePet = { id: undefined, name: '', kind: '', owner: undefined }; + } + + public back(){ + this.router.navigate(['/']); + } + + public onDelete(pet: PetModel): void { + if (confirm(`¿Estás seguro de que deseas eliminar a ${pet.name}?`)) { + this.petsService.delete(pet) + .pipe( + mergeMap(() => this.petsService.list(pet.owner)) + ) + .subscribe(pets => this.pets = pets); + } + } + + public onEdit(pet: PetModel): void { + this.activePet = pet; + console.log("Noe") + console.log(this.activePet) + } + + public onModifyForm(pet: PetModel): void { + if (pet.id === undefined) { + this.petsService.create(pet) + .pipe( + mergeMap(() => this.petsService.list(pet.owner)) + ) + .subscribe(pets => { + this.pets = pets; + this.clearActivePet(); + }); + } else { + this.petsService.modify(pet) + .pipe( + mergeMap(() => this.petsService.list(pet.owner)) + ) + .subscribe(pets => { + this.pets = pets; + this.clearActivePet(); + }); + } + } + +} diff --git a/src/main/angular/src/app/modules/pets/models/pet.model.ts b/src/main/angular/src/app/modules/pets/models/pet.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..58a954b4e45202521b706e1ee6cc1b2da36c3524 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/models/pet.model.ts @@ -0,0 +1,7 @@ +export class PetModel { + id?: number; + name: string; + kind: string; + owner:number; + } + \ No newline at end of file diff --git a/src/main/angular/src/app/modules/pets/pets-routing.module.ts b/src/main/angular/src/app/modules/pets/pets-routing.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..603d72ab783ad5b8116e610a9c3d1f5bfc9f81a1 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/pets-routing.module.ts @@ -0,0 +1,16 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {PetsMainComponent} from './components/pets-main/pets-main.component'; + +const routes: Routes = [ + { + path: '', + component: PetsMainComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class PetsRoutingModule { } diff --git a/src/main/angular/src/app/modules/pets/pets.module.ts b/src/main/angular/src/app/modules/pets/pets.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..e98bd8dbb84834dd2da40ed16efe54a273b776cc --- /dev/null +++ b/src/main/angular/src/app/modules/pets/pets.module.ts @@ -0,0 +1,22 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; + +import {PetsRoutingModule} from './pets-routing.module'; +import {PetsListComponent} from './components/pets-list/pets-list.component'; +import {PetsFormComponent} from './components/pets-form/pets-form.component'; +import {PetsMainComponent} from './components/pets-main/pets-main.component'; +import {FormsModule} from '@angular/forms'; + +@NgModule({ + declarations: [ + PetsFormComponent, + PetsListComponent, + PetsMainComponent + ], + imports: [ + CommonModule, + FormsModule, + PetsRoutingModule + ] +}) +export class PetsModule { } diff --git a/src/main/angular/src/app/modules/pets/services/pets.service.spec.ts b/src/main/angular/src/app/modules/pets/services/pets.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0e426fcd3a47160f1ee796441f605f13c39c6c8 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/services/pets.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { PetsService } from './pets.service'; + +describe('PetsService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: PetsService = TestBed.get(PetsService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/main/angular/src/app/modules/pets/services/pets.service.ts b/src/main/angular/src/app/modules/pets/services/pets.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..abc4dd23686e269a169c86194c9fa1e8d200aed3 --- /dev/null +++ b/src/main/angular/src/app/modules/pets/services/pets.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; +import {environment} from '../../../../environments/environment'; +import {Observable} from 'rxjs'; +import {PetModel} from '../models/pet.model'; + +@Injectable({ + providedIn: 'root' +}) +export class PetsService { + + constructor(private readonly http: HttpClient) { } + + + + public list(id: number): Observable{ + return this.http.get(`${environment.restApi}/pets?owner=${id}`); + } + + public delete(pet: PetModel): Observable { + return this.http.delete(`${environment.restApi}/pets/${pet.id}`); + } + + public modify(pet: PetModel): Observable { + const data = new HttpParams() + .set('name', pet.name) + .set('kind', pet.kind) + .set('owner', pet.owner.toString()); + + return this.http.put(`${environment.restApi}/pets/${pet.id}`, data); + } + + public create(pet: PetModel): Observable { + const data = new HttpParams() + .set('name', pet.name) + .set('kind', pet.kind) + .set('owner', pet.owner.toString()); + + return this.http.post(`${environment.restApi}/pets`, data); + } +} diff --git a/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java b/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java index 2a67f22f93bf402148426fd8becb0ce7c8f37dfc..2f3a5c0439740d2d340628ab5063fc3b7224fce2 100644 --- a/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java +++ b/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java @@ -12,6 +12,7 @@ import javax.ws.rs.core.Application; import es.uvigo.esei.daa.rest.PeopleResource; import es.uvigo.esei.daa.rest.UsersResource; +import es.uvigo.esei.daa.rest.PetsResource; /** * Configuration of the REST application. This class includes the resources and @@ -26,7 +27,8 @@ public class DAAExampleApplication extends Application { public Set> getClasses() { return Stream.of( PeopleResource.class, - UsersResource.class + UsersResource.class, + PetsResource.class ).collect(toSet()); } diff --git a/src/main/java/es/uvigo/esei/daa/dao/PetsDAO.java b/src/main/java/es/uvigo/esei/daa/dao/PetsDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..95ac5233f69e4021043192d80c8e47920132b02d --- /dev/null +++ b/src/main/java/es/uvigo/esei/daa/dao/PetsDAO.java @@ -0,0 +1,172 @@ +package es.uvigo.esei.daa.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.sound.sampled.SourceDataLine; + +import es.uvigo.esei.daa.entities.Pet; + +/** + * DAO class for the {@link Pets} entities. + * + * @author Noelia García Hervella + * + */ +public class PetsDAO extends DAO { + private final static Logger LOG = Logger.getLogger(PetsDAO.class.getName()); + + + /** + * Returns a list with all the pets persisted in the system. + * + * @return a list with all the pets persisted in the system. + * @throws DAOException if an error happens while retrieving the pets. + */ + public List list(int id) throws DAOException { + try (final Connection conn = this.getConnection()) { + final String query = "SELECT * FROM pets where owner=?"; + + try (final PreparedStatement statement = conn.prepareStatement(query)) { + statement.setInt(1, id); + try (final ResultSet result = statement.executeQuery()) { + final List pets = new LinkedList<>(); + + while (result.next()) { + pets.add(rowToEntity(result)); + + } + return pets; + } + } + } catch (SQLException e) { + LOG.log(Level.SEVERE, "Error listing pets", e); + throw new DAOException(e); + } + } + + /** + * Removes a persisted pet from the system. + * + * @param id identifier of the pet to be deleted. + * @throws DAOException if an error happens while deleting the pet. + * @throws IllegalArgumentException if the provided id does not corresponds + * with any persisted pet. + */ + public void delete(int id) + throws DAOException, IllegalArgumentException { + try (final Connection conn = this.getConnection()) { + final String query = "DELETE FROM pets WHERE id=?"; + + try (final PreparedStatement statement = conn.prepareStatement(query)) { + statement.setInt(1, id); + + if (statement.executeUpdate() != 1) { + throw new IllegalArgumentException("Invalid id"); + } + } + } catch (SQLException e) { + LOG.log(Level.SEVERE, "Error deleting a pet", e); + throw new DAOException(e); + } + } + + /** + * Persists a new pet in the system. An identifier will be assigned + * automatically to the new pet. + * + * @param name name of the new pet. Can't be {@code null}. + * @param kind surname of the new pet. Can't be {@code null}. + * @param owner id of the new owner. Can't be {@code null}. + * @return a {@link Pet} entity representing the persisted pet. + * @throws DAOException if an error happens while persisting the new pet. + * @throws IllegalArgumentException if the name or surname are {@code null}. + */ + public Pet add(String name, String kind, String owner) + throws DAOException, IllegalArgumentException { + if (name == null || kind == null || owner == null ) { + throw new IllegalArgumentException("name, kind and owner can't be null"); + } + + try (Connection conn = this.getConnection()) { + final String query = "INSERT INTO pets VALUES(null, ?, ?, ?)"; + + try (PreparedStatement statement = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) { + statement.setString(1, name); + statement.setString(2, kind); + statement.setString(3, owner); + + if (statement.executeUpdate() == 1) { + try (ResultSet resultKeys = statement.getGeneratedKeys()) { + if (resultKeys.next()) { + return new Pet(resultKeys.getInt(1), name, kind, Integer.parseInt(owner)); + } else { + LOG.log(Level.SEVERE, "Error retrieving inserted id"); + throw new SQLException("Error retrieving inserted id"); + } + } + } else { + LOG.log(Level.SEVERE, "Error inserting value"); + throw new SQLException("Error inserting value"); + } + } + } catch (SQLException e) { + LOG.log(Level.SEVERE, "Error adding a pet", e); + throw new DAOException(e); + } + } + + /** + * Modifies a pet previously persisted in the system. The pet will be + * retrieved by the provided id and its current name and surname will be + * replaced with the provided. + * + * @param pet a {@link pet} entity with the new data. + * @throws DAOException if an error happens while modifying the new pet. + * @throws IllegalArgumentException if the pet is {@code null}. + */ + public void modify(Pet pet) + throws DAOException, IllegalArgumentException { + if (pet == null) { + throw new IllegalArgumentException("pet can't be null"); + } + + try (Connection conn = this.getConnection()) { + final String query = "UPDATE pets SET name=?, kind=? WHERE id=?"; + + try (PreparedStatement statement = conn.prepareStatement(query)) { + statement.setString(1, pet.getName()); + statement.setString(2, pet.getKind()); + statement.setInt(3, pet.getId()); + + if (statement.executeUpdate() != 1) { + throw new IllegalArgumentException("name and surname can't be null"); + } + } + } catch (SQLException e) { + LOG.log(Level.SEVERE, "Error modifying a pet", e); + throw new DAOException(); + } + } + + + + + + private Pet rowToEntity(ResultSet row) throws SQLException { + return new Pet( + row.getInt("id"), + row.getString("name"), + row.getString("kind"), + row.getInt("owner") + ); + + } +} diff --git a/src/main/java/es/uvigo/esei/daa/entities/Pet.java b/src/main/java/es/uvigo/esei/daa/entities/Pet.java new file mode 100644 index 0000000000000000000000000000000000000000..bc282ba5f5dfe3b1198ebc28099c5e0497269ef4 --- /dev/null +++ b/src/main/java/es/uvigo/esei/daa/entities/Pet.java @@ -0,0 +1,121 @@ +package es.uvigo.esei.daa.entities; + +import static java.util.Objects.requireNonNull; + +/** + * An entity that represents a pet. + * + * @author Noelia García Hervella + */ +public class Pet { + private int id; + private String name; + private String kind; + private int owner; + + // Constructor needed for the JSON conversion + Pet() {} + + /** + * Constructs a new instance of {@link Pet}. + * + * @param id identifier of the pet. + * @param name name of the pet. + * @param surname surname of the pet. + */ + public Pet(int id, String name, String kind, int owner) { + this.id = id; + this.setName(name); + this.setKind(kind); + this.setOwner(owner); + } + + /** + * Returns the identifier of the pet. + * + * @return the identifier of the pet. + */ + public int getId() { + return id; + } + + /** + * Returns the name of the pet. + * + * @return the name of the pet. + */ + public String getName() { + return name; + } + + /** + * Set the name of this pet. + * + * @param name the new name of the pet. + * @throws NullPointerException if the {@code name} is {@code null}. + */ + public void setName(String name) { + this.name = requireNonNull(name, "Name can't be null"); + } + + /** + * Returns the surname of the pet. + * + * @return the surname of the pet. + */ + public String getKind() { + return kind; + } + + /** + * Set the surname of this pet. + * + * @param surname the new surname of the pet. + * @throws NullPointerException if the {@code surname} is {@code null}. + */ + public void setKind(String surname) { + this.kind = requireNonNull(surname, "Kind can't be null"); + } + + + /** + * Returns the id of the owner. + * + * @return the id of the owner. + */ + public int getOwner() { + return owner; + } + + /** + * Set the id of this owner. + * + * @param owner the new id of the owner. + * @throws NullPointerException if the {@code surname} is {@code null}. + */ + public void setOwner(int owner) { + this.owner = requireNonNull(owner, "The owner' id can't be null"); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Pet)) + return false; + Pet other = (Pet) obj; + if (id != other.id) + return false; + return true; + } +} diff --git a/src/main/java/es/uvigo/esei/daa/rest/PetsResource.java b/src/main/java/es/uvigo/esei/daa/rest/PetsResource.java new file mode 100644 index 0000000000000000000000000000000000000000..b05221f7bf4154165fa82b41977f8ece8f4bf8c6 --- /dev/null +++ b/src/main/java/es/uvigo/esei/daa/rest/PetsResource.java @@ -0,0 +1,189 @@ +package es.uvigo.esei.daa.rest; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import es.uvigo.esei.daa.dao.DAOException; +import es.uvigo.esei.daa.dao.PetsDAO; +import es.uvigo.esei.daa.entities.Pet; + +/** + * REST resource for managing pets. + * + * @author Noelia García Hervella + */ +@Path("/pets") +@Produces(MediaType.APPLICATION_JSON) +public class PetsResource { + private final static Logger LOG = Logger.getLogger(PetsResource.class.getName()); + + private final PetsDAO dao; + + /** + * Constructs a new instance of {@link PetsResource}. + */ + public PetsResource() { + this(new PetsDAO()); + } + + // Needed for testing purposes + PetsResource(PetsDAO dao) { + this.dao = dao; + } + + /** + * Returns the complete list of pets stored in the system. + * + * @return a 200 OK response with the complete list of pets stored in the + * system. If an error happens while retrieving the list, a 500 Internal + * Server Error response with an error message will be returned. + */ + @GET + public Response list( + @QueryParam("owner") int id + ) { + + try { + return Response.ok(this.dao.list(id)).build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error listing pets", e); + return Response.serverError().entity(e.getMessage()).build(); + } + } + + /** + * Deletes a pet from the system. + * + * @param id the identifier of the pet to be deleted. + * @return a 200 OK response with the identifier of the pet that has + * been deleted. If the identifier does not corresponds with any user, a 400 + * Bad Request response with an error message will be returned. If an error + * happens while retrieving the list, a 500 Internal Server Error response + * with an error message will be returned. + */ + @DELETE + @Path("/{id}") + public Response delete( + @PathParam("id") int id + ) { + try { + this.dao.delete(id); + + return Response.ok(id).build(); + } catch (IllegalArgumentException iae) { + LOG.log(Level.FINE, "Invalid pet id in delete method", iae); + + return Response.status(Response.Status.BAD_REQUEST) + .entity(iae.getMessage()) + .build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error deleting a pet", e); + + return Response.serverError() + .entity(e.getMessage()) + .build(); + } + } + + /** + * Creates a new pet in the system. + * + * @param name the name of the new pet. + * @param surname the surname of the new pet. + * @return a 200 OK response with a pet that has been created. If the + * name or the surname are not provided, a 400 Bad Request response with an + * error message will be returned. If an error happens while retrieving the + * list, a 500 Internal Server Error response with an error message will be + * returned. + */ + + @POST + public Response add( + @FormParam("name") String name, + @FormParam("kind") String kind, + @FormParam("owner") String owner + ) { + try { + final Pet newPet = this.dao.add(name, kind, owner); + + return Response.ok(newPet).build(); + } catch (IllegalArgumentException iae) { + LOG.log(Level.FINE, "Invalid pet id in add method", iae); + + return Response.status(Response.Status.BAD_REQUEST) + .entity(iae.getMessage()) + .build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error adding a pet", e); + + return Response.serverError() + .entity(e.getMessage()) + .build(); + } + } + + /** + * Modifies the data of a pet. + * + * @param id identifier of the pet to modify. + * @param name the new name of the pet. + * @param surname the new surname of the pet. + * @param owner identifier of the owner. + * @return a 200 OK response with a pet that has been modified. If the + * identifier does not corresponds with any user or the name or surname are + * not provided, a 400 Bad Request response with an error message will be + * returned. If an error happens while retrieving the list, a 500 Internal + * Server Error response with an error message will be returned. + * + */ + @PUT + @Path("/{id}") + public Response modify( + @PathParam("id") int id, + @FormParam("name") String name, + @FormParam("kind") String kind, + @FormParam("owner") String owner + ) { + try { + final Pet modifiedPet = new Pet(id, name, kind, Integer.parseInt(owner)); + this.dao.modify(modifiedPet); + + return Response.ok(modifiedPet).build(); + } catch (NullPointerException npe) { + final String message = String.format("Invalid data for pet (name: %s, KIND: %s)", name, kind); + + LOG.log(Level.FINE, message); + + return Response.status(Response.Status.BAD_REQUEST) + .entity(message) + .build(); + } catch (IllegalArgumentException iae) { + LOG.log(Level.FINE, "Invalid pet id in modify method", iae); + + return Response.status(Response.Status.BAD_REQUEST) + .entity(iae.getMessage()) + .build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error modifying a pet", e); + + return Response.serverError() + .entity(e.getMessage()) + .build(); + } + } + + + +} diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index b2b892cf60e8d4db82b89eb130fcf0b309df34eb..fd750c14c0ef57b1edb326429a7a850a27f71e76 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -1,5 +1,8 @@ es\uvigo\esei\daa\rest\PeopleResource.class es\uvigo\esei\daa\dao\PeopleDAO.class +es\uvigo\esei\daa\rest\PetsResource.class +es\uvigo\esei\daa\dao\PetsDAO.class +es\uvigo\esei\daa\entities\Pet.class es\uvigo\esei\daa\entities\User.class es\uvigo\esei\daa\dao\UsersDAO.class es\uvigo\esei\daa\dao\DAOException.class