The API assignment is a single assignment that is broken into focus areas that relate 1:1 with the lecture topics. You have the choice to start with any one area and either advance or jump repeatedly between them as you complete the overall solution. However, you are likely going to want to start out with the modules area so that you have some concrete modules to begin your early work. It is always good to be able to perform a successful root level build of all targeted modules before you begin adding detailed dependencies, plugins, and Java code.

1. Overview

The API will include three main concepts. We are going to try to keep the business rules pretty simple at this point:

  1. House - an individual house that can be part of a HouseRental

    1. Houses can be created, modified, listed (queried), and deleted

  2. Renter - identification for a person that may be part of a HouseRental and is not specific to any one HouseRental

    1. Renters can be created, modified, listed (queried), and deleted

  3. HouseRental - identifies a specific House and Renter agreement for a period of time

    1. HouseRentals must be created with an existing house and renter

    2. HouseRentals must be created with a current or future, non-overlapping period of time for the house

    3. HouseRentals must be created with a renter with a minimum age (21) for the rental timeperiod

    4. HouseRental time span is modifiable as long as the house is available

    5. HouseRental can be deleted

1.1. Grading Emphasis

Grading emphasis is focused on the demonstration of satisfying the listed learning objectives and — except the scenarios listed at the end — not on quantity. Most required capability/testing is focused on demonstration of what you know how to do. You are free to implement as much of the business model as you wish, but treat completing the listed scenarios at the end of the assignment (within the guidance of the stated static requirements) as the minimal functionality required.

1.2. HouseRenter Support

You are given a complete implementation of House and Renter as examples and building blocks in order to complete the assignment. Your primary work will be in completing HouseRentals.

1.2.1. HouseRenter Service

The houserenters-support-api-svc module contains a full @RestController/Service/Repo thread for both Houses and Renters. The module contains two Auto-configuration definitions that will automatically activate and configure the two services within a dependent application.

assignment2 houserentals api modules
Figure 1. houserenters-support-api-svc Module

The following dependency can be added to your service solution to bring in the Houses and Renters service examples to build upon.

HouseRenter Service Dependency
<dependency>
    <groupId>info.ejava.assignments.api.houserentals</groupId>
    <artifactId>houserenters-support-api-svc</artifactId>
    <version>${ejava.version}</version>
</dependency>

1.2.2. HouseRenter Client

A client module is supplied that includes the DTOs and client to conveniently communicate with the APIs. Your HouseRental solution will inject the Houses and Renters service components for interaction but your API tests will use the House and Renter http-based APIs. For demonstration,

  • the HouseAPIClient is implemented using explicit RestTemplate calls

  • the RenterAPIClient is implemented using Spring 6 Http Interface

The implementation details are available for inspection, but encapsulated such that they work the same via their HousesAPI and RentersAPI.

assignment2 houserentals api House

The following dependency can be added to your solution to bring in the Houses and Renters client artifact examples to build upon.

HouseRenter Client Dependency
<dependency>
    <artifactId>houserenters-support-api-client</artifactId> (1)
    <version>${ejava.version}</version>
</dependency>
1 dependency on client will bring in both client and dto modules

1.2.3. HouseRenter Tests

You are also supplied a set of tests that are meant to assist in your early development of the end-to-end capability. You are still encouraged to write your own tests and required to do so in specific sections and for the required scenarios.

You may find it productive and helpful to address the supplied tests first and implement the scenario tests at the end. It is up to you. At any time you can break off and write special-purpose tests for your own purpose.

The supplied tests are made available to you using the following Maven dependency.

HouseRenter Tests dependency
<dependency>
    <groupId>info.ejava.assignments.api.houserentals</groupId>
    <artifactId>houserenters-support-api-svc</artifactId>
    <version>${ejava.version}</version>
    <classifier>tests</classifier> (1)
    <scope>test</scope>
</dependency>
1 tests have been packaged within a separate -tests.java

The provided tests require that:

  • your HouseRental DTO class implement a RentalDTO "marker" interface provided by the support module. This interface has nothing defined and is only used to identify your DTO during the tests.

  • implement a ApiTestHelper<T extends RentalDTO> interface and make it a component available to be injected into the provided tests. A full skeleton of this class implementation has been supplied in the starter.

  • supply a @SpringBootTest class that pulls in the HouseRentalsApiNTest test case as a base class from the support module. This test case evaluates your solution during several core steps of the assignment. Much of the skeletal boilerplate for this work is provided in the starter.

assignment2 houserentals tests

Enable the tests whenever you are ready to use them. This can be immediately or at the end.

Groups of Tests can be Disabled Early In Development

There are empty @Nested classes provided in your starter’s MyHouseRentalsApiNTest that hide a group of tests in the parent while you focus on another group. Comment the @Nested declaration to enable a new group of tests.

//remove this class declaration to enable query tests
@Nested public class can_query_to { }
The tests are written to execute from the subclass in your area. With adhoc navigation, sometimes the IDE can get lost — lose the context of the subclass and provide errors as if there were only the base class. If that occurs — make a more direct IDE command to run the subclass to clear the issue.

2. Assignment 2a: Modules

2.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of establishing Maven modules for different portions of an application. You will:

  1. package your implementation along proper module boundaries

2.2. Overview

In this portion of the assignment you will be establishing your source module(s) for development. Your new work should be spread between two modules:

  • a single client module for DTO and other API artifacts

  • a single application module where the Spring Boot executable JAR is built

Your client module should declare a dependency on the provided houserenters-support-api-client to be able to make use of any DTO or API constructs. Your service/App module should declare a dependency on houserenters-support-api-svc to be able to host the House and Renter services. You do not copy or clone these "support" modules. Create a Maven dependency on these and use them as delivered.

assignment2 houserentals svc pkging
Figure 2. Module Packaging

2.3. Requirements

  1. Create your overall project as two (or more) Maven modules under a single parent

    1. client module(s) should contain any dependencies required by a client of the Web API. This includes the DTOs, any helpers created to implement the API calls, and unit tests for the DTOs. This module produces a regular Java JAR. houserenters-support-api-client/dto has been supplied for you use as an example and be part of your client modules. Create a dependency on the client module for access to House and Renter client classes. Do not copy/clone the support modules.

    2. svc module to include your HouseRentals controller, service, and repository work. houserenters-support-api-svc has been supplied for you to both be part of your solution and to use as an example. Create a Maven dependency on this support module. Do not copy/clone it.

    3. app module that contains the @SpringBootApplication class will produce a Spring Boot Executable JAR to instantiate the implemented services.

      The app and svc modules can be the same module. In this dual role, it will contain your HouseRental service solution and also host the @SpringBootApplication.
      The Maven pom.xml in the assignment starter for the App builds both a standard library JAR and a separate executable JAR (bootexec) to make sure we retain the ability to offer the HouseRental service as a library to a downstream assignment. By following this approach, you can make this assignment immediately reusable in assignment 3.
    4. parent module that establishes a common groupId and version for the child modules and delegate build commands. This can be the same parent used for assignments 0 and 1. Only your app and client modules will be children of this parent.

  2. Define the svc module as a Web Application (dependency on spring-boot-starter-web).

  3. Add a @SpringBootApplication class to the app module (already provided in starter for initial demo).

  4. Once constructed, the modules should be able to

    1. build the project from the root level

    2. build regular Java JARs for use in downstream modules

    3. build a Spring Boot Executable JAR (bootexec) for the @SpringBootApplication module

    4. immediately be able to access the /api/houses and /api/renters resource API when the application is running — because of Auto-Configuration.

      Example Calls to Houses and Renters Resource API
      $ curl -X GET http://localhost:8080/api/houses
      {"contents":[]}
      $ curl -X GET http://localhost:8080/api/renters
      {"contents":[]}

2.4. Grading

Your solution will be evaluated on:

  1. package your implementation along proper module boundaries

    1. whether you have divided your solution into separate module boundaries

    2. whether you have created appropriate dependencies between modules

    3. whether your project builds from the root level module

    4. whether you have successfully activated the House and Renter API

2.5. Additional Details

  1. Pick a Maven hierarchical groupId for your modules that is unique to your overall work on this assignment.

3. Assignment 2b: Content

3.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of designing a Data Transfer Object that is to be marshalled/unmarshalled using various internet content standards. You will:

  1. design a set of Data Transfer Objects (DTOs) to render information from and to the service

  2. define a Java class content type mappings to customize marshalling/unmarshalling

  3. specify content types consumed and produced by a controller

  4. specify content types accepted by a client

3.2. Overview

In this portion of the assignment you will be implementing a set of DTO classes that will be used to represent a HouseRental. All information expressed in the HouseRental will be derived from the House and Renter objects — except for the rental ID and any milestone properties unique to the HouseRental.

Lecture/Assignment Module Ordering
It is helpful to have a data model in place before writing your services. However, the lectures are structured with a content-less (String) domain up front — focusing on the Web API and services before tackling content. If you are starting this portion of the assignment before we have covered the details of content, it is suggested that you simply fill in the DTO properties required to pass the test_is_ready criteria. Skip the Content-Type aspects of this section until we have covered the Web content lecture.
assignment2 houserentals svc content
Figure 3. Content
HouseRental.id Avoids Compound Primary Key
The HouseRentalDTO id was added to keep from having to use a compound (houseId + renterId) primary key. This makes it an easier 1:1 example with House and Renter to follow.
String Primary Keys
Strings were used for the primary key type. This will make it much easier and more portable when we use database repositories in a later assignment.

The provided houserenters-support-api-dto module has the House and Renter DTO classes.

  • HouseDTO - provides information specific to the house

  • RenterDTO - provides information specific to the renter

  • StreetAddress - provides properties specific to a location for the House

  • MessageDTO - commonly used to provide error message information for request failures

  • <Type>ListDTO - used used to conveniently express typed collection of objects

  • SearchParams - a helper class to pass several HouseRental properties as a single unit versus numerous paramters.

  • TimePeriod - a helper class used to express a start/end period of dates, perform comparisons, and other period utility functions.

MessageDTO is from ejava-dto-util Class Examples
A MessageDTO is supplied in the ejava-dto-util package and used in most of the class API examples. You are free to create your own for use with the HouseRentals portion of the assignment.

3.3. Requirements

  1. Create a DTO class to represent HouseRental. Use the attributes in the diagram above and descriptions below as candidate properties for each class.

    1. The following attributes can be expressed both client and server-side

      1. houseId - required and an external reference to House

      2. renterId - required and an external reference to Renter

      3. startDate - required can be before or equal to endDate

      4. endDate - required and must be after or equal to startDate

        TimeSpan Class
        I have provided a TimePeriod utility class that can be used as an optional encapsulation of startDate and endDate methods. It can be used to help enforce consistency between start/endDate and for time span overlap comparisons.
    2. The following attributes are only assigned server-side.

      "Assigned server-side" means that at no time should your client supply this information to complete a HouseRental. The service will look up the information server-side and produce this information from the client-side properties listed above. A client should leave these null and the service should ignore input when working with an object with these properties.
      1. id is a unique ID for a HouseRental

      2. houseName copied from the house.name

      3. renterName should be the concatenation of Renter.firstName and Renter.lastName such that an evaluation of the string will contain the original first and lastName string values

      4. renterAge should be a calculation of years, rounded down, between the Renter.dob and the date the HouseRental starts.

      5. amount total cost of the Rental

    3. streetAddress should be a deep copy of the House.location

  2. Create a HouseRentalListDTO class to provide a typed collection of HouseRentalDTO.

    I am recommending you name the collection within the class a generic content for later reuse reasons.
  3. Map each DTO class to:

    1. Jackson JSON (the only required form)

    2. mapping to Jackson XML is optional

  4. Create a unit test to verify your new DTO type(s) can be marshalled/unmarshalled to/from the targeted serialization type.

  5. API TODO: Annotate controller methods to consume and produce supported content type(s) when they are implemented.

  6. API TODO: Update clients used in unit tests to explicitly only accept supported content type(s) when they are implemented.

3.4. Grading

Your solution will be evaluated on:

  1. design a set of Data Transfer Objects (DTOs) to render information from and to the service

    1. whether DTO class(es) represent the data requirements of the assignment

  2. define a Java class content type mappings to customize marshalling/unmarshalling

    1. whether unit test(s) successfully demonstrate the ability to marshall and unmarshal to/from a content format

  3. API TODO: specify content types consumed and produced by a controller

    1. whether controller methods are explicitly annotated with consumes and produces definitions for supported content type(s)

  4. API TODO: specify content types accepted by a client

    1. whether the clients in the unit integration tests have been configured to explicitly supply and accept supported content type(s).

3.5. Additional Details

  1. This portion of the assignment alone primarily produces a set of information classes that make up the primary vocabulary of your API and service classes.

  2. Use of lombok is highly encouraged here and can tremendously reduce the amount of code you write for these classes

  3. The Java Period class can easily calculate age in years between two LocalDates. A TimePeriod class has been provided in the DTO package to aggregate startDate and endDate LocalDates together and to provide convenience methods related to those values.

  4. The houserenters-support-api-client module also provides a HouseDTOFactory, RenterDTOFactory, and StreetAddressDTOFactory that makes it easy for tests and other demonstration code to quickly assembly example instances. You are encouraged to follow that pattern.

  5. The houserenters-support-api-client test cases for House and Renter demonstrate marshalling and unmarshalling DTO classes within a JUnit test. You should create a similar test of your HouseRenterDTO class to satisfy the testing requirement. Note that those tests leverage a JsonUtil class that is part of the class utility examples and simplifies example use of the Jackson JSON parser.

  6. The houserenters-support-api-client and supplied starter unit tests make use of JUnit @ParameterizedTest — which allows a single JUnit test method to be executed N times with variable parameters — pretty cool feature. Try it.

  7. Supporting multiple content types is harder than it initially looks — especially when trying to mix different libraries. WebClient does not currently support Jackson XML and will attempt to resort to using JAXB in the client. I provide an example of this later in the semester (Spring Data JPA End-to-End) and advise you to address the optional XML mapping last after all other requirements of the assignment are complete. If you do attempt to tackle both XML and WebClient together, know to use JacksonXML mappings for the server-side and JAXB mappings for the client-side.

4. Assignment 2c: Resources

4.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of designing a simple Web API. You will:

  1. identify resources

  2. define a URI for a resource

  3. define the proper method for a call against a resource

  4. identify appropriate response code family and value to use in certain circumstances

4.2. Overview

In this portion of the assignment you will be identifying a resource to implement the HouseRental API. Your results will be documented in a @RestController class. There is nothing to test here until the DTO and service classes are implemented.

assignment2 houserentals svc api resources
Figure 4. Identify Resources

The API will include three main concepts:

  1. Houses (provided) - an individual house that can be part of a house rental

    1. House information can be created, modified, listed, and deleted

    2. House information can be modified or deleted at any time but changes do not impact previous house rentals

  2. Renters (provided) - identification for a person that may participate in a house rental

    1. Renter information can be created, modified, listed, and deleted

    2. Renter information can be modified or deleted at any time but changes do not impact previous house rentals

  3. HouseRentals (your assignment) - a transaction for one House and one Renter for a unique period of time

    1. HouseRental information can be created, modified, listed, and deleted

    2. business rules will be applied for new and modified HouseRentals

4.3. Requirements

Capture the expression of the following requirements in a set of @RestController class(es) to represent your resources, URIs, required methods, and status codes.

  1. Identify your base resource(s) and sub-resource(s)

    1. create URIs to represent each resource and sub-resource

      Example Skeletal API Definitions
      public interface HousesAPI {
          String HOUSES_PATH="/api/houses";
          String HOUSE_PATH="/api/houses/{id}";
      ...
    2. create a separate @RestController class — at a minimum — for each base resource

      Example Skeletal Controller
      @RestController
      public class HousesController {
  2. Identify the @RestController methods required to represent the following actions for HouseRental. Assign them specific URIs and HTTP methods.

    1. create new HouseRental resource (a "contract")

      1. House and Renter must exist and identified using IDs

      2. provided time period must be current or future dates

      3. provided time period must not overlap with existing HouseRental for the same House for two different Renters (same Renter may have overlapping HouseRentals)

      4. Renter must be at least 21 on startDate of rental

    2. update an existing HouseRental resource

      1. modified time period must be current or future dates

      2. modified time period must not overlap with existing HouseRental for the same House for two different Renters

    3. get a specific HouseRental resource

    4. list HouseRental resources with paging

      1. accept optional houseId, renterId, and time period query parameters

      2. accept optional pageNumber, pageSize, and optional query parameters

      3. return HouseRentalListDTO containing contents of List<HouseRentalDTO>

    5. delete a specific resource

    6. delete all instances of the resource

      Example Skeletal Controller Method
          @RequestMapping(path=HousesAPI.HOUSE_PATH,
              method = RequestMethod.POST,
              consumes = {...},
              produces = {...})
          public ResponseEntity<HouseDTO> createHouse(@RequestBody HouseDTO newHouse) {
              throw new RuntimeException("not implemented");
              //or
              return ResponseEntity.status(HttpStatus.CREATED).body(...);
          }
  3. CLIENT TODO: Identify the response status codes to be returned for each of the actions

    1. account for success and failure conditions

    2. authorization does not need to be taken into account at this time

4.4. Grading

Your solution will be evaluated on:

  1. identify resources

    1. whether your identified resource(s) represent thing(s)

  2. define a URI for a resource

    1. whether the URI(s) center on the resource versus actions performed on the resource

  3. define the proper method for a call against a resource

    1. whether proper HTTP methods have been chosen to represent appropriate actions

  4. CLIENT TODO: identify appropriate response code family and value to use in certain circumstances

    1. whether proper response codes been identified for each action

4.5. Additional Details

  1. This portion of the assignment alone should produce a @RestController class with annotated methods that statically define your API interface (possibly missing content details). There is nothing to run or test in this portion alone.

  2. A simple and useful way of expressing your URIs can be through defining a set of public static attributes expressing the collection and individual instance of the resource type.

    Example Template Resource Declaration
    String (RESOURCE)S_PATH="(path)";
    String (RESOURCE)_PATH="(path)/{identifier(s)}";
  3. If you start with this portion, you may find it helpful to

    1. create sparsely populated DTO classes — HouseRentalDTO with just an id and HouseRentalListDTO — to represent the payloads that are accepted and returned from the methods

    2. have the controller simply throw a RuntimeException indicating that the method is not yet implemented. That would be a good excuse to also establish an exception advice to handle thrown exceptions.

  4. The details of the HouseRental will be performed server-side — based upon IDs and properties provided by the client and the House and Renter values found server-side. The client never provides more than an ID for an House or Renter and if it does — the server-side must ignore and rely on the server-side source for details.

  5. There is nothing to code up relative to response codes at this point. However:

    1. Finding zero resources to list is not a failure. It is a success with no resources in the collection.

    2. Not finding a specific resource is a failure and the status code returned should reflect that.

Instances of Action Verbs can be Resource Nouns

If an action does not map cleanly to a resource+HTTP method, consider thinking of the action (e.g., cancel) as one instance of an action (e.g., cancellation) that is a sub-resource of the subject (e.g., subjects/{subjectId}/cancellations). How might you think of the action if it took days to complete? (e.g. a sub-resource with POST/create(), GET/isComplete(), PUT/changePriority(), and DELETE/terminate())

5. Assignment 2d: Client/API Interactions

5.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of designing and implementing the interaction between a Web client and API. You will:

  1. implement a service method with Spring MVC synchronous annotated controller

  2. implement a client using RestTemplate, RestClient, or Spring Http Interface

  3. pass parameters between client and service over HTTP

  4. return HTTP response details from service

  5. access HTTP response details in client

5.2. Overview

In this portion of the assignment you will invoke your resource’s Web API from a client running within a JUnit test case.

assignment2 houserentals svc api initial
Figure 5. API/Service Interactions

There will be at least two primary tests in this portion of the assignment: handling success and handling failure. The failure will be either real or simulated through a temporary resource stub implementation.

assignment2 houserentals svc api client
Figure 6. Two Primary Tests

5.3. Requirements

  1. Implement stub behavior in the controller class as necessary to complete the example end-to-end calls.

    Example Stub Response
    HouseDTO newRental = HouseDTO.builder().id("1").build()
    URI location = ServletUriComponentsBuilder.fromCurrentRequestUri()
            .replacePath(HOUSERENTAL_PATH)
            .build("1");
    return ResponseEntity.created(location).body(newRental);
  2. Implement a unit integration test to demonstrate a success path

    1. use either a RestTemplate, RestClient, or Spring HTTP Interface API client class for this test.

    2. make at least one call that passes parameter(s) to the service and the results of the call depend on that passed parameter value

    3. access the return status and payload in the JUnit test/client

    4. evaluate the result based on the provided parameter(s) and expected success status

      Example Response Evaluation
      then(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
      then(response.getHeaders().getLocation()).isNotEmpty();
      then(houseResult.getId()).isNotBlank();
      then(houseResult).isEqualTo(houseRequestDTO.withId(houseResult.getId()));
Examples use RestTemplate
The House and Renter examples only use the RestTemplate and Spring HTTP Interface approaches.
One Success, One Failure, and Move On
Don’t put too much work into more than a single success and failure path test before completing more of the end-to-end. Your status and details will likely change.

5.4. Grading

Your solution will be evaluated on:

  1. implement a service method with Spring MVC synchronous annotated controller

    1. whether your solution implements the intended round-trip behavior for an HTTP API call to a service component

  2. implement a client using RestTemplate or RestClient

    1. whether you are able to perform an API call using either the RestTemplate, RestClient, or Spring HTTP Interface APIs

  3. pass parameters between client and service over HTTP

    1. whether you are able to successfully pass necessary parameters between the client and service

  4. return HTTP response details from service

    1. whether you are able to return service response details to the API client

  5. access HTTP response details in client

    1. whether you are able to access HTTP status and response payload

5.5. Additional Details

  1. The required end-to-end tests in the last section require many specific success and failure test scenarios. View this portion of the assignment as just an early draft work of those scenarios. If you have completed the end-to-end tests, you have completed this portion of the assignment.

  2. Your DTO class(es) have been placed in your Client module in a separate section of this assignment. You may want to add an optional API client class to that Client module — to encapsulate the details of the HTTP calls. The houserenters-support-client module contains example client API classes for Houses and Renters using RestTemplate and Spring HTTP Interface.

  3. Avoid placing extensive business logic into the stub portion of the assignment. The controller method details are part of a separate section of this assignment.

  4. This portion of the assignment alone should produce a simple, but significant demonstration of client/API communications (success and failure) using HTTP and service as the model for implementing additional resource actions.

  5. Inject the dependencies for the test from the Spring context. Anything that depends on the server’s port number must be delayed (@Lazy)

    //ProvidedApiHouseRenterTestConfiguration.java
    @Bean @Lazy (2)
    @ConditionalOnMissingBean(name="testServerConfig")
    public ServerConfig testServerConfig(ServerConfig serverConfig,
                                        @LocalServerPort int port) { (1)
        return serverConfig./*...*/.withPort(port).build();
    }
    
    @Bean @Lazy (3)
    public HousesAPI housesAPI(RestTemplate restTemplate, ServerConfig testServerConfig) {
        return new HousesAPIClient(restTemplate, testServerConfig, MediaType.APPLICATION_JSON);
    }
    
    //HouseRentalsAPINTest.java
    @SpringBootTest(...webEnvironment=...
    public class HouseRentalsAPINTest {
        @Autowired
        private HousesAPI houseAPI;
    1 server’s port# is not known until runtime
    2 cannot eagerly create @Bean until server port number available
    3 cannot eagerly create dependents of port number

6. Assignment 2e: Service/Controller Interface

6.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of separating the Web API facade details from the service implementation details and integrating the two. You will:

  1. implement a service class to encapsulate business logic

  2. turn @RestController class into a facade and delegate business logic details to an injected service class

  3. implement an error reporting strategy

6.2. Overview

In this portion of the assignment you will be implementing the core of the HouseRental components and integrating them as seamlessly as possible.

  • the controller will delegate commands to a service class to implement the business logic.

  • the service will use internal logic and external services to implement the details of the business logic.

  • the repository will provide storage for the service.

assignment2 houserentals svc xxx
Figure 7. Service/Controller Interface
Your Assignment is Primarily HouseRental and Integration
You have been provided complete implementation of the House and Renter services. You only have to implement the HouseRental components and integration that with House and Renter services.

A significant detail in this portion of the assignment is to design a way to convey success and failure when carrying out an API command. The controller should act only as a web facade. The service(s) will implement the details of the services and report the results.

assignment2 houserentals svc iface
Figure 8. Service/Controller Interface

Under the hood of the HouseRentalService is a repository and external clients.

  • You will create a HouseRentalService interface that uses HouseRentalDTO as its primary data type. This interface can be made reusable through the full semester of assignments.

This assignment will only work with DTO types (no entities/BOs) and a simulated/stub Repository.

  • You will create a repository interface and implementation that mimic the behavior of a CRUD and Pageable Repository of a future assignment.

  • You will inject and implement calls to the House and Renter services.

assignment2 houserentals svcrepo
Figure 9. Assignment Components

6.3. Requirements

  1. Implement a HouseRentalDTORepository interface and implementation component to simulate necessary behavior (e.g., save, findById, find) for the base HouseRentalDTO resource type. Don’t go overboard here. We just need some place to generate IDs and hold the data in memory.

    1. implement a Java interface (e.g., HouseRentalDTORepository).

      Try to make this interface conceptually consistent with the Spring Data ListCrudRepository and ListPagingAndSortingRepository (including the use of Pageable and Page) to avoid changes later on. This is just a tip and not a requirement — implement what you need for now. Start with just save().
    2. implement a component class stub (e.g., HouseRentalDTORepositoryStub) using simple, in-memory storage (e.g., HashMap or ConcurrentHashMap) and an ID generation mechanism (e.g., int or AtomicInteger)

    You are free to make use of the POJORepositoryMapImpl<T> class in the houserentals_support_api module as your implementation for the repository. It comes with a POJORepository<T> interface and the Renter repository and service provide an example of its use. Report any bugs you find.
  2. Implement a HouseRental service to implement actions and enforce business logic on the base resources

    1. implement a Java interface This will accept and return HouseRentalDTO types.

    2. implement a component class for the service.

    3. inject the dependencies required to implement the business logic

      1. (provided) HousesService - to verify existence of and obtain details of houses

      2. (provided) RentersService - to verify existence of and obtain details of renters

      3. (your assignment) HouseRentalDTORepository - to store details that are important to house rentals

        You are injecting the service implementations (not the HTTP API) for the House and Renter services into your HouseRental service. That means they will be part of your application and you will have a Java ⇒ Java interface with them.
    4. implement the business logic for the service

      1. a HouseRental can only be created for an existing House and Renter. It will be populated using the values of that House and Renter on the server-side.

        • houseId (mandatory input) — used to locate the details of the House on the server-side

        • renterId (mandatory input) — used to locate the details of the Renter on the server-side

        • startDate (mandatory input) — used to indicate when the Rental will begin. This must be in the future and before or equal to endDate.

        • endDate (mandatory input) — used to indicate when the Rental will end. This must be after or equal to startDate.

      2. HouseRental must populate the following fields from the House and Renter obtained server-side using the houseId and renterId.

        • from the server-side House

          • houseName — derived from the House’s name details obtained server-side

          • amount — calculated from House.dailyRate * days in time span

        • from the server-side Renter

          • renterName — derived from the Renter’s firstName and lastName details on the server-side

          • renterAge — calculated form the startDate and Renter dob details on the server-side. A Renter must be at least 21 on startDate to be valid. The rejection shall include the text "too young".

      3. startDate/endDate time span cannot overlap with another HouseRental for the same House for a different Renter. The rejection shall include the text "conflict".

      4. a successful request to create a HouseRental will be returned with

        • the above checks made, id assigned, and a 201/CREATED http status returned

        • properties (houseNamel, renterName renterAge, amount) filled in

      5. getHouseRental returns current state of the requested HouseRental

      6. implement a paged findHouseRentals that returns all matches. Use the Spring Data Pageable and Page (and PageImpl) classes to express pageNumber, pageSize, and page results (i.e., Page findHouseRentals(Pageable)). You do not need to implement sort.

      7. augment the findHouseRentals to optionally include a search for matching houseId, renterId, time span (startDate and endDate), or any combination.

        Implement Search Details within Repository class
        Delegate the gory details of searching through the data — to the repository class.
  3. Design a means for service calls to

    1. indicate success

    2. indicate failure to include internal or client error reason. Client error reasons must include separate issues "not found" and "bad request" at a minimum.

  4. Design a means for Controller calls to

    1. separate happy paths from error paths

    2. consolidate error handling

      It is advised that you leverage exceptions and exception advice for this.
  5. Integrate services into controller components

    1. complete and report successful results to API client

    2. report errors to API client, to include the status code and a textual message that is specific to the error that just occurred

  6. Implement a unit integration test to demonstrate at least one success and error path

    1. access the return status and payload in the client

    2. evaluate the result based on the provided parameter(s) and expected success/failure status

6.4. Grading

Your solution will be evaluated on:

  1. implement a service class to encapsulate business logic

    1. whether your service class performs the actions of the service and acts as the primary enforcer of stated business rules

  2. turn @RestController class into a facade and delegate business logic details to an injected service class

    1. whether your API tier of classes act as a thin adapter facade between the HTTP protocol and service component interactions

    2. whether you have separated happy paths from error paths and made all error handling/reporting consistent

  3. implement an error reporting strategy

    1. whether your design has identified how errors are reported by the service tier and below

    2. whether your API tier is able to translate errors into meaningful error responses to the client

6.5. Additional Details

  1. This portion of the assignment alone primarily provides an implementation pattern for how services will report successful and unsuccessful requests and how the API will turn that into a meaningful HTTP response that the client can access.

  2. The houserenters-support-api-svc module contains a set of example DTO Repository Stubs.

    • The Houses package shows an example of a fully exploded implementation. Take this approach if you wish to write all the code yourself.

    • The Renters package shows an example of how to use the templated POJORepository<T> interface and POJORepositoryMapImpl<T> implementation. Take this approach if you want to delegate to an existing implementation and only provide the custom query methods.

    POJORepositoryMapImpl<T> provides a protected findAll(Predicate<T> predicate, Pageable pageable) that returns a Page<T>. All you have to provide are the predicates for the custom query methods.
  3. You are required to use the Pageable and Page classes (from the org.springframework.data.domain Java package) for paging methods in your finder method(s) — to be forward compatible with later assignments that make use of Spring Data. You can find example use of Pageable and Page (and PageImpl) in House and Renter examples.

  4. It is highly recommended that exceptions be used between the service and controller layers to identify error scenarios and specific exceptions be used to help identify which kind of error is occurring in order to report accurate status to the client. Leave non-exception paths for successful results. The Houses and Renters example leverage the exceptions defined in the ejava-dto-util module. You are free to define your own.

  5. It is highly recommended that ExceptionHandlers and RestExceptionAdvice be used to handle exceptions thrown and report status. The Houses and Renters example leverage the ExceptionHandlers from the ejava-web-util module. You are free to define your own.

7. Assignment 2f: Required Test Scenarios

There are a minimum set of required scenarios that a complete project must specifically demonstrate in the submission.

  1. Creation of HouseRental

    1. success (201/CREATED)

    2. failed creation because House unknown (422/UNPROCESSABLE_ENTITY)

    3. failed creation because House unavailable for time span (422/UNPROCESSABLE_ENTITY)

    4. failed creation because Renter unknown (422/UNPROCESSABLE_ENTITY)

    5. failed creation because Renter too young (422/UNPROCESSABLE_ENTITY)

  2. Update of HouseRental

    1. success (200/OK)

    2. failed because HouseRental does not exist (404/NOT_FOUND)

    3. failed because HouseRental exists but change is invalid (422/UNPROCESSABLE_ENTITY)

7.1. Scenario: Creation of HouseRental

In this scenario, a HouseRental is attempted to be created.

7.1.1. Primary Path: Success

In this primary path, the House and Renter exist, all business rules are satisfied, and the API client is able to successfully create a HouseRental. The desired status in the response is a 201/CREATED. A follow-on query for HouseRentals will report the new entry.

assignment2 houserentals svc api scenario create
Figure 10. Creation of HouseRental for a House

7.1.2. Alternate Path: House unknown

In this alternate path, the House does not exist and the API client is unable to create a HouseRental. The desired response status is a 422/UNPROCESSABLE_ENTITY. The HouseRentals resource understood the request (i.e., not a 400/BAD_REQUEST or 404/NOT_FOUND), but request contained information that could not be processed.

assignment2 houserentals svc api scenario create fail item unknown
Figure 11. House unknown
getHouse() will return a 404/NOT_FOUND — which is not the same status requested here. HouseRentals will need to account for that difference.

7.1.3. Alternate Path: House Unavailable

In this alternate path, the House exists but is unavailable for the requested time span. The desired response status is a 422/UNPROCESSABLE_ENTITY. The HouseRentals resource understood the request, but request contained information that could not be processed.

assignment2 houserentals svc api scenario create fail item unavail
Figure 12. House unavailable

7.1.4. Alternate Path: Renter Unknown

In this alternate path, the Renter does not exist and the API client is unable to create a HouseRental for the Renter. The desired response status is a 422/UNPROCESSABLE_ENTITY.

assignment2 houserentals svc api scenario create fail ptp unknown
Figure 13. Renter unknown
getRenter() will return a 404/NOT_FOUND — which is not the same status requested here. HouseRentals will need to account for that difference.

7.1.5. Alternate Path: Renter Too Young

In this alternate path, the Renter exists but is too young to complete a HouseRental. The desired response status is a 422/UNPROCESSABLE_ENTITY.

assignment2 houserentals svc api scenario create fail ptp invalid
Figure 14. Renter too young
renterAge is calculated from houseRental startDate.

7.2. Scenario: Update of HouseRental

In this scenario, a HouseRental is attempted to be updated.

7.2.1. Primary Path: Success

In this primary path, the API client is able to successfully update the time span for a HouseRental. This update should be performed all on the server-side. The client primarily expresses an updated proposal and the business rules pass. A follow-on query for HouseRentals will report the updated entry.

assignment2 houserentals svc api scenario update
Figure 15. HouseRental update
Your evaluation must verify what was stored on the server-side reflects the modifications made.

7.2.2. Alternate Path: HouseRental does not exist

In this alternate path, the requested HouseRental does not exist. The expected response status code should be 404/NOT_FOUND to express that the target resource could not be found.

assignment2 houserentals svc api scenario update fail1
Figure 16. HouseRental does not exist

7.2.3. Alternate Path: Invalid Change

In this alternate path, the requested HouseRental exists but the existing House is not available for the new time span. The expected response status code should be 422/UNPROCESSABLE_ENTITY.

assignment2 houserentals svc api scenario update fail2
Figure 17. HouseRental invalid change
The scenario is showing that only the time span is being changed and the existing House and Renter should be used. The scenario is also showing that the existing House and Renter are being verified to still exist.

7.3. Requirements

  1. Implement the above scenarios within one or more integration unit tests.

  2. Name the tests such that they are picked up and executed by the Surefire test phase of the maven build.

  3. Turn in a cleaned source tree of the project under a single root parent project. The House and Renter modules do not need to be included.

  4. The source tree should be ready to build in an external area that has access to the ejava-nexus-snaphots repository.

7.4. Grading

  1. create an integration test that verifies a successful scenario

    1. whether you implemented a set of integration unit tests that verify the primary paths for HouseRentals

  2. create an integration test that verifies a failure scenario

    1. whether you implemented a set of integration unit tests that verify the failure paths for HouseRentals.

7.5. Additional Details

  1. Place behavior in the proper place

    1. The unit integration test is responsible for populating the Houses and Renters. It will supply HouseDTOs and RenterDTOs populated on the client-side — to the Houses and Renters APIs/services.

    2. The unit integration test will pass houseId, renterId, etc. values creating a rental (can be expressed using a sparsely populated HouseRentalDTO). All details to populate the returned HouseRentalDTOs (i.e., House and Renter info) will come from the server-side. There should never be a need for the client to self-create/fully-populate a HouseRentalDTO.