diff --git a/diagram.svg b/diagram.svg index dfb5ff3..c03813a 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradlegradlebuildbuild.gradle.gradleprotoprotomainmaindocsdocsbdd-testbdd-testtmptmptest-resultstest-resultsresourcesresourcesreportsreportslibslibsjacocojacocogenerated/sourcesgenerated/sourcesgenerated-snippetsgenerated-snippetsdocsdocsclasses/javaclasses/java7.67.6resourcesresourcesjava/com/arc_e_tect/blogjava/com/arc_e_tect/blogresourcesresourcesdocGendocGenbddTestbddTestbddTestbddTestteststestsjacocojacocobdd-testbdd-testmain/com/arc_e_tect/blogmain/com/arc_e_tect/blogdependencies-accessorsdependencies-accessorswebwebphonebookphonebookwebwebMETA-INFMETA-INFtesttestdocGendocGenbddTestbddTestHtmlHtmlphonebookphonebookwebwebwebwebserviceservicewebwebserviceservicecontactscontactscontactscontacts.6.adoc.asciidoc.bat.css.csv.feature.gitignore.gradle.html.java.js.json.md.properties.snippet.toml.txt.xml.ymleach dot sized by file size \ No newline at end of file +srcsrcgradlegradlebuildbuild.gradle.gradleprotoprotomainmaindocsdocsbdd-testbdd-testtmptmptest-resultstest-resultsresourcesresourcesreportsreportsjacocojacocogenerated/sourcesgenerated/sourcesgenerated-snippetsgenerated-snippetsdocsdocsclasses/javaclasses/java7.67.6resourcesresourcesjava/com/arc_e_tect/blogjava/com/arc_e_tect/blogresourcesresourcesdocGendocGenbddTestbddTestbddTestbddTestteststestsjacocojacocobdd-testbdd-testmain/com/arc_e_tect/blogmain/com/arc_e_tect/blogdependencies-accessorsdependencies-accessorswebwebphonebookphonebookwebwebMETA-INFMETA-INFtesttestdocGendocGenbddTestbddTestHtmlHtmlphonebookphonebookwebwebwebwebserviceservicewebwebserviceservicecontactscontactscontactscontacts.6.adoc.asciidoc.bat.css.csv.feature.gitignore.gradle.html.java.js.json.md.properties.snippet.svg.toml.txt.xml.ymleach dot sized by file size \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f669143..d89de70 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,8 +41,10 @@ nebula-lint = { id = "nebula.lint", version = "_" } spring-boot = "3.0.6" ## ⬆ = "3.0.7" ## ⬆ = "3.0.8" +## ⬆ = "3.0.9" ## ⬆ = "3.1.0" ## ⬆ = "3.1.1" +## ⬆ = "3.1.2" spring-restdocs = "3.0.0" [libraries] diff --git a/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactSteps.java b/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactSteps.java index 95fdba9..36996b6 100644 --- a/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactSteps.java +++ b/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactSteps.java @@ -13,6 +13,7 @@ import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.mongodb.client.result.DeleteResult; +import com.mongodb.client.result.InsertOneResult; import io.cucumber.java.Before; import io.cucumber.java.DataTableType; import io.cucumber.java.en.Given; @@ -26,6 +27,7 @@ import org.springframework.http.HttpStatus; import java.io.IOException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -48,6 +50,28 @@ public class ContactSteps { CodecRegistry pojoCodecRegistry; MongoCollection collection; + public static String contactPostTemplate = + """ + {"id":%d,"name":"%s","phone":"%s"} + """; + public static String contactPostNameNullTemplate = + """ + {"id":%d,"name":null,"phone":"%s"} + """; + public static String contactPatchPhoneTemplate = + """ + {"id":%d,"phone":"%s"}; + """; + + public static String contactPatchNameTemplate = + """ + {"id":%d,"name":"%s"}; + """; + public static String contactPatchNamePhoneTemplate = + """ + {"id":%d,"name":"%s","phone":"%s"}; + """; + private long contactId = 1; private TestContact contact; @@ -74,32 +98,35 @@ public void the_phonebook_is_empty() { collection.drop(); } - @Given("the contact with name {string} is listed in the phonebook") - public void the_contact_with_name_is_listed_in_the_phonebook(String name) { - TestContact newContact = new TestContact(); - newContact.setName(name); - newContact.setId(contactId++); - collection.insertOne(newContact); - } + @Given("the listed contact(s)") + public void the_listed_contacts(io.cucumber.datatable.DataTable dataTable) { + int contactId=0; + List> signUpForms = dataTable.asMaps(String.class, String.class); - @Given("the contact with name {string} is not listed in the phonebook") - public void the_contact_with_name_is_not_listed_in_the_phonebook(String name) { - Bson query = eq("name", name); - try { - DeleteResult result = collection.deleteOne(query); - log.atInfo().log("Deleted document count: %S", result.getDeletedCount()); - } catch (MongoException me) { - log.atWarning().log("Unable to delete due to an error: %s", me); + Iterator> it = signUpForms.iterator(); + List contactList = new ArrayList<>(); + while (it.hasNext()) { + Map item = it.next(); + + String name = item.get("name"); + String phone = item.get("phone"); + TestContact testContact = new TestContact(); + testContact.setName(name); + testContact.setPhone(phone); + + String id = item.get("id"); + if (id != null) { + testContact.setId(Long.parseLong(id)); + } else { + testContact.setId(++contactId); + } + InsertOneResult result = collection.insertOne(testContact); } } - @Given("the unlisted contact") - public void the_unlisted_contact(io.cucumber.datatable.DataTable dataTable) { - List> signUpForms = dataTable.asMaps(String.class, String.class); - - String name = signUpForms.get(0).get("name"); - - Bson query = eq("name", name); + @Given("the contact with id {long} is not listed in the phonebook") + public void the_contact_with_id_is_not_listed_in_the_phonebook(Long id) { + Bson query = eq("id", id); try { DeleteResult result = collection.deleteOne(query); log.atInfo().log("Deleted document count: %S", result.getDeletedCount()); @@ -108,93 +135,104 @@ public void the_unlisted_contact(io.cucumber.datatable.DataTable dataTable) { } } - @When("the contact with name {string} is added to the phonebook") - public void the_contact_with_name_is_added_to_the_phonebook(String name) throws IOException { - ContactResource resource = new ContactResource(); - resource.setName(name); - httpClient.postNewContact(resource); + @When("the contact with id {long} is requested") + public void the_contact_with_name_is_requested(long id) throws IOException { + httpClient.getSingleById(id); } - @When("the contact is added to the phonebook") - public void the_contact_is_added_to_the_phonebook(io.cucumber.datatable.DataTable dataTable) throws IOException { - List> signUpForms = dataTable.asMaps(String.class, String.class); + @When("the contact with name {string} is requested") + public void the_contact_with_name_is_requested(String name) throws IOException { + httpClient.getSingleByName(name); + } - String name = signUpForms.get(0).get("name"); - String phone = signUpForms.get(0).get("phone"); + @When("all contacts are requested") + public void all_contacts_are_requested() throws IOException { + httpClient.getAll(); + } - ContactResource resource = new ContactResource(); - resource.setId(contactId++); - resource.setName(name); - resource.setPhone(phone); - httpClient.postNewContact(resource); + @When("the contact with name {string} is deleted") + public void the_contact_with_name_is_deleted(String name) { + httpClient.deleteContactByName(name); } - @When("the contact with no name is added to the phonebook") - public void the_contact_with_no_name_is_added_to_the_phonebook() throws IOException { - ContactResource resource = new ContactResource(); - httpClient.postNewContact(resource); + @When("the contact with id {int} is deleted") + public void the_contact_with_id_is_deleted(int id) { + httpClient.deleteContactById(id); } - @Given("the listed contact") - public void the_listed_contact(io.cucumber.datatable.DataTable dataTable) { - List> listedContacts = dataTable.asMaps(String.class, String.class); + @When("adding to the phonebook the contact") + public void adding_to_the_phonebook_the_contact(io.cucumber.datatable.DataTable dataTable) { + int contactId=0; + List> signUpForms = dataTable.asMaps(String.class, String.class); - String name = listedContacts.get(0).get("name"); - String phone = listedContacts.get(0).get("phone"); + Iterator> it = signUpForms.iterator(); + Map item = it.next(); - contact.setId(contactId++); - contact.setName(name); - contact.setPhone(phone); - collection.insertOne(contact); - } + String name = item.get("name"); + String phone = item.get("phone"); + ContactResource contactResource = new ContactResource(); + contactResource.setName(name); + contactResource.setPhone(phone); - @When("the phone number of contact {string} is changed to {string}") - public void the_phone_number_of_contact_is_changed_to(String name, String phone) throws JsonProcessingException { - ContactResource resource = new ContactResource(0, name, phone); - httpClient.patchContact(name,resource); - } + String id = item.get("id"); + if (id != null) { + contactResource.setId(Long.parseLong(id)); + } else { + contactResource.setId(++contactId); + } - @When("the name of contact {string} is changed to {string}") - public void the_name_of_contact_is_changed_to(String name, String newName) throws JsonProcessingException { - ContactResource resource = new ContactResource(0, newName, null); - httpClient.patchContact(name,resource); + String requestJson; + if (name != null) { + requestJson = String.format(contactPostTemplate, + contactResource.getId(), contactResource.getName(), contactResource.getPhone()); + } else { + requestJson = String.format(contactPostNameNullTemplate, + contactResource.getId(), contactResource.getPhone()); + } + + httpClient.postNewContact(contactResource.getId(),requestJson); } - @When("the contact with name {string} is deleted") - public void the_contact_with_name_is_deleted(String name) { - httpClient.deleteContact(name); + @When("the phone number of contact {int} is changed to {string}") + public void the_phone_number_of_contact_is_changed_to(int id, String phone) throws JsonProcessingException { + String patchJson = String.format(contactPatchPhoneTemplate,id,phone); + httpClient.patchContact(id,patchJson); } - @When("all contacts are requested") - public void the_api_consumer_requests() throws IOException { - httpClient.getAll(); + @When("the name of contact {int} is changed to {string}") + public void the_name_of_contact_is_changed_to(Integer id, String name) throws JsonProcessingException { + String patchJson = String.format(contactPatchNameTemplate,id,name); + httpClient.patchContact(id,patchJson); } - @When("the contact with name {string} is requested") - public void the_contact_with_name_is_requested(String name) throws IOException { - httpClient.getSingleByName(name); + @Then("the response is an error indicating that the contact could not be found") + public void the_response_is_an_error_indicating_that_the_contact_could_not_be_found() { + assertEquals(HttpStatus.NOT_FOUND, httpClient.getHttpStatus()); } - @Then("the contact with phone {string} has name {string}") - public void the_contact_with_phone_has_name(String phone, String name) { - Bson query = eq("name", name); - try { - TestContact found = collection.find(query).first(); - assertAll("Found with phone and name", - () -> assertNotNull(found), - () -> assertEquals(phone, found.getPhone())); - } catch (MongoException me) { - log.atWarning().log("Unable to find due to an error: %s", me); - } + @Then("the response contains a/the single contact with id {long}") + public void the_response_contains_a_single_contact_with_id(Long id) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode contactNode = objectMapper.readTree(httpClient.getBody()); + long contactId = contactNode.path("id").asLong(); + assertEquals(id, contactId); } - @Then("the response contains no contacts") - public void the_response_contains_no_contacts() { - assertEquals(204, httpClient.getHttpStatus().value()); + @Then("the response contains the (new )contact {string} with phone {string}") + public void the_response_contains_the_contact_with_phone(String name, String phone) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode contactNode = objectMapper.readTree(httpClient.getBody()); + String contactName = contactNode.path("name").asText(); + String contactPhone = contactNode.path("phone").asText(); + + assertAll("Contact", + () -> assertEquals(name, contactName), + () -> assertEquals(phone, contactPhone) + ); } - @Then("the response contains the contact {string}") - public void the_response_contains_the_contact(String name) throws JsonProcessingException { + @Then("the response contains a contact {string} with phone {string}") + public void the_response_contains_a_contact_with_phone(String name, String phone) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode rootNode = objectMapper.readTree(httpClient.getBody()); JsonNode contactsNode = rootNode.path("_embedded").path("contacts"); @@ -203,41 +241,24 @@ public void the_response_contains_the_contact(String name) throws JsonProcessing while (it.hasNext()) { JsonNode contactNode = it.next(); String contactName = contactNode.path("name").asText(); - if (name.equals(contactName)) { + String contactPhone = contactNode.path("phone").asText(); + if (name.equals(contactName) && phone.equals(contactPhone)) { found = true; + break; } } assertTrue(found); } - @Then("the contact cannot be found") - public void the_contact_cannot_be_found() { - assertEquals(HttpStatus.NOT_FOUND, httpClient.getHttpStatus()); - } - - @Then("the response contains no contact") - public void the_response_contains_no_contact() throws JsonProcessingException { + @Then("{int} contacts are retrieved") + public void contacts_are_retrieved(Integer expectedNumberOfContacts) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); - JsonNode contactNode = objectMapper.readTree(httpClient.getBody()); - assertAll("No Contact", - () -> assertNull(contactNode.get("id")), - () -> assertNull(contactNode.get("name"))); - } - - @Then("the phonebook contains the contact with name {string}") - public void the_phonebook_contains_the_contact_with_name(String name) { - Bson query = eq("name", name); - try { - TestContact found = collection.find(query).first(); - assertAll("Found with name", - () -> assertNotNull(found), - () -> assertEquals(name, found.getName())); - } catch (MongoException me) { - log.atWarning().log("Unable to find due to an error: %s", me); - } + JsonNode rootNode = objectMapper.readTree(httpClient.getBody()); + JsonNode contactsNode = rootNode.path("_embedded").path("contacts"); + assertEquals((int) expectedNumberOfContacts, contactsNode.size()); } - @Then("the phonebook does not contain the contact with name {string}") + @Then("the phonebook does not contain a/the contact with name {string}") public void the_phonebook_does_not_contain_the_contact_with_name(String name) { Bson query = eq("name", name); try { @@ -248,9 +269,9 @@ public void the_phonebook_does_not_contain_the_contact_with_name(String name) { } } - @Then("the phonebook does not contain a contact with no name") - public void the_phonebook_does_not_contain_a_contact_with_no_name() { - Bson query = eq("name", ""); + @Then("the phonebook does not contain a/the contact with id {long}") + public void the_phonebook_does_not_contain_a_contact_with_id(Long id) { + Bson query = eq("id", id); try { TestContact found = collection.find(query).first(); assertNull(found); @@ -259,70 +280,14 @@ public void the_phonebook_does_not_contain_a_contact_with_no_name() { } } - @Then("the response contains the contact with name {string}") - public void the_response_contains_the_contact_with_name(String name) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode contactNode = objectMapper.readTree(httpClient.getBody()); - String contactName = contactNode.path("name").asText(); - assertEquals(name, contactName); - } - - @Then("the response contains the contact with phone {string}") - public void the_response_contains_the_contact_with_phone(String phone) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode contactNode = objectMapper.readTree(httpClient.getBody()); - String contactPhone = contactNode.path("phone").asText(); - assertEquals(phone, contactPhone); - } - - @Then("the contact with name {string} has phone number {string}") - public void the_contact_with_name_has_phone(String name, String phone) { - Bson query = eq("name", name); - try { - TestContact found = collection.find(query).first(); - assertAll("Found with name and phone", - () -> assertNotNull(found), - () -> assertEquals(phone, found.getPhone())); - } catch (MongoException me) { - log.atWarning().log("Unable to find due to an error: %s", me); - } - } - - @Then("the contact with name {string} has no phone number") - public void the_contact_with_name_has_phone(String name) { - Bson query = eq("name", name); - try { - TestContact found = collection.find(query).first(); - assertAll("Found with name and no phone", - () -> assertNotNull(found), - () -> assertNull(found.getPhone())); - } catch (MongoException me) { - log.atWarning().log("Unable to find due to an error: %s", me); - } - } - - @Then("the contact formerly known as {string} now has name {string}") - public void the_contact_formerly_known_as_now_has_name(String oldName, String newName) { - Bson query = eq("name", newName); - try { - TestContact found = collection.find(query).first(); - assertAll("Found with name and phone", - () -> assertNotNull(found), - () -> assertEquals(contact.getId(), found.getId()), - () -> assertEquals(contact.getPhone(), found.getPhone()) - ); - } catch (MongoException me) { - log.atWarning().log("Unable to find due to an error: %s", me); - } + @Then("the response is an error indicating that the contact already exists") + public void the_response_is_an_error_indicating_that_the_contact_already_exists() { + assertEquals(HttpStatus.CONFLICT, httpClient.getHttpStatus()); } - @Then("the response is an error indicating that invalid contact data was provided") - public void the_response_is_an_error_indicating_that_invalid_contact_data_was_provided() { + @Then("the response is an error indicating that the contact details are invalid") + public void the_response_is_an_error_indicating_that_the_contact_details_are_invalid() { assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, httpClient.getHttpStatus()); } - @Then("the response is an error indicating that a contact with the same name already exists") - public void the_response_is_an_error_indicating_that_a_contact_with_the_same_name_already_exists() { - assertEquals(HttpStatus.CONFLICT, httpClient.getHttpStatus()); - } } diff --git a/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsHttpClient.java b/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsHttpClient.java index 050d3d9..7e85fd8 100644 --- a/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsHttpClient.java +++ b/src/bdd-test/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsHttpClient.java @@ -57,7 +57,12 @@ public void getAll() throws IOException { } public void getSingleByName(String name) throws IOException { - String url = String.format("%s/%s", apiEndpoint(), name); + String url = String.format("%s?contactName=%s", apiEndpoint(), name); + executeGet(url); + } + + public void getSingleById(long id) throws IOException { + String url = String.format("%s/%d", apiEndpoint(), id); executeGet(url); } @@ -71,15 +76,18 @@ public void postNewContact(long id, String jsonDoc) { executePost(apiEndpoint(), jsonDoc); } - public void patchContact(String name, ContactResource resource) throws JsonProcessingException { - String url = String.format("%s/%s", apiEndpoint(), name); - String jsonDocument = new ObjectMapper().writeValueAsString(resource); - + public void patchContact(long id, String jsonDocument) throws JsonProcessingException { + String url = String.format("%s/%d", apiEndpoint(), id); executePatch(url, jsonDocument); } - public void deleteContact(String contactName) { - String url = String.format("%s/%s", apiEndpoint(), contactName); + public void deleteContactByName(String contactName) { + String url = String.format("%s?contactName=%s", apiEndpoint(), contactName); + executeDelete(url); + } + + public void deleteContactById(Integer contactId) { + String url = String.format("%s/%d", apiEndpoint(), contactId); executeDelete(url); } diff --git a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/add_a_contact.feature b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/add_a_contact.feature index 41e4354..d73d509 100644 --- a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/add_a_contact.feature +++ b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/add_a_contact.feature @@ -1,48 +1,50 @@ Feature: Add a contact to the phonebook Scenario: 01 - A new contact - Given the contact with name "Peter Parker" is not listed in the phonebook - When the contact is added to the phonebook - | name | phone | - | Peter Parker | +1 (555) 432748 | - Then the phonebook contains the contact with name "Peter Parker" - And the contact with name "Peter Parker" has phone number "+1 (555) 432748" - And the response contains the contact with name "Peter Parker" - And the response contains the contact with phone "+1 (555) 432748" + Given the phonebook is empty + When adding to the phonebook the contact + | id | name | phone | + | 42 | Peter Parker | +1 (555) 432748 | + Then the response contains the new contact "Peter Parker" with phone "+1 (555) 432748" - Scenario: 02 - A contact with no name - When the contact with no name is added to the phonebook - Then the phonebook does not contain a contact with no name - And the response is an error indicating that invalid contact data was provided + Scenario: 02 - A new contact without a phone + Given the phonebook is empty + When adding to the phonebook the contact + | id | name | phone | + | 42 | Peter Parker | [blank] | + Then the response contains the new contact "Peter Parker" with phone "" - Scenario: 03 - A contact with an empty string as name - When the contact with name "" is added to the phonebook - Then the phonebook does not contain a contact with no name - And the response is an error indicating that invalid contact data was provided - - Scenario: 04 - An already listed contact - Given the contact with name "John Smith" is listed in the phonebook - When the contact with name "John Smith" is added to the phonebook - Then the response is an error indicating that a contact with the same name already exists + Scenario: 03 - A contact with the same name as a listed Contact + Given the listed contact + | id | name | phone | + | 42 | Peter Parker | +1 (555) 748432 | + When adding to the phonebook the contact + | id | name | phone | + | 1 | Peter Parker | +1 (555) 432748 | + Then the response contains the new contact "Peter Parker" with phone "+1 (555) 432748" - @ignore - Scenario: 04 - An already listed contact + @error + Scenario: 04 - A contact with the same id as a listed Contact Given the listed contact - | name | phone | - | Peter Parker | +1 (555) 748432 | - When the contact is added to the phonebook - | name | phone | - | Peter Parker | +1 (555) 432748 | - Then the phonebook contains the contact with name "Peter Parker" - And the contact with name "Peter Parker" has phone number "+1 (555) 432748" - And the response contains the contact with name "Peter Parker" + | id | name | phone | + | 42 | Peter Parker | +1 (555) 748432 | + When adding to the phonebook the contact + | id | name | phone | + | 42 | Peter Parker | +1 (555) 432748 | + Then the response is an error indicating that the contact already exists - Scenario: 05 - A new contact - Given the contact with name "Peter Parker" is not listed in the phonebook - When the contact is added to the phonebook - | name | phone | - | Peter Parker | | - Then the phonebook contains the contact with name "Peter Parker" - And the contact with name "Peter Parker" has no phone number - And the response contains the contact with name "Peter Parker" + @error + Scenario: 05 - A contact without an empty name is added + Given the phonebook is empty + When adding to the phonebook the contact + | id | name | phone | + | 42 | [blank] | +1 (555) 432748 | + Then the response is an error indicating that the contact details are invalid + @error + Scenario: 06 - A contact without a name is added + Given the phonebook is empty + When adding to the phonebook the contact + | id | name | phone | + | 42 | | +1 (555) 432748 | + Then the response is an error indicating that the contact details are invalid diff --git a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/delete_a_contact.feature b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/delete_a_contact.feature index 79fcb2d..4cc4dbc 100644 --- a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/delete_a_contact.feature +++ b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/delete_a_contact.feature @@ -3,12 +3,30 @@ Feature: Delete a Contact from the phonebook This feature-file specifies the scenarios related to deleting a single contact from the Arc-E-Tect Phonebook. Scenario: 01 - A listed contact - Given the contact with name "John Smith" is listed in the phonebook - When the contact with name "John Smith" is deleted - Then the phonebook does not contain the contact with name "John Smith" + Given the listed contact + | id | name | | phone | + | 1 | Peter Parker | | +1 (555) 748432 | + When the contact with name "Peter Parker" is deleted + Then the phonebook does not contain a contact with name "Peter Parker" Scenario: 02 - An unlisted contact - Given the contact with name "John Doe" is not listed in the phonebook - When the contact with name "John Doe" is deleted - Then the phonebook does not contain the contact with name "John Doe" + Given the phonebook does not contain a contact with name "Peter Parker" + When the contact with name "Peter Parker" is deleted + Then the phonebook does not contain a contact with name "Peter Parker" + + Scenario: 03 - Multiple listed contacts with the same name + Given the listed contacts + | id | name | | phone | + | 1 | Peter Parker | | +1 (555) 748432 | + | 2 | Peter Parker | | +1 (555) 234947 | + When the contact with name "Peter Parker" is deleted + Then the phonebook does not contain a contact with name "Peter Parker" + + Scenario: 04 - Multiple listed contacts with the same name + Given the listed contacts + | id | name | | phone | + | 1 | Peter Parker | | +1 (555) 748432 | + | 2 | Peter Parker | | +1 (555) 234947 | + When the contact with id 2 is deleted + Then the phonebook does not contain a contact with id 2 diff --git a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_a_specific_contact.feature b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_a_specific_contact.feature index e60033c..fc7e0ed 100644 --- a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_a_specific_contact.feature +++ b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_a_specific_contact.feature @@ -3,18 +3,43 @@ Feature: Get a single contact from the phonebook This feature-file specifies the scenarios related to retrieving a single contact from the Arc-E-Tect Phonebook. Scenario: 01 - A contact that is listed - Given the contact with name "John Smith" is listed in the phonebook - When the contact with name "John Smith" is requested - Then the response contains the contact with name "John Smith" + Given the listed contact + | id | name | phone | + | 1 | Peter Parker | +1 (555) 748432 | + When the contact with name "Peter Parker" is requested + Then the response contains a contact "Peter Parker" with phone "+1 (555) 748432" - Scenario: 02 - A contact from an empty phonebook + Scenario: 02 - A contact from an empty Phonebook Given the phonebook is empty When the contact with name "Peter Parker" is requested - Then the contact cannot be found - And the response contains no contact - - Scenario: 03 - A contact that is not listed - Given the contact with name "John Doe" is not listed in the phonebook - When the contact with name "John Doe" is requested - Then the contact cannot be found - And the response contains no contact + Then 0 contacts are retrieved + + Scenario: 03 - Multiple Contacts with the same name + Given the listed contacts + | id | name | phone | + | 1 | Peter Parker | +1 (555) 748432 | + | 2 | Peter Parker | +1 (555) 234847 | + | 3 | Charly Brown | | + When the contact with name "Peter Parker" is requested + Then the response contains a contact "Peter Parker" with phone "+1 (555) 748432" + And the response contains a contact "Peter Parker" with phone "+1 (555) 234847" + + Scenario: 04 - A contact that is listed + Given the listed contact + | id | name | phone | + | 1 | Peter Parker | +1 (555) 748432 | + When the contact with id 1 is requested + Then the response contains a single contact with id 1 + + @error + Scenario: 05 - A contact from an empty phonebook + Given the phonebook is empty + When the contact with id 1 is requested + Then the response is an error indicating that the contact could not be found + + @error + Scenario: 06 - A contact that is not listed + Given the contact with id 666 is not listed in the phonebook + When the contact with id 666 is requested + Then the response is an error indicating that the contact could not be found + diff --git a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_all_contacts.feature b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_all_contacts.feature index 952aa1f..726c624 100644 --- a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_all_contacts.feature +++ b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/retrieve_all_contacts.feature @@ -3,16 +3,15 @@ Feature: Get all contacts available in the phonebook Scenario: 01 - From an empty phonebook Given the phonebook is empty When all contacts are requested - Then the response contains no contacts + Then 0 contacts are retrieved - Scenario: 02 - From a phonebook with a single contact in it - Given the contact with name "John Smith" is listed in the phonebook + Scenario: 02 - From a phonebook with several contacts with the same name in it + Given the listed contacts + | id | name | phone | + | 1 | Peter Parker | +1 (555) 748432 | + | 2 | Peter Parker | +1 (555) 234947 | When all contacts are requested - Then the response contains the contact "John Smith" + Then 2 contacts are retrieved + And the response contains a contact "Peter Parker" with phone "+1 (555) 748432" + And the response contains a contact "Peter Parker" with phone "+1 (555) 234947" - Scenario: 03 - From a phonebook with several contacts in it - Given the contact with name "John Smith" is listed in the phonebook - And the contact with name "Jane Brown" is listed in the phonebook - When all contacts are requested - Then the response contains the contact "John Smith" - And the response contains the contact "Jane Brown" diff --git a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/update_a_contact.feature b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/update_a_contact.feature index 55ec3f9..473590e 100644 --- a/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/update_a_contact.feature +++ b/src/bdd-test/resources/com/arc_e_tect/blog/phonebook/web/contacts/update_a_contact.feature @@ -4,34 +4,26 @@ Feature: Update a contact in the phonebook Scenario: 01 - Update the contact's phone number Given the listed contact - | name | phone | - | John Smith | +1 (555) 748432 | - When the phone number of contact "John Smith" is changed to "+1 (555) 432748" - Then the contact with name "John Smith" has phone number "+1 (555) 432748" + | id | name | phone | + | 1 | Peter Parker | +1 (555) 748432 | + When the phone number of contact 1 is changed to "+1 (555) 432748" + Then the response contains the contact "Peter Parker" with phone "+1 (555) 432748" - @ignore Scenario: 02 - Update the contact's name Given the listed contact - | name | phone | - | John Smith | +1 (555) 748432 | - When the name of contact "John Smith" is changed to "John Stark" - Then the contact with phone "+1 (555) 748432" has name "John Stark" - - Scenario: 02 - Update the contact's name - Given the listed contact - | name | phone | - | John Smith | +1 (555) 748432 | - When the name of contact "John Smith" is changed to "John Stark" - Then the contact formerly known as "John Smith" now has name "John Stark" + | id | name | phone | + | 1 | Peter Parker | +1 (555) 748432 | + When the name of contact 1 is changed to "John Stark" + Then the response contains the contact "John Stark" with phone "+1 (555) 748432" + @error Scenario: 03 - Update a Contact in an empty phonebook Given the phonebook is empty - When the phone number of contact "Peter Parker" is changed to "+1 (555) 432748" - Then the contact cannot be found - And the response contains no contact + When the phone number of contact 1 is changed to "+1 (555) 432748" + Then the response is an error indicating that the contact could not be found + @error Scenario: 04 - Update an unlisted Contact - Given the contact with name "John Doe" is not listed in the phonebook - When the phone number of contact "John Doe" is changed to "+1 (555) 432748" - Then the contact cannot be found - And the response contains no contact + Given the contact with id 1 is not listed in the phonebook + When the phone number of contact 1 is changed to "+1 (555) 432748" + Then the response is an error indicating that the contact could not be found diff --git a/src/docs/java/com/arc_e_tect/blog/phonebook/ApiContractValidator.java b/src/docs/java/com/arc_e_tect/blog/phonebook/ApiContractValidator.java index 6b94b0e..e4022ea 100644 --- a/src/docs/java/com/arc_e_tect/blog/phonebook/ApiContractValidator.java +++ b/src/docs/java/com/arc_e_tect/blog/phonebook/ApiContractValidator.java @@ -18,6 +18,7 @@ import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.hypermedia.LinksSnippet; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -33,8 +34,7 @@ import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -134,14 +134,14 @@ void getSingleContact() throws Exception { newContact.setId(42l); newContact.setPhone("+1 (555) 748432"); collection.insertOne(newContact); - this.mockMvc.perform(get("/contacts/{contact_name}", newContact.getName()) + this.mockMvc.perform(get("/contacts/{contact_id}", newContact.getId()) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.parseMediaType("application/hal+json"))) .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") - .description("Name of the Contact that is to be retrieved.")), + pathParameters(parameterWithName("contact_id") + .description("Unique id of the Contact that is to be retrieved.")), responseFields( fieldWithPath("id").description("Unique id for the Contact. The id is unique within the context of the Arc-E-Tect phonebook.").type(JsonFieldType.NUMBER), fieldWithPath("name").description("The name of the Contact. Typically this is the firstname and lastname combined.").type(JsonFieldType.STRING), @@ -157,14 +157,14 @@ void getSingleContact() throws Exception { @Test void getSingleContactEmptyPhonebook() throws Exception { collection.drop(); - this.mockMvc.perform(get("/contacts/{contact_name}", "John Doe") + this.mockMvc.perform(get("/contacts/{contact_id}", 666) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isNotFound()) .andExpect(content().contentTypeCompatibleWith(MediaType.parseMediaType("application/hal+json"))) .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") - .description("Identifier of the Contact that is to be retrieved.")), + pathParameters(parameterWithName("contact_id") + .description("Unique id of the Contact that is to be retrieved.")), responseFields( fieldWithPath("timestamp").description("The time the at which the error occurred. The timestamp is the server time.").type(JsonFieldType.STRING), fieldWithPath("status").description("The http status related to the error.").type(JsonFieldType.STRING), @@ -177,14 +177,14 @@ void getSingleContactEmptyPhonebook() throws Exception { @Test void getUnlistedContact() throws Exception { collection.drop(); - this.mockMvc.perform(get("/contacts/{contact_name}", "John Doe") + this.mockMvc.perform(get("/contacts/{contact_id}", 666) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isNotFound()) .andExpect(content().contentTypeCompatibleWith(MediaType.parseMediaType("application/hal+json"))) - .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", + .andDo(MockMvcRestDocumentation.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") - .description("Identifier of the Contact that is to be retrieved.")), + pathParameters(parameterWithName("contact_id") + .description("Unique id of the Contact that is to be retrieved.")), responseFields( fieldWithPath("timestamp").description("The time the at which the error occurred. The timestamp is the server time.").type(JsonFieldType.STRING), fieldWithPath("status").description("The http status related to the error.").type(JsonFieldType.STRING), @@ -198,7 +198,10 @@ void getUnlistedContact() throws Exception { void postNewContact() throws Exception { collection.drop(); - String jsonDoc = "{\"name\":\"Peter Parker\",\"phone\":\"+1 (555) 748432\"}"; + String jsonDoc = + """ + {"name":"Peter Parker", "phone":"+1 (555) 748432"} + """; this.mockMvc.perform(post("/contacts") .content(jsonDoc) @@ -226,7 +229,11 @@ void postListedContact() throws Exception { newContact.setPhone("+1 (555) 748432"); collection.insertOne(newContact); - String jsonDoc = "{\"name\":\"Peter Parker\",\"phone\":\"+1 (555) 748432\"}"; + String jsonDoc = + """ + {"id":42, "name":"Peter Parker", "phone":"+1 (555) 748432"} + """; + this.mockMvc.perform(post("/contacts") .content(jsonDoc) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) @@ -251,16 +258,21 @@ void patchListedContactNameAndPhone() throws Exception { newContact.setPhone("+1 (555) 748432"); collection.insertOne(newContact); - String jsonDoc = "{\"name\":\"John Stark\",\"phone\":\"+1 (555) 432748\"}"; - this.mockMvc.perform(patch("/contacts/{contact_name}", "Peter Parker") + String jsonDoc = + """ + {"id":42, "name":"John Stark", "phone":"+1 (555) 748432"} + """; + + + this.mockMvc.perform(patch("/contacts/{contact_id}", newContact.getId()) .content(jsonDoc) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.parseMediaType("application/hal+json"))) .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") - .description("Name of the Contact that is to be patched.")), + pathParameters(parameterWithName("contact_id") + .description("Unique id of the Contact that is to be patched.")), responseFields( fieldWithPath("id").description("Unique id for the Contact. The id is unique within the context of the Arc-E-Tect phonebook.").type(JsonFieldType.NUMBER), fieldWithPath("name").description("The name of the Contact. Typically this is the firstname and lastname combined.").type(JsonFieldType.STRING), @@ -281,15 +293,15 @@ void patchListedContactName() throws Exception { collection.insertOne(newContact); String jsonDoc = "{\"name\":\"John Stark\"}"; - this.mockMvc.perform(patch("/contacts/{contact_name}", "Peter Parker") + this.mockMvc.perform(patch("/contacts/{contact_id}", newContact.getId()) .content(jsonDoc) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.parseMediaType("application/hal+json"))) .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") - .description("Name of the Contact that is to be patched.")), + pathParameters(parameterWithName("contact_id") + .description("Unique id of the Contact that is to be patched.")), responseFields( fieldWithPath("id").description("Unique id for the Contact. The id is unique within the context of the Arc-E-Tect phonebook.").type(JsonFieldType.NUMBER), fieldWithPath("name").description("The name of the Contact. Typically this is the firstname and lastname combined.").type(JsonFieldType.STRING), @@ -310,14 +322,14 @@ void patchListedContactPhone() throws Exception { collection.insertOne(newContact); String jsonDoc = "{\"phone\":\"+1 (555) 432748\"}"; - this.mockMvc.perform(patch("/contacts/{contact_name}", "Peter Parker") + this.mockMvc.perform(patch("/contacts/{contact_id}", newContact.getId()) .content(jsonDoc) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.parseMediaType("application/hal+json"))) .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") + pathParameters(parameterWithName("contact_id") .description("Name of the Contact that is to be patched.")), responseFields( fieldWithPath("id").description("Unique id for the Contact. The id is unique within the context of the Arc-E-Tect phonebook.").type(JsonFieldType.NUMBER), @@ -337,25 +349,42 @@ void deleteListedContact() throws Exception { newContact.setId(42l); newContact.setPhone("+1 (555) 748432"); collection.insertOne(newContact); - this.mockMvc.perform(delete("/contacts/{contact_name}", newContact.getName()) + this.mockMvc.perform(delete("/contacts/{contact_id}", newContact.getId()) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isNoContent()) .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") - .description("Name of the Contact that is to be patched.")) + pathParameters(parameterWithName("contact_id") + .description("Unique id of the Contact that is to be deleted.")) + )); + } + + @Test + void deleteListedContactByName() throws Exception { + Contact newContact = new Contact(); + newContact.setName("Peter Parker"); + newContact.setId(42l); + newContact.setPhone("+1 (555) 748432"); + collection.insertOne(newContact); + this.mockMvc.perform(delete("/contacts?contactName={contact_name}", newContact.getName()) + .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) + .andExpect(status().isNoContent()) + .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", + preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), + queryParameters(parameterWithName("contactName") + .description("Name of the Contact that is to be deleted.")) )); } @Test void deleteUnlistedContact() throws Exception { - this.mockMvc.perform(delete("/contacts/{contact_name}", "John Doe") + this.mockMvc.perform(delete("/contacts/{contact_id}", 666) .contentType(MediaType.parseMediaType("application/hal+json"))).andDo(print()) .andExpect(status().isNoContent()) .andDo(MockMvcRestDocumentationWrapper.document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), - pathParameters(parameterWithName("contact_name") - .description("Name of the Contact that is to be deleted.")) + pathParameters(parameterWithName("contact_id") + .description("Unique id of the Contact that is to be deleted.")) )); } } diff --git a/src/main/java/com/arc_e_tect/blog/phonebook/repository/ContactRepository.java b/src/main/java/com/arc_e_tect/blog/phonebook/repository/ContactRepository.java index 07c2c21..39dc0cf 100644 --- a/src/main/java/com/arc_e_tect/blog/phonebook/repository/ContactRepository.java +++ b/src/main/java/com/arc_e_tect/blog/phonebook/repository/ContactRepository.java @@ -4,8 +4,9 @@ import lombok.NonNull; import org.springframework.data.mongodb.repository.MongoRepository; +import java.util.List; import java.util.Optional; public interface ContactRepository extends MongoRepository { - Optional findByName(@NonNull String name); + Optional> findByName(@NonNull String name); } diff --git a/src/main/java/com/arc_e_tect/blog/phonebook/service/ContactService.java b/src/main/java/com/arc_e_tect/blog/phonebook/service/ContactService.java index 5b8c198..089be8c 100644 --- a/src/main/java/com/arc_e_tect/blog/phonebook/service/ContactService.java +++ b/src/main/java/com/arc_e_tect/blog/phonebook/service/ContactService.java @@ -9,11 +9,14 @@ public interface ContactService { Contact saveContact(Contact contact); - Contact getContactByName(String name) throws ContactNotFoundException; + Contact getContactById(Long id) throws ContactNotFoundException; List retrieveAllContacts(); + List retrieveAllContacts(String contactName); - Contact updateContactByName(String name, Contact patch); + Contact updateContact(Long id, Contact patch); + + void deleteContact(Long id); void deleteContactByName(String name); } diff --git a/src/main/java/com/arc_e_tect/blog/phonebook/service/impl/ContactServiceImpl.java b/src/main/java/com/arc_e_tect/blog/phonebook/service/impl/ContactServiceImpl.java index bdb630c..9ae67ff 100644 --- a/src/main/java/com/arc_e_tect/blog/phonebook/service/impl/ContactServiceImpl.java +++ b/src/main/java/com/arc_e_tect/blog/phonebook/service/impl/ContactServiceImpl.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -25,10 +26,20 @@ public Contact saveContact(Contact contact) { } @Override - public Contact getContactByName(String name) throws ContactNotFoundException { - Optional result = repo.findByName(name); + public Contact getContactById(Long id) throws ContactNotFoundException { + Optional result = repo.findById(id); if (result.isEmpty()) { - throw new ContactNotFoundException(name); + throw new ContactNotFoundException(id); + } + + return result.get(); + } + + @Override + public List retrieveAllContacts(String name) throws ContactNotFoundException { + Optional> result = repo.findByName(name); + if (result.get().isEmpty()) { + return new ArrayList<>(); } return result.get(); @@ -39,20 +50,28 @@ public List retrieveAllContacts(){ return repo.findAll(); } + @Override + public void deleteContact(Long id) { + repo.deleteById(id); + } + @Override public void deleteContactByName(String name) { - Optional result = repo.findByName(name); + Optional> result = repo.findByName(name); + if (result.isPresent()) { - Contact deletable = result.get(); - repo.deleteById(deletable.getId()); + List deletables = result.get(); + for(Contact deletable : deletables) { + repo.deleteById(deletable.getId()); + } } } @Override - public Contact updateContactByName(String name, Contact patch) { - Optional result = repo.findByName(name); + public Contact updateContact(Long id, Contact patch) { + Optional result = repo.findById(id); if (result.isEmpty()) { - throw new ContactNotFoundException(name); + throw new ContactNotFoundException(id); } Contact contact = result.get(); diff --git a/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsController.java b/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsController.java index dbce8ec..a90bf8f 100644 --- a/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsController.java +++ b/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsController.java @@ -32,8 +32,14 @@ public class ContactsController { } @GetMapping(produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) - public CollectionModel getAllContacts(HttpServletResponse response) { - List contactList = contactService.retrieveAllContacts(); + public CollectionModel getAllContacts(HttpServletResponse response, + @RequestParam(required = false) String contactName) { + List contactList = null; + if (contactName == null || "".equals(contactName)) { + contactList = contactService.retrieveAllContacts(); + } else { + contactList = contactService.retrieveAllContacts(contactName); + } if (contactList.size() == 0) { response.setStatus(204); @@ -45,9 +51,9 @@ public CollectionModel getAllContacts(HttpServletResponse respo return result; } - @GetMapping(value="/{name}", produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getSingleContactByName(@PathVariable String name, HttpServletResponse response) { - Contact contact = contactService.getContactByName(name); + @GetMapping(value="/{id}", produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getSingleContactById(@PathVariable Long id, HttpServletResponse response) { + Contact contact = contactService.getContactById(id); ContactResource result = resourceAssembler.toModel(contact); @@ -62,7 +68,7 @@ public ContactResource postContact(@RequestBody ContactResource newResource, Htt } try { - contactService.getContactByName(newResource.getName()); + contactService.getContactById(newResource.getId()); response.setStatus(HttpStatus.CONFLICT.value()); throw new DuplicateContactException(newResource.getName()); } catch (ContactNotFoundException cnfe) { @@ -78,16 +84,22 @@ public ContactResource postContact(@RequestBody ContactResource newResource, Htt return resourceAssembler.toModel(contact); } - @DeleteMapping(value = "/{name}", produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) - public void deleteContact(@PathVariable String name, HttpServletResponse response) { - contactService.deleteContactByName(name); + @DeleteMapping(value = "/{id}", produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) + public void deleteContact(@PathVariable Long id, HttpServletResponse response) { + contactService.deleteContact(id); + response.setStatus(HttpStatus.NO_CONTENT.value()); + } + + @DeleteMapping(produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) + public void deleteContactByName(@RequestParam String contactName, HttpServletResponse response) { + contactService.deleteContactByName(contactName); response.setStatus(HttpStatus.NO_CONTENT.value()); } - @PatchMapping(value = "/{name}", produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) - public ContactResource patchContact(@PathVariable String name, @RequestBody ContactResource newResource, HttpServletResponse response) { + @PatchMapping(value = "/{id}", produces = {"application/hal+json", MediaType.APPLICATION_JSON_VALUE}) + public ContactResource patchContact(@PathVariable Long id, @RequestBody ContactResource newResource, HttpServletResponse response) { Contact contact = new Contact(newResource.getId(), newResource.getName(), newResource.getPhone()); - contact = contactService.updateContactByName(name, contact); + contact = contactService.updateContact(id, contact); response.setStatus(HttpStatus.OK.value()); return resourceAssembler.toModel(contact); diff --git a/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsResourceAssembler.java b/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsResourceAssembler.java index 28ca86d..9622ce1 100644 --- a/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsResourceAssembler.java +++ b/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/ContactsResourceAssembler.java @@ -25,7 +25,7 @@ public ContactResource toModel(Contact contact) { BeanUtils.copyProperties(contact, result); - result.add(linkTo(methodOn(ContactsController.class).getAllContacts(null)).withRel(LINK_COLLECTION_CONTACTS)); + result.add(linkTo(methodOn(ContactsController.class).getAllContacts(null,null)).withRel(LINK_COLLECTION_CONTACTS)); return result; } @@ -34,7 +34,7 @@ public ContactResource toModel(Contact contact) { public CollectionModel toCollectionModel(Iterable contacts) { CollectionModel ContactResources = super.toCollectionModel(contacts); - ContactResources.add(linkTo(methodOn(ContactsController.class).getAllContacts(null)).withSelfRel()); + ContactResources.add(linkTo(methodOn(ContactsController.class).getAllContacts(null,null)).withSelfRel()); return ContactResources; } diff --git a/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/advisories/ContactAdvisoryHandler.java b/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/advisories/ContactAdvisoryHandler.java index 6c1706e..355cc17 100644 --- a/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/advisories/ContactAdvisoryHandler.java +++ b/src/main/java/com/arc_e_tect/blog/phonebook/web/contacts/advisories/ContactAdvisoryHandler.java @@ -25,7 +25,7 @@ public final ResponseEntity contactNotFoundHandler(ContactNotFound @ExceptionHandler(DuplicateContactException.class) @ResponseStatus(HttpStatus.CONFLICT) - public final ResponseEntity duplicateContactHandler(DuplicateContactException ex, WebRequest request) { + public final ResponseEntity duplicateNamespaceHandler(DuplicateContactException ex, WebRequest request) { ErrorDetails error = new ErrorDetails(new Date(),HttpStatus.CONFLICT, "Contact already exists", ex.getMessage()); return new ResponseEntity<>(error, HttpStatus.CONFLICT); } diff --git a/versions.properties b/versions.properties index 146a708..7d026e3 100644 --- a/versions.properties +++ b/versions.properties @@ -24,6 +24,7 @@ version.org.owasp..dependency-check-gradle=8.2.1 version.org.mongodb..mongodb-driver-sync=4.9.1 ## # available=4.10.0 ## # available=4.10.1 +## # available=4.10.2 version.org.jacoco..org.jacoco.agent=0.8.9 ## # available=0.8.10 @@ -33,6 +34,7 @@ version.net.masterthought..cucumber-reporting=5.7.5 version.junit=5.9.2 ### available=5.9.3 +### available=5.10.0 version.io.cucumber..cucumber-spring=7.11.2 ## # available=7.12.0 @@ -54,8 +56,10 @@ plugin.org.unbroken-dome.test-sets=4.0.0 plugin.org.springframework.boot=3.0.6 ## # available=3.0.7 ## # available=3.0.8 +## # available=3.0.9 ## # available=3.1.0 ## # available=3.1.1 +## # available=3.1.2 plugin.org.owasp.dependencycheck=8.2.1 ## # available=8.3.1 @@ -68,6 +72,8 @@ plugin.nebula.lint=18.0.3 ## # available=18.1.0 plugin.io.spring.dependency-management=1.1.0 +## # available=1.1.1 +## # available=1.1.2 plugin.io.freefair.lombok=8.0.1 ## # available=8.1.0