diff --git a/db/mysql-with-inserts.sql b/db/mysql-with-inserts.sql index a77578cfea15728ddcb168525493d8146a7d28f1..015e860f4e2b8ea2385de782042faa0cb3e2d293 100644 --- a/db/mysql-with-inserts.sql +++ b/db/mysql-with-inserts.sql @@ -43,4 +43,4 @@ INSERT INTO `daaexample`.`pets` (`id`, `owner`, `name`,`weight`) VALUES (0,3,'Ya INSERT INTO `daaexample`.`users` (`login`,`password`,`role`) VALUES ('admin', '713bfda78870bf9d1b261f565286f85e97ee614efe5f0faf7c34e7ca4f65baca','ADMIN'); INSERT INTO `daaexample`.`users` (`login`,`password`,`role`) -VALUES ('normal', '7bf24d6ca2242430343ab7e3efb89559a47784eea1123be989c1b2fb2ef66e83','USER'); +VALUES ('normal', '7bf24d6ca2242430343ab7e3efb89559a47784eea1123be989c1b2fb2ef66e83','USER'); \ No newline at end of file diff --git a/nb-configuration.xml b/nb-configuration.xml new file mode 100644 index 0000000000000000000000000000000000000000..4da1f6c9b1e199a114dd332c473fd42da4ea89b4 --- /dev/null +++ b/nb-configuration.xml @@ -0,0 +1,18 @@ + + + + + + ide + + diff --git a/pom.xml b/pom.xml index 76c19487b6d890323fe4695454b3735d9a0fe910..4eba1682bb6ac47fbe51a5696dfdf5d5a1be45c7 100644 --- a/pom.xml +++ b/pom.xml @@ -182,6 +182,13 @@ equalsverifier ${equalsverifier.version} + + + jaxen + jaxen + 1.1.6 + + @@ -305,6 +312,7 @@ + diff --git a/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java b/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java index 2a67f22f93bf402148426fd8becb0ce7c8f37dfc..18f4b96d75183222a7fba00d61ecb7b0a8a8239b 100644 --- a/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java +++ b/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java @@ -11,6 +11,7 @@ import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; import es.uvigo.esei.daa.rest.PeopleResource; +import es.uvigo.esei.daa.rest.PetsResource; import es.uvigo.esei.daa.rest.UsersResource; /** @@ -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 index 619052b55f16d7739e731719e0eb9a0e0f4b4ac1..2e73805abc0fce55e2b655fc71602e2af2c72284 100644 --- a/src/main/java/es/uvigo/esei/daa/dao/PetsDAO.java +++ b/src/main/java/es/uvigo/esei/daa/dao/PetsDAO.java @@ -231,4 +231,3 @@ public class PetsDAO extends DAO{ } - diff --git a/src/main/java/es/uvigo/esei/daa/entities/Pet.java b/src/main/java/es/uvigo/esei/daa/entities/Pet.java index 9a6aa64b5f5d642c293b6e5d186373ca8f9223ef..5771ab069771cc8b10fd6f888ccb15131738994d 100644 --- a/src/main/java/es/uvigo/esei/daa/entities/Pet.java +++ b/src/main/java/es/uvigo/esei/daa/entities/Pet.java @@ -117,4 +117,3 @@ public class Pet { } - 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..a9d948cc5c36d63056994ef0a32bc7de7d916431 --- /dev/null +++ b/src/main/java/es/uvigo/esei/daa/rest/PetsResource.java @@ -0,0 +1,210 @@ +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.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; + +@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 a pet with the provided identifier. + * + * @param owner the identifier of the pet's owner to retrieve. + * @param id the identifier of the pet to retrieve. + * @return a 200 OK response with a pet that has the provided identifier. + * If the identifier does not corresponds with any pet, 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. + */ + @GET + @Path("/{id}") + public Response listWithOwner( + @PathParam("id") int id + ) { + try { + return Response.ok(this.dao.listWithOwner(id)).build(); + } catch (IllegalArgumentException iae) { + LOG.log(Level.FINE, "Invalid owner id in get method", iae); + + return Response.status(Response.Status.BAD_REQUEST) + .entity(iae.getMessage()) + .build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error listing pets with owner", e); + + return Response.serverError() + .entity(e.getMessage()) + .build(); + } + } + + /** + * 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() { + try { + return Response.ok(this.dao.list()).build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error listing pets", e); + return Response.serverError().entity(e.getMessage()).build(); + } + } + + /** + * Creates a new person in the system. + * + * @param name the name of the new person. + * @param surname the surname of the new person. + * @return a 200 OK response with a person 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("owner") int owner, + @FormParam("name") String name, + @FormParam("weight") Float weight + ) { + try { + final Pet newPet = this.dao.add(owner, name, weight); + + 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 person. + * + * @param id identifier of the person to modify. + * @param name the new name of the person. + * @param surname the new surname of the person. + * @return a 200 OK response with a person 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("owner") int owner, + @FormParam("name") String name, + @FormParam("weight") Float weight + ) { + try { + final Pet modifiedPet = new Pet(id, owner, name, weight); + this.dao.modify(modifiedPet); + + return Response.ok(modifiedPet).build(); + } catch (NullPointerException npe) { + final String message = String.format("Invalid data for pet (owner: %s, name: %s, weight: %s)", owner, name, weight); + + 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(); + } + } + + /** + * Deletes a person from the system. + * + * @param id the identifier of the person to be deleted. + * @return a 200 OK response with the identifier of the person 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(); + } + } + + + +} diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index f748efffdd5d7b5c12d930885f334ce2fb1b8f5b..9a5433952f4d992daf89b2f32c635c9bd6272ea9 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -59,6 +59,20 @@ ADMIN + + + + Admin Area + /rest/pets/* + GET + PUT + DELETE + POST + + + ADMIN + + diff --git a/src/main/webapp/js/dao/pets.js b/src/main/webapp/js/dao/pets.js new file mode 100644 index 0000000000000000000000000000000000000000..a1ef9c4949f0ade0726e1f13328d27deba616dac --- /dev/null +++ b/src/main/webapp/js/dao/pets.js @@ -0,0 +1,60 @@ +var PetsDAO = (function() { + var resourcePath = "rest/pets/"; + var requestByAjax = function(data, done, fail, always) { + done = typeof done !== 'undefined' ? done : function() {}; + fail = typeof fail !== 'undefined' ? fail : function() {}; + always = typeof always !== 'undefined' ? always : function() {}; + + let authToken = localStorage.getItem('authorization-token'); + if (authToken !== null) { + data.beforeSend = function(xhr) { + xhr.setRequestHeader('Authorization', 'Basic ' + authToken); + }; + } + + $.ajax(data).done(done).fail(fail).always(always); + }; + + function PetsDAO() { + this.listPets = function(done, fail, always) { + requestByAjax({ + url : resourcePath, + type : 'GET' + }, done, fail, always); + }; + + this.addPet = function(pet, done, fail, always) { + requestByAjax({ + url : resourcePath, + type : 'POST', + data : pet + }, done, fail, always); + }; + + this.modifyPet = function(pet, done, fail, always) { + requestByAjax({ + url : resourcePath + pet.id, + type : 'PUT', + data : pet + }, done, fail, always); + }; + + this.deletePet = function(id, done, fail, always) { + requestByAjax({ + url : resourcePath + id, + type : 'DELETE', + }, done, fail, always); + }; + + this.listPetsWithOwner = function(ownerId, done, fail, always) { + requestByAjax({ + url : resourcePath + ownerId, + type : 'GET', + }, done, fail, always); + }; + + + } + + return PetsDAO; +})(); \ No newline at end of file diff --git a/src/main/webapp/js/view/people.js b/src/main/webapp/js/view/people.js index 802d6b228a27a98d921ff39604f82e616f79a735..eedb8b221f0b768f89a9c4d4f57bdc1e66692d86 100644 --- a/src/main/webapp/js/view/people.js +++ b/src/main/webapp/js/view/people.js @@ -1,6 +1,6 @@ var PeopleView = (function() { - var dao; - + var dao; + var pets; // Referencia a this que permite acceder a las funciones públicas desde las funciones de jQuery. var self; @@ -9,14 +9,19 @@ var PeopleView = (function() { var formQuery = '#' + formId; var listQuery = '#' + listId; - function PeopleView(peopleDao, formContainerId, listContainerId) { + function PeopleView(peopleDao, petsView, formContainerId, listContainerId) { + dao = peopleDao; + pets = petsView; self = this; - insertPeopleForm($('#' + formContainerId)); - insertPeopleList($('#' + listContainerId)); + this.init = function() { + + insertPeopleForm($('#' + formContainerId)); + insertPeopleList($('#' + listContainerId)); + dao.listPeople(function(people) { $.each(people, function(key, person) { appendToTable(person); @@ -123,6 +128,12 @@ var PeopleView = (function() { $(formQuery + ' input[name="id"]').val(''); $('#btnSubmit').val('Crear'); }; + + this.detachForms = function(person) { + $(formQuery).remove(); + $(listQuery).remove(); + $('#titulo').text('Mascotas de ' + person.name + ' ' + person.surname); + }; }; var insertPeopleList = function(parent) { @@ -131,8 +142,8 @@ var PeopleView = (function() { \ \ Nombre\ - Apellido\ -  \ + Apellido\ +  \ \ \ \ @@ -149,10 +160,10 @@ var PeopleView = (function() {
\ \
\ -
\ +
\ \
\ -
\ +
\ \ \
\ @@ -160,14 +171,16 @@ var PeopleView = (function() { ' ); }; + var createPersonRow = function(person) { return '\ ' + person.name + '\ - ' + person.surname + '\ - \ + ' + person.surname + '\ + \ Editar\ Eliminar\ + Mascotas\ \ '; }; @@ -184,6 +197,11 @@ var PeopleView = (function() { $('#person-' + person.id + ' a.delete').click(function() { self.deletePerson(person.id); }); + + $('#person-' + person.id + ' a.pet').click(function() { + self.detachForms(person); + pets.init(self, person); + }); }; var appendToTable = function(person) { @@ -191,6 +209,7 @@ var PeopleView = (function() { .append(createPersonRow(person)); addRowListeners(person); }; + return PeopleView; })(); diff --git a/src/main/webapp/js/view/pets.js b/src/main/webapp/js/view/pets.js new file mode 100644 index 0000000000000000000000000000000000000000..4374c618ccbc81fa3f80ed64b3a0ac2239124089 --- /dev/null +++ b/src/main/webapp/js/view/pets.js @@ -0,0 +1,225 @@ +var PetsView = (function() { + var dao; + var owner; + var people; + // Referencia a this que permite acceder a las funciones públicas desde las funciones de jQuery. + var self; + + var formId = 'pets-form'; + var listId = 'pets-list'; + var formQuery = '#' + formId; + var listQuery = '#' + listId; + + function PetsView(petsDao, formContainerId, listContainerId) { + dao = petsDao; + self = this; + + this.init = function(peopleView, person) { + people = peopleView; + owner = person; + + insertPetForm($('#' + formContainerId)); + insertPetList($('#' + listContainerId)); + + dao.listPetsWithOwner(owner.id, function(pets) { + $.each(pets, function(key, pet) { + appendToTable(pet); + }); + }, + function() { + alert('No has sido posible acceder al listado de mascotas.'); + }); + + // La acción por defecto de enviar formulario (submit) se sobreescribe + // para que el envío sea a través de AJAX + $(formQuery).submit(function(event) { + var pet = self.getPetInForm(); + + if (self.isEditing()) { + dao.modifyPet(pet, + function(pet) { + $('#pet-' + pet.id + ' td.owner').text(pet.owner); + $('#pet-' + pet.id + ' td.name').text(pet.name); + $('#pet-' + pet.id + ' td.weight').text(pet.weight); + self.resetForm(); + }, + showErrorMessage, + self.enableForm + ); + } else { + dao.addPet(pet, + function(pet) { + + appendToTable(pet); + self.resetForm(); + }, + showErrorMessage, + self.enableForm + ); + } + + return false; + }); + + $('#btnClear').click(this.resetForm); + $('#btnReturn').click(returnToPeople); + + + + }; + + this.getPetInForm = function() { + var form = $(formQuery); + return { + 'id': form.find('input[name="id"]').val(), + 'owner': owner.id, + 'name': form.find('input[name="name"]').val(), + 'weight': form.find('input[name="weight"]').val() + }; + }; + + this.getPetInRow = function(id) { + var row = $('#pet-' + id); + + if (row !== undefined) { + return { + 'id': id, + 'owner': owner.id, + 'name': row.find('td.name').text(), + 'surname': row.find('td.weight').text() + }; + } else { + return undefined; + } + }; + + this.editPet = function(id,owner) { + var row = $('#pet-' + id); + + if (row !== undefined) { + var form = $(formQuery); + + form.find('input[name="id"]').val(id); + form.find('input[name="owner"]').val(owner); + form.find('input[name="name"]').val(row.find('td.name').text()); + form.find('input[name="weight"]').val(row.find('td.weight').text()); + + $('input#btnSubmit').val('Modificar'); + } + }; + + this.deletePet = function(id) { + if (confirm('Está a punto de eliminar a una mascota. ¿Está seguro de que desea continuar?')) { + dao.deletePet(id, + function() { + $('tr#pet-' + id).remove(); + }, + showErrorMessage + ); + } + }; + + this.isEditing = function() { + return $(formQuery + ' input[name="id"]').val() != ""; + }; + + this.disableForm = function() { + $(formQuery + ' input').prop('disabled', true); + }; + + this.enableForm = function() { + $(formQuery + ' input').prop('disabled', false); + }; + + this.resetForm = function() { + $(formQuery)[0].reset(); + $(formQuery + ' input[name="id"]').val(''); + $('#btnSubmit').val('Crear'); + }; + + this.detachForms = function() { + $(formQuery).detach(); + $(listQuery).detach(); + $('#titulo').text('Personas'); + }; + + }; + + + var insertPetList = function(parent) { + parent.append( + '\ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
Nombre MascotaPeso 
' + ); + }; + + var insertPetForm = function(parent) { + parent.append( + '
\ + \ + \ +
\ +
\ + \ +
\ +
\ + \ +
\ +
\ + \ + \ + \ +
\ +
\ +
' + ); + }; + + var createPetRow = function(pet) { + return '\ + ' + pet.name + '\ + ' + pet.weight + '\ + \ + Editar\ + Eliminar\ + \ + '; + }; + + var showErrorMessage = function(jqxhr, textStatus, error) { + alert(textStatus + ": " + error); + }; + + var addRowListeners = function(pet) { + $('#pet-' + pet.id + ' a.edit').click(function() { + self.editPet(pet.id, pet.owner); + }); + + $('#pet-' + pet.id + ' a.delete').click(function() { + self.deletePet(pet.id); + }); + + }; + + var appendToTable = function(pet) { + $(listQuery + ' > tbody:last') + .append(createPetRow(pet)); + addRowListeners(pet); + }; + + var returnToPeople = function() { + self.detachForms(); + people.init(); + }; + + return PetsView; +})(); diff --git a/src/main/webapp/main.html b/src/main/webapp/main.html index c2e28f18e86ede920b9b3bdb675f343473452886..1618e5b46f430c6a6a58a976ad1ab98969d41caf 100644 --- a/src/main/webapp/main.html +++ b/src/main/webapp/main.html @@ -1,4 +1,3 @@ - @@ -22,7 +21,7 @@
-

Personas

+

Personas

@@ -30,6 +29,8 @@ src="http://code.jquery.com/jquery-2.2.4.min.js"> + +