Browse Source

Updated README and cleaned up code

main
JonesWVU 4 years ago
parent
commit
768ae20468
  1. 14
      ContactsAPI.postman_collection.json
  2. 54
      README.md
  3. 6
      src/main/resources/application.properties
  4. 82
      src/test/java/com/singlestone/contacts/controller/ContactControllerTest.java
  5. 1
      src/test/java/com/singlestone/contacts/service/ContactServiceTest.java

14
ContactsAPI.postman_collection.json

@ -71,7 +71,7 @@
}
},
"url": {
"raw": "localhost:8080/api/v1/contacts/1",
"raw": "localhost:8080/api/v1/contacts/5",
"host": [
"localhost"
],
@ -80,9 +80,10 @@
"api",
"v1",
"contacts",
"1"
"5"
]
}
},
"description": "Modify the ID, in this case '5', at the end of the URL to the desired contact ID and modify fields in the message body as needed."
},
"response": []
},
@ -92,7 +93,7 @@
"method": "DELETE",
"header": [],
"url": {
"raw": "localhost:8080/api/v1/contacts/1",
"raw": "localhost:8080/api/v1/contacts/5",
"host": [
"localhost"
],
@ -101,9 +102,10 @@
"api",
"v1",
"contacts",
"1"
"5"
]
}
},
"description": "Modify the ID, in this case '5', at the end of the URL to the desired contact ID as needed."
},
"response": []
},

54
README.md

@ -1,2 +1,54 @@
# contacts-api
# Contacts REST API
## About
The aim of this exercise is to create a simple contact entry system. It consists of a REST API that will enable a client to perform
CRUD operations on the contact collection.
## Requirements
The API must have the following endpoints:
| Method | Endpoint | Description |
| ------ | -------- | ----------- |
| **GET** | **/contacts** | List all contacts |
| **POST** | **/contacts** | Create a new contact |
| **PUT** | **/contacts/{id}** | Update a contact |
| **GET** | **/contacts/{id}** | Get a specific contact |
| **DELETE** | **/contacts/{id}** | Delete a contact |
| **GET** | **/contacts/call-list** | List of all contacts ^1^ |
^1^ Restricted to contacts with a home phone sorted by last name then first name
## Software
The following software is required in order to build and run:
1. [**Maven**](https://maven.apache.org/download.cgi)
2. [**Java JDK 8**](https://openjdk.java.net/install/) or later
## Implementation
I chose to use the [**Spring Framework**](https://spring.io/) and more specifically, the [**Spring Boot**](https://spring.io/projects/spring-boot) and [**Spring Data JPA**](https://spring.io/projects/spring-data-jpa) projects as the base for this API.
**Spring Boot** makes it simple to create a stand-alone REST API and **Spring Data JPA** simplifies interfacing with an h2 database.
## Build and Run
To build and run, from the command line, navigate to the root directory and run the command:
```sh
mvn spring-boot:run
```
The URL to access the API is: [**http://localhost:8080/api/v1/contacts**](http://localhost:8080/api/v1/contacts)
## Running Tests
To run tests, from the command line, navigate to the root directory and run the command:
```sh
mvn test
```
## Additional Resources
To prepopulate the database with demo data, uncomment the following lines in the 'src/main/resources/application.properties' file
```sh
# spring.jpa.defer-datasource-initialization=true
# spring.sql.init.data-locations=classpath:demo.sql
```
An H2 console will be available at [**http://localhost:8080/h2-console**](http://localhost:8080/h2-console) to allow direct access to the underlying H2 database
username: sa /password: leave empty
Also included is the file 'ContactsAPI.postman_collection.json'. This can be imported into the [**Postman**](https://www.postman.com/downloads/) application in order to issue REST commands.

6
src/main/resources/application.properties

@ -1,18 +1,24 @@
# ---Comments without preceeding '---' are settings to enable for development
# ---Enable H2 console
spring.h2.console.enabled=true
# ---Turn Statistics on
spring.jpa.properties.hibernate.generate_statistics=true
logging.level.org.hibernate.stat=debug
logging.level.org.hibernate.type=debug
# ---Show queries
# spring.jpa.show-sql=true
# spring.jpa.properties.hibernate.format_sql=true
# ---In memory DB
spring.datasource.url=jdbc:h2:mem:contactsdb
spring.data.jpa.repositories.bootstrap-mode=default
# ---Uncomment the next properties to load demo data from file
# spring.jpa.defer-datasource-initialization=true
# spring.sql.init.data-locations=classpath:demo.sql
# ---For development only
# server.error.include-message=always

82
src/test/java/com/singlestone/contacts/controller/ContactControllerTest.java

@ -69,6 +69,14 @@ public class ContactControllerTest {
return testContactDTO;
}
private static String asJsonString(final Object obj){
try{
return new ObjectMapper().writeValueAsString(obj);
}catch (Exception e){
throw new RuntimeException(e);
}
}
@BeforeEach
public void setup(){
mockMvc = MockMvcBuilders.standaloneSetup(contactController).build();
@ -79,9 +87,9 @@ public class ContactControllerTest {
ContactDTO contactToCreate = createTestContactDTO();
when(contactService.createContact(any())).thenReturn(contactToCreate);
mockMvc.perform(post("/api/v1/contacts").
contentType(MediaType.APPLICATION_JSON).
content(asJsonString(contactToCreate))).
andExpect(status().isCreated());
contentType(MediaType.APPLICATION_JSON).
content(asJsonString(contactToCreate))).
andExpect(status().isCreated());
verify(contactService, times(1)).createContact(any());
}
@ -92,9 +100,9 @@ public class ContactControllerTest {
contacts.add(contactToCreate);
when(contactService.getAllContacts()).thenReturn(contacts);
mockMvc.perform(get("/api/v1/contacts").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isOk());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isOk());
verify(contactService, times(1)).getAllContacts();
}
@ -103,9 +111,9 @@ public class ContactControllerTest {
List<ContactDTO> contacts = new ArrayList<>();
when(contactService.getAllContacts()).thenReturn(contacts);
mockMvc.perform(get("/api/v1/contacts").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNoContent());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNoContent());
verify(contactService, times(1)).getAllContacts();
}
@ -114,9 +122,9 @@ public class ContactControllerTest {
ContactDTO contactToGet = createTestContactDTO();
when(contactService.getContact(anyLong())).thenReturn(contactToGet);
mockMvc.perform(get("/api/v1/contacts/1").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isOk());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isOk());
verify(contactService, times(1)).getContact(anyLong());
}
@ -124,9 +132,9 @@ public class ContactControllerTest {
void testGetContactDoesNotExist() throws Exception {
when(contactService.getContact(anyLong())).thenReturn(null);
mockMvc.perform(get("/api/v1/contacts/1").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNotFound());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNotFound());
verify(contactService, times(1)).getContact(anyLong());
}
@ -135,9 +143,9 @@ public class ContactControllerTest {
ContactDTO contactToUpdate = createTestContactDTO();
when(contactService.updateContact(anyLong(), any())).thenReturn(contactToUpdate);
mockMvc.perform(put("/api/v1/contacts/1").
contentType(MediaType.APPLICATION_JSON).
content(asJsonString(contactToUpdate))).
andExpect(status().isOk());
contentType(MediaType.APPLICATION_JSON).
content(asJsonString(contactToUpdate))).
andExpect(status().isOk());
verify(contactService, times(1)).updateContact(anyLong(), any());
}
@ -146,9 +154,9 @@ public class ContactControllerTest {
ContactDTO contact = createTestContactDTO();
when(contactService.updateContact(anyLong(), any())).thenReturn(null);
mockMvc.perform(put("/api/v1/contacts/1").
contentType(MediaType.APPLICATION_JSON).
content(asJsonString(contact))).
andExpect(status().isNotFound());
contentType(MediaType.APPLICATION_JSON).
content(asJsonString(contact))).
andExpect(status().isNotFound());
verify(contactService, times(1)).updateContact(anyLong(), any());
}
@ -157,9 +165,9 @@ public class ContactControllerTest {
ContactDTO contactToDelete = createTestContactDTO();
when(contactService.deleteContact(anyLong())).thenReturn(contactToDelete);
mockMvc.perform(delete("/api/v1/contacts/1").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNoContent());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNoContent());
verify(contactService, times(1)).deleteContact(anyLong());
}
@ -167,9 +175,9 @@ public class ContactControllerTest {
void testDeleteContactDoesNotExist() throws Exception {
when(contactService.deleteContact(anyLong())).thenReturn(null);
mockMvc.perform(delete("/api/v1/contacts/1").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNotFound());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNotFound());
verify(contactService, times(1)).deleteContact(anyLong());
}
@ -182,9 +190,9 @@ public class ContactControllerTest {
contacts.add(contactToreturn);
when(contactService.getCallList()).thenReturn(contacts);
mockMvc.perform(get("/api/v1/contacts/call-list").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isOk());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isOk());
verify(contactService, times(1)).getCallList();
}
@ -193,17 +201,9 @@ public class ContactControllerTest {
List<CallListDTO> contacts = new ArrayList<>();
when(contactService.getCallList()).thenReturn(contacts);
mockMvc.perform(get("/api/v1/contacts/call-list").
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNoContent());
accept(MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isNoContent());
verify(contactService, times(1)).getCallList();
}
public static String asJsonString(final Object obj){
try{
return new ObjectMapper().writeValueAsString(obj);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}

1
src/test/java/com/singlestone/contacts/service/ContactServiceTest.java

@ -100,6 +100,7 @@ public class ContactServiceTest {
void testDeleteContact() {
when(contactRepository.findById(anyLong())).thenReturn(Optional.of(createTestContact()));
contactService.deleteContact(1);
verify(contactRepository, times(1)).findById(anyLong());
verify(contactRepository, times(1)).deleteById(anyLong());
}

Loading…
Cancel
Save