From 21785083d24ea3fe53b31cc8c6ccda44345e253c Mon Sep 17 00:00:00 2001 From: alexgomezg Date: Sat, 27 Mar 2021 18:57:32 +0100 Subject: [PATCH] =?UTF-8?q?Se=20a=C3=B1ade=20Pets=20a=20la=20aplicaci?= =?UTF-8?q?=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se añade todo lo necesario para la gestion de Mascotas en la aplicación --- .../uvigo/esei/daa/DAAExampleApplication.java | 2 + .../java/es/uvigo/esei/daa/dao/PetDAO.java | 171 +++++++++ .../java/es/uvigo/esei/daa/entities/Pet.java | 80 +++++ .../es/uvigo/esei/daa/rest/PetResource.java | 174 ++++++++++ src/main/webapp/js/dao/pets.js | 60 ++++ src/main/webapp/js/view/people.js | 10 + src/main/webapp/js/view/pets.js | 261 ++++++++++++++ src/main/webapp/main.html | 27 +- .../es/uvigo/esei/daa/dataset/PetDataset.java | 71 ++++ .../uvigo/esei/daa/entities/PetUnitTest.java | 118 +++++++ .../esei/daa/filters/AuthorizationFilter.java | 2 +- .../uvigo/esei/daa/matchers/IsEqualToPet.java | 57 +++ .../uvigo/esei/daa/rest/PetResourceTest.java | 325 ++++++++++++++++++ .../esei/daa/suites/IntegrationTestSuite.java | 2 + .../uvigo/esei/daa/suites/UnitTestSuite.java | 5 +- src/test/resources/datasets/dataset-add.xml | 6 + .../resources/datasets/dataset-delete.xml | 4 + .../resources/datasets/dataset-modify.xml | 5 + src/test/resources/datasets/dataset.dtd | 9 +- src/test/resources/datasets/dataset.xml | 5 + src/test/resources/db/hsqldb-drop.sql | 1 + src/test/resources/db/hsqldb.sql | 8 + 22 files changed, 1394 insertions(+), 9 deletions(-) create mode 100644 src/main/java/es/uvigo/esei/daa/dao/PetDAO.java create mode 100644 src/main/java/es/uvigo/esei/daa/entities/Pet.java create mode 100644 src/main/java/es/uvigo/esei/daa/rest/PetResource.java create mode 100644 src/main/webapp/js/dao/pets.js create mode 100644 src/main/webapp/js/view/pets.js create mode 100644 src/test/java/es/uvigo/esei/daa/dataset/PetDataset.java create mode 100644 src/test/java/es/uvigo/esei/daa/entities/PetUnitTest.java create mode 100644 src/test/java/es/uvigo/esei/daa/matchers/IsEqualToPet.java create mode 100644 src/test/java/es/uvigo/esei/daa/rest/PetResourceTest.java diff --git a/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java b/src/main/java/es/uvigo/esei/daa/DAAExampleApplication.java index 2a67f22..2dfd859 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.PetResource; /** * Configuration of the REST application. This class includes the resources and @@ -26,6 +27,7 @@ public class DAAExampleApplication extends Application { public Set> getClasses() { return Stream.of( PeopleResource.class, + PetResource.class, UsersResource.class ).collect(toSet()); } diff --git a/src/main/java/es/uvigo/esei/daa/dao/PetDAO.java b/src/main/java/es/uvigo/esei/daa/dao/PetDAO.java new file mode 100644 index 0000000..0bc99e2 --- /dev/null +++ b/src/main/java/es/uvigo/esei/daa/dao/PetDAO.java @@ -0,0 +1,171 @@ +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 es.uvigo.esei.daa.entities.Pet; + +/** + * DAO class for the {@link Pet} entities. + * + * @author Alex + * + */ +public class PetDAO extends DAO { + + private final static Logger LOG = Logger.getLogger(PetDAO.class.getName()); + + public Pet get(int id) throws DAOException, IllegalArgumentException { + try (final Connection conn = this.getConnection()) { + final String query = "SELECT * FROM pets WHERE id=?"; + + try (final PreparedStatement statement = conn.prepareStatement(query)) { + statement.setInt(1, id); + + try (final ResultSet result = statement.executeQuery()) { + if (result.next()) { + return rowToEntity(result); + } else { + throw new IllegalArgumentException("Invalid id"); + } + } + } + } catch (SQLException e) { + LOG.log(Level.SEVERE, "Error getting a pet", e); + throw new DAOException(e); + } + } + + public List listAll() throws DAOException { + try (final Connection conn = this.getConnection()) { + final String query = "SELECT * FROM pets"; + + try (final PreparedStatement statement = conn.prepareStatement(query)) { + 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); + } + } + + public List listByPeopleID(int peopleID) throws DAOException { + try (final Connection conn = this.getConnection()) { + final String query = "SELECT * FROM pets where peopleID=?"; + + try (final PreparedStatement statement = conn.prepareStatement(query)) { + statement.setInt(1, peopleID); + + 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); + } + } + + public Pet add(String name, String type, int peopleID) throws DAOException, IllegalArgumentException { + if (name == null || type==null) { + throw new IllegalArgumentException("name and type 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, type); + statement.setInt(3, peopleID); + System.out.println(statement.toString()); + if (statement.executeUpdate() == 1) { + try (ResultSet resultKeys = statement.getGeneratedKeys()) { + if (resultKeys.next()) { + return new Pet(resultKeys.getInt(1), name, type, peopleID); + } 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); + } + } + + 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=?,type=?,peopleID=? WHERE id=?"; + + try (PreparedStatement statement = conn.prepareStatement(query)) { + statement.setString(1, pet.getName()); + statement.setString(2, pet.getType()); + statement.setInt(3, pet.getPeopleID()); + statement.setInt(4, pet.getId()); + + if (statement.executeUpdate() != 1) { + throw new IllegalArgumentException("name and peopleID can't be null"); + } + } + } catch (SQLException e) { + LOG.log(Level.SEVERE, "Error modifying a pet", e); + throw new DAOException(); + } + } + + 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); + } + } + + private Pet rowToEntity(ResultSet row) throws SQLException { + return new Pet( + row.getInt("id"), + row.getString("name"), + row.getString("type"), + row.getInt("peopleID") + ); + } +} 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 0000000..4a19369 --- /dev/null +++ b/src/main/java/es/uvigo/esei/daa/entities/Pet.java @@ -0,0 +1,80 @@ +package es.uvigo.esei.daa.entities; + +import static java.util.Objects.requireNonNull; + +/** + * An entity that represents a pet. + * + * @author Alex + */ +public class Pet { + + private int id; + private String name; + private String type; + private int peopleID; + + Pet() { + } + + public Pet(int id, String name, String type, int peopleID) { + this.id = id; + this.setName(name); + this.setType(type); + this.setPeopleID(peopleID); + } + + public int getId() { + return id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = requireNonNull(type, "Name can't be null"); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = requireNonNull(name, "Name can't be null"); + } + + public int getPeopleID() { + return peopleID; + } + + public void setPeopleID(int peopleID) { + this.peopleID = requireNonNull(peopleID, "PeopleID 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/PetResource.java b/src/main/java/es/uvigo/esei/daa/rest/PetResource.java new file mode 100644 index 0000000..bfa6ed3 --- /dev/null +++ b/src/main/java/es/uvigo/esei/daa/rest/PetResource.java @@ -0,0 +1,174 @@ +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.PetDAO; +import es.uvigo.esei.daa.entities.Pet; + +/** + * REST resource for managing people. + * + * @author Imanol Cobian Martinez + */ +@Path("/pets") +@Produces(MediaType.APPLICATION_JSON) +public class PetResource { + + private final static Logger LOG = Logger.getLogger(PeopleResource.class.getName()); + + private final PetDAO dao; + + public PetResource() { + this(new PetDAO()); + } + + PetResource(PetDAO dao) { + this.dao = dao; + } + + @GET + @Path("/{id}") + public Response get( + @PathParam("id") int id + ) { + try { + final Pet pet = this.dao.get(id); + + return Response.ok(pet).build(); + } catch (IllegalArgumentException iae) { + LOG.log(Level.FINE, "Invalid pet id in get method", iae); + + return Response.status(Response.Status.BAD_REQUEST) + .entity(iae.getMessage()) + .build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error getting a pet", e); + + return Response.serverError() + .entity(e.getMessage()) + .build(); + } + } + + @GET + public Response listAll() { + try { + return Response.ok(this.dao.listAll()).build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error listing pets", e); + return Response.serverError().entity(e.getMessage()).build(); + } + } + + @GET + @Path("/people{peopleID}") + public Response listByPeopleID( + @PathParam("peopleID") int peopleID + ) { + try { + return Response.ok(this.dao.listByPeopleID(peopleID)).build(); + } catch (DAOException e) { + LOG.log(Level.SEVERE, "Error listing pets", e); + return Response.serverError().entity(e.getMessage()).build(); + } + } + + @POST + public Response add( + @FormParam("name") String name, + @FormParam("type") String type, + @FormParam("peopleID") int peopleID + + ) { + try { + final Pet newPet = this.dao.add(name,type,peopleID); + + 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(); + } + } + + @PUT + @Path("/{id}") + public Response modify( + @PathParam("id") int id, + @FormParam("name") String name, + @FormParam("type") String type, + @FormParam("peopleID") int peopleID + ) { + try { + final Pet modifiedPet = new Pet(id, name,type,peopleID); + this.dao.modify(modifiedPet); + + return Response.ok(modifiedPet).build(); + } catch (NullPointerException npe) { + final String message = String.format("Invalid data for person (name: %s, peopleID: %s)", name, peopleID); + + 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(); + } + } + + @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/js/dao/pets.js b/src/main/webapp/js/dao/pets.js new file mode 100644 index 0000000..bd80be6 --- /dev/null +++ b/src/main/webapp/js/dao/pets.js @@ -0,0 +1,60 @@ +var PetDAO = (function () { + var resourcePath = "rest/pets/"; + //var peopleID = atob(localStorage.getItem('peopleID')); + var peopleID = 1; + 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 PetDAO() { + this.listPetsByPeopleID = function (peopleID,done, fail, always) { + requestByAjax({ + url: resourcePath + 'people' + peopleID, + type: 'GET' + }, done, fail, always); + }; + + this.listAll = 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); + }; + } + + return PetDAO; +})(); diff --git a/src/main/webapp/js/view/people.js b/src/main/webapp/js/view/people.js index 802d6b2..cac09d4 100644 --- a/src/main/webapp/js/view/people.js +++ b/src/main/webapp/js/view/people.js @@ -36,6 +36,7 @@ var PeopleView = (function() { function(person) { $('#person-' + person.id + ' td.name').text(person.name); $('#person-' + person.id + ' td.surname').text(person.surname); + $('#person-' + person.id + ' td.pet_type').text(person.type); self.resetForm(); }, showErrorMessage, @@ -168,6 +169,7 @@ var PeopleView = (function() { \ Editar\ Eliminar\ + Mascotas\ \ '; }; @@ -184,6 +186,14 @@ var PeopleView = (function() { $('#person-' + person.id + ' a.delete').click(function() { self.deletePerson(person.id); }); + + $('#person-' + person.id + ' a.show').click(function () { + document.getElementById("people-container").innerHTML = "

Mascotas de "+person.name+" "+person.surname+"

"; + + var view = new PetView(new PetDAO(), + 'people-container', 'people-container',person.id); + view.init(); + }); }; var appendToTable = function(person) { diff --git a/src/main/webapp/js/view/pets.js b/src/main/webapp/js/view/pets.js new file mode 100644 index 0000000..8d30df9 --- /dev/null +++ b/src/main/webapp/js/view/pets.js @@ -0,0 +1,261 @@ +var PetView = (function () { + var dao; + + // Referencia a this que permite acceder a las funciones públicas desde las funciones de jQuery. + var self; + var peopleID; + + var formId = 'people-form'; + var listId = 'people-list'; + var formQuery = '#' + formId; + var listQuery = '#' + listId; + + var humans = [] + var humans_list = [] + function Human(id, name, surname) { + this.id = id; + this.name = name; + this.surname = surname; + } + var cont=0; + var daoPeople = new PeopleDAO(); + daoPeople.listPeople(function (people) { + $.each(people, function (key, human) { + humans[human.id] = new Human(human.id, human.name, human.surname); + humans_list[cont] = new Human(human.id, human.name, human.surname); + cont++; + }); + }, + function () { + alert('No has sido posible acceder al listado de personas.'); + }); + + + function PetView(petDao, formContainerId, listContainerId,paramID) { + peopleID=paramID; + dao = petDao; + self = this; + insertPetForm($('#' + formContainerId), humans_list,humans); + insertPetList($('#' + listContainerId)); + console.log(humans); + + this.init = function () { + if(peopleID==="all"){ + dao.listAll(function (people) { + $.each(people, function (key, pet) { + appendToTable(pet, humans); + }); + }, + function () { + alert('No has sido posible acceder al listado de mascotas.'); + }); + }else{ + dao.listPetsByPeopleID(peopleID,function (people) { + $.each(people, function (key, pet) { + appendToTable(pet, humans); + }); + },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) { + $('#person-' + pet.id + ' td.name').text(pet.name); + $('#person-' + pet.id + ' td.surname').text(humans[pet.peopleID].name+" "+humans[pet.peopleID].surname); + $('#person-' + pet.id + ' td.pet_type').text(pet.type); + self.resetForm(); + }, + showErrorMessage, + self.enableForm + ); + } else { + dao.addPet(pet, + function (pet) { + appendToTable(pet,humans); + self.resetForm(); + }, + showErrorMessage, + self.enableForm + ); + } + + return false; + }); + + $('#btnClear').click(this.resetForm); + }; + + this.getPetInForm = function () { + var form = $(formQuery); + return { + 'id': form.find('input[name="id"]').val(), + 'name': form.find('input[name="name"]').val(), + 'type': form.find('input[name="type"]').val(), + 'peopleID': document.getElementById('dueno').value + }; + }; + + this.getPetInRow = function (id) { + var row = $('#person-' + id); + + if (row !== undefined) { + return { + 'id': id, + 'name': row.find('td.name').text(), + 'type': row.find('td.pet_type').text(), + 'peopleID': row.find('td.surname').text() + }; + } else { + return undefined; + } + }; + + this.editPet = function (id) { + var row = $('#person-' + id); + + if (row !== undefined) { + var form = $(formQuery); + + form.find('input[name="id"]').val(id); + form.find('input[name="name"]').val(row.find('td.name').text()); + form.find('input[name="surname"]').val(row.find('td.surname').text()); + form.find('input[name="type"]').val(row.find('td.pet_type').text()); + + $('input#btnSubmit').val('Modificar'); + } + }; + + this.deletePet = function (id) { + if (confirm('Está a punto de eliminar a una persona. ¿Está seguro de que desea continuar?')) { + dao.deletePet(id, + function () { + $('tr#person-' + 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'); + }; + } + ; + + + + var returnHumansSelect = function (humans) { + var toret = ""; + return toret; + }; + + var returnHumanTextBox = function (human) { + var toret =""; + return toret; + }; + + var insertPetForm = function (parent, humans,humans_map) { + var txtToAppend=""; + if(peopleID==='all'){ + txtToAppend=returnHumansSelect(humans); + }else{ + alert(peopleID); + txtToAppend=returnHumanTextBox(humans_map[peopleID]); + } + parent.append( + '
\ + \ +
\ +
\ + \ +
\ +
\ + \ +
\ +
Dueño:
\ +
'+ txtToAppend +'
\ +
\ + \ + \ +
\ +
\ +
' + ); + }; + var insertPetList = function (parent) { + parent.append( + '\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
NombreDueñoTipo 
' + ); + }; + var createPetRow = function (pet, human) { + return '\ + ' + pet.name + '\ + ' + human[pet.peopleID].name + " " + human[pet.peopleID].surname + '\ + ' + pet.type + '\ + \ + Editar\ + Eliminar\ + \ + '; + }; + + var showErrorMessage = function (jqxhr, textStatus, error) { + alert(textStatus + ": " + error); + }; + + var addRowListeners = function (pet) { + $('#person-' + pet.id + ' a.edit').click(function () { + self.editPet(pet.id); + }); + + $('#person-' + pet.id + ' a.delete').click(function () { + self.deletePet(pet.id); + }); + }; + + var appendToTable = function (pet, humans) { + $(listQuery + ' > tbody:last') + .append(createPetRow(pet, humans)); + addRowListeners(pet); + }; + + return PetView; +})(); diff --git a/src/main/webapp/main.html b/src/main/webapp/main.html index c2e28f1..649a0dc 100644 --- a/src/main/webapp/main.html +++ b/src/main/webapp/main.html @@ -15,6 +15,8 @@ DAA Example + + @@ -22,7 +24,7 @@
-

Personas

+
@@ -30,6 +32,8 @@ src="http://code.jquery.com/jquery-2.2.4.min.js"> + + diff --git a/src/test/java/es/uvigo/esei/daa/dataset/PetDataset.java b/src/test/java/es/uvigo/esei/daa/dataset/PetDataset.java new file mode 100644 index 0000000..551060e --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/dataset/PetDataset.java @@ -0,0 +1,71 @@ +package es.uvigo.esei.daa.dataset; + +import static java.util.Arrays.binarySearch; +import static java.util.Arrays.stream; + +import java.util.Arrays; +import java.util.function.Predicate; + +import es.uvigo.esei.daa.entities.Pet; + +public final class PetDataset { + private PetDataset() {} + public static Pet[] pets() { + return new Pet[] { + new Pet(1, "Trotsky", "Perro",1), + new Pet(2, "Cato", "Gato",2), + new Pet(3, "Hiroshi", "Conejo",1), + new Pet(4, "Jael", "Perro",3) + }; + } + + public static Pet[] petsWithout(int ... ids) { + Arrays.sort(ids); + + final Predicate hasValidId = pet -> + binarySearch(ids, pet.getId()) < 0; + + return stream(pets()) + .filter(hasValidId) + .toArray(Pet[]::new); + } + + public static Pet pet(int id) { + return stream(pets()) + .filter(pet -> pet.getId() == id) + .findAny() + .orElseThrow(IllegalArgumentException::new); + } + + public static int existentId() { + return 4; + } + + public static int nonExistentId() { + return 1234; + } + + public static Pet existentPet() { + return pet(existentId()); + } + + public static Pet nonExistentPet() { + return new Pet(nonExistentId(), "Lolo", "Vaca",4); + } + + public static String newName() { + return "Lucky"; + } + + public static String newType() { + return "Anaconda"; + } + + public static int newPeopleID() { + return 5; + } + + public static Pet newPet() { + return new Pet(pets().length + 1, newName(), newType(),newPeopleID()); + } +} diff --git a/src/test/java/es/uvigo/esei/daa/entities/PetUnitTest.java b/src/test/java/es/uvigo/esei/daa/entities/PetUnitTest.java new file mode 100644 index 0000000..29d2a15 --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/entities/PetUnitTest.java @@ -0,0 +1,118 @@ +package es.uvigo.esei.daa.entities; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; + +public class PetUnitTest { + @Test + public void testPetIntStringString() { + final int id = 1; + final String name = "Casper"; + final String type = "Perro"; + final int peopleID = 1; + + final Pet pet = new Pet(id, name, type,peopleID); + + assertThat(pet.getId(), is(equalTo(id))); + assertThat(pet.getName(), is(equalTo(name))); + assertThat(pet.getType(), is(equalTo(type))); + assertThat(pet.getPeopleID(), is(equalTo(peopleID))); + } + + @Test(expected = NullPointerException.class) + public void testPetIntStringStringNullName() { + new Pet(1, null, "Perro",1); + } + + @Test(expected = NullPointerException.class) + public void testPersonIntStringStringNullType() { + new Pet(1, "Casper",null,1); + } + + /* public void testPersonIntStringStringNullPeopleID() { + new Pet(1, "Casper","Type",null); + }*/ + + @Test + public void testSetName() { + final int id = 1; + final String type = "Perro"; + final int peopleID = 1; + + final Pet pet = new Pet(id, "Casper", type,peopleID); + pet.setName("Juan"); + + assertThat(pet.getId(), is(equalTo(id))); + assertThat(pet.getName(), is(equalTo("Juan"))); + assertThat(pet.getType(), is(equalTo(type))); + assertThat(pet.getPeopleID(), is(equalTo(peopleID))); + } + + @Test(expected = NullPointerException.class) + public void testSetNullName() { + final Pet pet = new Pet(1, "Casper", "Perro",1); + + pet.setName(null); + } + + @Test + public void testSetType() { + final int id = 1; + final String name = "Casper"; + final int peopleID = 1; + + final Pet pet = new Pet(id,name,"Perro",peopleID); + pet.setType("Gato"); + + assertThat(pet.getId(), is(equalTo(id))); + assertThat(pet.getName(), is(equalTo(name))); + assertThat(pet.getType(), is(equalTo("Gato"))); + assertThat(pet.getPeopleID(), is(equalTo(peopleID))); + } + + @Test(expected = NullPointerException.class) + public void testSetNullType() { + final Pet pet = new Pet(1, "Casper", "Perro",1); + + pet.setType(null); + } + + @Test + public void testSetPeopleID() { + final int id = 1; + final String name = "Casper"; + final String type = "Gato"; + + final Pet pet = new Pet(id,name,type,1); + pet.setPeopleID(2); + + assertThat(pet.getId(), is(equalTo(id))); + assertThat(pet.getName(), is(equalTo(name))); + assertThat(pet.getType(), is(equalTo(type))); + assertThat(pet.getPeopleID(), is(equalTo(2))); + } + + @Test + public void testEqualsObject() { + final Pet petA = new Pet(1, "Name A", "Type A",1); + final Pet petB = new Pet(1, "Name B", "Type B",1); + + assertTrue(petA.equals(petB)); + } + + @Test + public void testEqualsHashcode() { + EqualsVerifier.forClass(Pet.class) + .withIgnoredFields("name", "type","peopleID") + .suppress(Warning.STRICT_INHERITANCE) + .suppress(Warning.NONFINAL_FIELDS) + .verify(); + } +} diff --git a/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java b/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java index 40400f7..01dd8fb 100644 --- a/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java +++ b/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java @@ -31,7 +31,7 @@ import es.uvigo.esei.daa.entities.User; @Priority(Priorities.AUTHENTICATION) public class AuthorizationFilter implements ContainerRequestFilter { // Add here the list of REST paths that an administrator can access. - private final static List ADMIN_PATHS = Arrays.asList("people"); + private final static List ADMIN_PATHS = Arrays.asList("people","pets"); private final UsersDAO dao; diff --git a/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToPet.java b/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToPet.java new file mode 100644 index 0000000..38548c9 --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToPet.java @@ -0,0 +1,57 @@ +package es.uvigo.esei.daa.matchers; + +import org.hamcrest.Factory; +import org.hamcrest.Matcher; + +import es.uvigo.esei.daa.entities.Pet; + +public class IsEqualToPet extends IsEqualToEntity { + public IsEqualToPet(Pet entity) { + super(entity); + } + + @Override + protected boolean matchesSafely(Pet actual) { + this.clearDescribeTo(); + + if (actual == null) { + this.addTemplatedDescription("actual", expected.toString()); + return false; + } else { + return checkAttribute("id", Pet::getId, actual) + && checkAttribute("name", Pet::getName, actual) + && checkAttribute("surname", Pet::getType, actual) + && checkAttribute("peopleID", Pet::getPeopleID, actual); + } + } + + /** + * Factory method that creates a new {@link IsEqualToEntity} matcher with + * the provided {@link Person} as the expected value. + * + * @param person the expected person. + * @return a new {@link IsEqualToEntity} matcher with the provided + * {@link Person} as the expected value. + */ + @Factory + public static IsEqualToPet equalsToPet(Pet pet) { + return new IsEqualToPet(pet); + } + + /** + * Factory method that returns a new {@link Matcher} that includes several + * {@link IsEqualToPerson} matchers, each one using an {@link Person} of the + * provided ones as the expected value. + * + * @param persons the persons to be used as the expected values. + * @return a new {@link Matcher} that includes several + * {@link IsEqualToPerson} matchers, each one using an {@link Person} of the + * provided ones as the expected value. + * @see IsEqualToEntity#containsEntityInAnyOrder(java.util.function.Function, Object...) + */ + @Factory + public static Matcher> containsPetsInAnyOrder(Pet ... pets) { + return containsEntityInAnyOrder(IsEqualToPet::equalsToPet, pets); + } + +} diff --git a/src/test/java/es/uvigo/esei/daa/rest/PetResourceTest.java b/src/test/java/es/uvigo/esei/daa/rest/PetResourceTest.java new file mode 100644 index 0000000..cc7ae97 --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/rest/PetResourceTest.java @@ -0,0 +1,325 @@ +package es.uvigo.esei.daa.rest; + +import static es.uvigo.esei.daa.dataset.PetDataset.existentId; +import static es.uvigo.esei.daa.dataset.PetDataset.existentPet; +import static es.uvigo.esei.daa.dataset.PetDataset.newName; +import static es.uvigo.esei.daa.dataset.PetDataset.newPet; +import static es.uvigo.esei.daa.dataset.PetDataset.newType; +import static es.uvigo.esei.daa.dataset.PetDataset.newPeopleID; +import static es.uvigo.esei.daa.dataset.PetDataset.nonExistentId; +import static es.uvigo.esei.daa.dataset.PetDataset.pets; +import static es.uvigo.esei.daa.dataset.UsersDataset.adminLogin; +import static es.uvigo.esei.daa.dataset.UsersDataset.normalLogin; +import static es.uvigo.esei.daa.dataset.UsersDataset.userToken; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasBadRequestStatus; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasOkStatus; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasUnauthorized; +import static es.uvigo.esei.daa.matchers.IsEqualToPet.containsPetsInAnyOrder; +import static es.uvigo.esei.daa.matchers.IsEqualToPet.equalsToPet; +import static javax.ws.rs.client.Entity.entity; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.IOException; +import java.util.List; + +import javax.sql.DataSource; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.github.springtestdbunit.DbUnitTestExecutionListener; +import com.github.springtestdbunit.annotation.DatabaseSetup; +import com.github.springtestdbunit.annotation.ExpectedDatabase; + +import es.uvigo.esei.daa.DAAExampleTestApplication; +import es.uvigo.esei.daa.entities.Pet; +import es.uvigo.esei.daa.listeners.ApplicationContextBinding; +import es.uvigo.esei.daa.listeners.ApplicationContextJndiBindingTestExecutionListener; +import es.uvigo.esei.daa.listeners.DbManagement; +import es.uvigo.esei.daa.listeners.DbManagementTestExecutionListener; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:contexts/mem-context.xml") +@TestExecutionListeners({ + DbUnitTestExecutionListener.class, + DbManagementTestExecutionListener.class, + ApplicationContextJndiBindingTestExecutionListener.class +}) +@ApplicationContextBinding( + jndiUrl = "java:/comp/env/jdbc/daaexample", + type = DataSource.class +) +@DbManagement( + create = "classpath:db/hsqldb.sql", + drop = "classpath:db/hsqldb-drop.sql" +) +@DatabaseSetup("/datasets/dataset.xml") +@ExpectedDatabase("/datasets/dataset.xml") +public class PetResourceTest extends JerseyTest { + @Override + protected Application configure() { + return new DAAExampleTestApplication(); + } + + @Override + protected void configureClient(ClientConfig config) { + super.configureClient(config); + + // Enables JSON transformation in client + config.register(JacksonJsonProvider.class); + config.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE); + } + + @Test + public void testList() throws IOException { + final Response response = target("pets").request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .get(); + assertThat(response, hasOkStatus()); + + final List pets = response.readEntity(new GenericType>(){}); + + assertThat(pets, containsPetsInAnyOrder(pets())); + } + + @Test + public void testListUnauthorized() throws IOException { + final Response response = target("pets").request() + .header("Authorization", "Basic " + userToken(normalLogin())) + .get(); + assertThat(response, hasUnauthorized()); + } + + @Test + public void testGet() throws IOException { + final Response response = target("pets/" + existentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .get(); + assertThat(response, hasOkStatus()); + + final Pet pet = response.readEntity(Pet.class); + + assertThat(pet, is(equalsToPet(existentPet()))); + } + + @Test + public void testGetUnauthorized() throws IOException { + final Response response = target("pets/" + existentId()).request() + .header("Authorization", "Basic " + userToken(normalLogin())) + .get(); + assertThat(response, hasUnauthorized()); + } + + @Test + public void testGetInvalidId() throws IOException { + final Response response = target("pets/" + nonExistentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .get(); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + @ExpectedDatabase("/datasets/dataset-add.xml") + public void testAdd() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("type", newType()); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + assertThat(response, hasOkStatus()); + + final Pet pet = response.readEntity(Pet.class); + + assertThat(pet, is(equalsToPet(newPet()))); + } + + @Test + public void testAddUnauthorized() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("type", newType()); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(normalLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasUnauthorized()); + } + + @Test + public void testAddMissingName() throws IOException { + final Form form = new Form(); + form.param("type", newType()); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + public void testAddMissingType() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + public void testAddMissingPeopleID() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("type", newType()); + + final Response response = target("pets").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + @ExpectedDatabase("/datasets/dataset-modify.xml") + public void testModify() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("type", newType()); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + assertThat(response, hasOkStatus()); + + final Pet modifiedPet = response.readEntity(Pet.class); + + final Pet pet = existentPet(); + pet.setName(newName()); + pet.setType(newType()); + pet.setPeopleID(newPeopleID()); + + assertThat(modifiedPet, is(equalsToPet(pet))); + } + + @Test + public void testModifyUnauthorized() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("type", newType()); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(normalLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasUnauthorized()); + } + + @Test + public void testModifyName() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + + final Response response = target("pets/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + public void testModifyType() throws IOException { + final Form form = new Form(); + form.param("type", newType()); + + final Response response = target("pets/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + public void testModifyPeopleID() throws IOException { + final Form form = new Form(); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + public void testModifyInvalidId() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("type", newType()); + form.param("peopleID", String.valueOf(newPeopleID())); + + final Response response = target("pets/" + nonExistentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasBadRequestStatus()); + } + + @Test + @ExpectedDatabase("/datasets/dataset-delete.xml") + public void testDelete() throws IOException { + final Response response = target("pets/" + existentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .delete(); + + assertThat(response, hasOkStatus()); + + final Integer deletedId = response.readEntity(Integer.class); + + assertThat(deletedId, is(equalTo(existentId()))); + } + + @Test + public void testDeleteUnauthorized() throws IOException { + final Response response = target("pets/" + existentId()).request() + .header("Authorization", "Basic " + userToken(normalLogin())) + .delete(); + + assertThat(response, hasUnauthorized()); + } + + @Test + public void testDeleteInvalidId() throws IOException { + final Response response = target("pets/" + nonExistentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .delete(); + + assertThat(response, hasBadRequestStatus()); + } +} diff --git a/src/test/java/es/uvigo/esei/daa/suites/IntegrationTestSuite.java b/src/test/java/es/uvigo/esei/daa/suites/IntegrationTestSuite.java index 3f0c667..10e8f95 100644 --- a/src/test/java/es/uvigo/esei/daa/suites/IntegrationTestSuite.java +++ b/src/test/java/es/uvigo/esei/daa/suites/IntegrationTestSuite.java @@ -5,10 +5,12 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import es.uvigo.esei.daa.rest.PeopleResourceTest; +import es.uvigo.esei.daa.rest.PetResourceTest; import es.uvigo.esei.daa.rest.UsersResourceTest; @SuiteClasses({ PeopleResourceTest.class, + PetResourceTest.class, UsersResourceTest.class }) @RunWith(Suite.class) diff --git a/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java b/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java index a08e965..7318123 100644 --- a/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java +++ b/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java @@ -5,9 +5,12 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import es.uvigo.esei.daa.entities.PersonUnitTest; +import es.uvigo.esei.daa.entities.PetUnitTest; @SuiteClasses({ - PersonUnitTest.class + PersonUnitTest.class, + PetUnitTest.class + }) @RunWith(Suite.class) public class UnitTestSuite { diff --git a/src/test/resources/datasets/dataset-add.xml b/src/test/resources/datasets/dataset-add.xml index 9a75a99..ab1b90f 100644 --- a/src/test/resources/datasets/dataset-add.xml +++ b/src/test/resources/datasets/dataset-add.xml @@ -16,4 +16,10 @@ + + + + + + \ No newline at end of file diff --git a/src/test/resources/datasets/dataset-delete.xml b/src/test/resources/datasets/dataset-delete.xml index e49223d..711d3a8 100644 --- a/src/test/resources/datasets/dataset-delete.xml +++ b/src/test/resources/datasets/dataset-delete.xml @@ -14,4 +14,8 @@ + + + + \ No newline at end of file diff --git a/src/test/resources/datasets/dataset-modify.xml b/src/test/resources/datasets/dataset-modify.xml index 6e2dfc9..fdb534e 100644 --- a/src/test/resources/datasets/dataset-modify.xml +++ b/src/test/resources/datasets/dataset-modify.xml @@ -15,4 +15,9 @@ + + + + + \ No newline at end of file diff --git a/src/test/resources/datasets/dataset.dtd b/src/test/resources/datasets/dataset.dtd index e64500f..d584ec5 100644 --- a/src/test/resources/datasets/dataset.dtd +++ b/src/test/resources/datasets/dataset.dtd @@ -1,7 +1,8 @@ - + + + diff --git a/src/test/resources/datasets/dataset.xml b/src/test/resources/datasets/dataset.xml index 3f48cc9..7780785 100644 --- a/src/test/resources/datasets/dataset.xml +++ b/src/test/resources/datasets/dataset.xml @@ -15,4 +15,9 @@ + + + + + \ No newline at end of file diff --git a/src/test/resources/db/hsqldb-drop.sql b/src/test/resources/db/hsqldb-drop.sql index 31f8643..7bc45bd 100644 --- a/src/test/resources/db/hsqldb-drop.sql +++ b/src/test/resources/db/hsqldb-drop.sql @@ -1,2 +1,3 @@ DROP TABLE People IF EXISTS; DROP TABLE Users IF EXISTS; +DROP TABLE Pets IF EXISTS; diff --git a/src/test/resources/db/hsqldb.sql b/src/test/resources/db/hsqldb.sql index a629441..5486fbe 100644 --- a/src/test/resources/db/hsqldb.sql +++ b/src/test/resources/db/hsqldb.sql @@ -10,4 +10,12 @@ CREATE TABLE users ( password VARCHAR(64) NOT NULL, role VARCHAR(5) NOT NULL, PRIMARY KEY (login) +); + +CREATE TABLE pets( + id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) NOT NULL, + name VARCHAR(50) NOT NULL, + type VARCHAR(100) NOT NULL, + peopleID INTEGER NOT NULL, + PRIMARY KEY (id) ); \ No newline at end of file -- 2.18.1