Skip to content

Commit

Permalink
Merge pull request #5 from dsyer/push-url
Browse files Browse the repository at this point in the history
Remove unnecessary servlet response manipulations
  • Loading branch information
agrison authored Oct 22, 2023
2 parents 5cbecd5 + e874516 commit e51d734
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import io.github.wimdeblauwe.htmx.spring.boot.mvc.HtmxResponse;
import io.github.wimdeblauwe.htmx.spring.boot.mvc.HxRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;

/**
Expand Down Expand Up @@ -119,21 +118,22 @@ public String htmxInitFindForm() {
}

@GetMapping("/owners")
public String ownersList(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result, Model model,
HttpServletResponse response) {
return processFindForm(page, owner, result, model, response, "owners/findOwners", "owners/ownersList");
public String ownersList(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
Model model) {
return processFindForm(page, owner, result, model, "owners/findOwners", "owners/ownersList");
}

@HxRequest
@GetMapping("/owners")
public String htmxOwnersList(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
Model model, HttpServletResponse response) {
return processFindForm(page, owner, result, model, response, FRAGMENTS_OWNERS_FIND_FORM,
public HtmxResponse htmxOwnersList(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
Model model) {
String view = processFindForm(page, owner, result, model, FRAGMENTS_OWNERS_FIND_FORM,
"fragments/owners :: list");
return new HtmxResponse().addTemplate(view);
}

public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
Model model, HttpServletResponse response, String emptyView, String listView) {
Model model, String emptyView, String listView) {
// allow parameterless GET request for /owners to return all records
if (owner.getLastName() == null) {
owner.setLastName(""); // empty string signifies broadest possible search
Expand All @@ -154,18 +154,16 @@ public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner
}

// multiple owners found
return addPaginationModel(owner.getLastName(), page, model, ownersResults, response, listView);
return addPaginationModel(owner.getLastName(), page, model, ownersResults, listView);
}

private String addPaginationModel(String lastName, int page, Model model, Page<Owner> paginated,
HttpServletResponse response, String listView) {
private String addPaginationModel(String lastName, int page, Model model, Page<Owner> paginated, String listView) {
model.addAttribute("listOwners", paginated);
List<Owner> listOwners = paginated.getContent();
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", paginated.getTotalPages());
model.addAttribute("totalItems", paginated.getTotalElements());
model.addAttribute("listOwners", listOwners);
response.addHeader("HX-Push-Url", "/owners?lastName=" + lastName + "&page=" + page);
return listView;
}

Expand All @@ -182,9 +180,7 @@ public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model mo

@HxRequest
@GetMapping("/owners/{ownerId}/edit")
public String htmxInitUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model, HttpServletRequest request,
HttpServletResponse response) {
response.addHeader("HX-Push-Url", request.getServletPath());
public String htmxInitUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
return handleInitUpdateOwnerForm(ownerId, model, FRAGMENTS_OWNERS_EDIT);
}

Expand Down Expand Up @@ -229,8 +225,7 @@ public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {

@HxRequest
@GetMapping("/owners/{ownerId}")
public ModelAndView htmxShowOwner(@PathVariable("ownerId") int ownerId, HttpServletResponse response) {
response.addHeader("HX-Push-Url", "/owners/" + ownerId);
public ModelAndView htmxShowOwner(@PathVariable("ownerId") int ownerId) {
return handleShowOwner(ownerId, "fragments/owners :: details");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
import org.springframework.web.bind.annotation.RequestMapping;

import io.github.wimdeblauwe.htmx.spring.boot.mvc.HxRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;

/**
Expand Down Expand Up @@ -87,9 +85,7 @@ public String initCreationForm(Owner owner, ModelMap model) {

@HxRequest
@GetMapping("/pets/new")
public String htmxInitCreationForm(Owner owner, ModelMap model, HttpServletRequest request,
HttpServletResponse response) {
response.addHeader("HX-Push-Url", request.getServletPath());
public String htmxInitCreationForm(Owner owner, ModelMap model) {
return handleInitCreationForm(owner, model, FRAGMENTS_PETS_EDIT);
}

Expand Down Expand Up @@ -135,8 +131,7 @@ public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, Mode
@HxRequest
@GetMapping("/pets/{petId}/edit")
public String htmxInitUpdateForm(@PathVariable("ownerId") int ownerId, Owner owner,
@PathVariable("petId") int petId, ModelMap model, HttpServletResponse response) {
response.addHeader("HX-Push-Url", "/owners/" + ownerId + "/pets/" + petId + "/edit");
@PathVariable("petId") int petId, ModelMap model) {
return handleInitUpdateForm(owner, petId, model, FRAGMENTS_PETS_EDIT);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
import org.springframework.web.bind.annotation.PostMapping;

import io.github.wimdeblauwe.htmx.spring.boot.mvc.HxRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;

/**
Expand Down Expand Up @@ -85,8 +83,7 @@ public String initNewVisitForm() {

@HxRequest
@GetMapping("/owners/{ownerId}/pets/{petId}/visits/new")
public String htmxInitNewVisitForm(HttpServletRequest request, HttpServletResponse response) {
response.addHeader("HX-Push-Url", request.getServletPath());
public String htmxInitNewVisitForm() {
return FRAGMENTS_PETS_VISITS;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.springframework.web.bind.annotation.ResponseBody;

import io.github.wimdeblauwe.htmx.spring.boot.mvc.HxRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
* @author Juergen Hoeller
Expand All @@ -46,34 +45,31 @@ public VetController(VetRepository clinicService) {
}

@GetMapping("/vets.html")
public String showVetList(@RequestParam(defaultValue = "1") int page, Model model, HttpServletResponse response) {
return handleVetList(page, model, "vets/vetList", response);
public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) {
return handleVetList(page, model, "vets/vetList");
}

@HxRequest
@GetMapping("/vets.html")
public String htmxShowVetList(@RequestParam(defaultValue = "1") int page, Model model,
HttpServletResponse response) {
return handleVetList(page, model, "fragments/vets :: list", response);
public String htmxShowVetList(@RequestParam(defaultValue = "1") int page, Model model) {
return handleVetList(page, model, "fragments/vets :: list");
}

protected String handleVetList(int page, Model model, String view, HttpServletResponse response) {
protected String handleVetList(int page, Model model, String view) {
// Here we are returning an object of type 'Vets' rather than a collection of Vet
// objects so it is simpler for Object-Xml mapping
Vets vets = new Vets();
Page<Vet> paginated = findPaginated(page);
vets.getVetList().addAll(paginated.toList());
return addPaginationModel(page, paginated, model, view, response);
return addPaginationModel(page, paginated, model, view);
}

private String addPaginationModel(int page, Page<Vet> paginated, Model model, String view,
HttpServletResponse response) {
private String addPaginationModel(int page, Page<Vet> paginated, Model model, String view) {
List<Vet> listVets = paginated.getContent();
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", paginated.getTotalPages());
model.addAttribute("totalItems", paginated.getTotalElements());
model.addAttribute("listVets", listVets);
response.addHeader("HX-Push-Url", "/vets.html");
return view;
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/templates/fragments/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<li th:fragment="menuItem (link,active,title,glyph,text,target)" th:class="nav-item"
_="on htmx:afterOnLoad take .active for event.target">
<a th:class="${active==menu ? 'nav-link active' : 'nav-link'}" th:href="@{__${link}__}" th:title="${title}"
hx:get="@{__${link}__}" hx:target="${target}" hx:push-url="@{__${link}__}">
hx:get="@{__${link}__}" hx:target="${target}" hx-push-url="true">
<span th:class="'fa fa-'+${glyph}" class="fa fa-home"></span>
<span th:text="${text}">Template</span>
</a>
Expand Down
18 changes: 11 additions & 7 deletions src/main/resources/templates/fragments/owners.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div th:fragment="find-form" th:remove="tag">
<h2>Find Owners</h2>

<form th:object="${owner}" th:action="@{/owners}" method="get"
<form th:object="${owner}" th:action="@{/owners}" method="get" hx-push-url="true"
hx:get="@{/owners}" hx-swap="innerHTML" hx-target="#block-content"
class="form-horizontal" id="search-owner-form">
<div class="form-group">
Expand All @@ -25,7 +25,7 @@ <h2>Find Owners</h2>
</div>

<a class="btn btn-primary" hx:get="@{/owners/new}" hx-swap="innerHTML" hx-target="#block-content"
hx:push-url="@{/owners/new}" th:href="@{/owners/new}">Add Owner</a>
hx-push-url="true" th:href="@{/owners/new}">Add Owner</a>

</form>
</div>
Expand All @@ -47,7 +47,7 @@ <h2>Owners</h2>
<tr th:each="owner : ${listOwners}">
<td>
<a th:href="@{/owners/__${owner.id}__}" th:text="${owner.firstName + ' ' + owner.lastName}"
hx:get="@{/owners/__${owner.id}__}" hx-target="#block-content"/></a>
hx:get="@{/owners/__${owner.id}__}" hx-push-url="true" hx-target="#block-content"/></a>
</td>
<td th:text="${owner.address}"/>
<td th:text="${owner.city}"/>
Expand Down Expand Up @@ -83,10 +83,10 @@ <h2>Owner Information</h2>
</tr>
</table>

<a hx:get="@{__${owner.id}__/edit}" hx-target="#block-content" th:href="@{__${owner.id}__/edit}"
<a hx:get="@{__${owner.id}__/edit}" hx-push-url="true" hx-target="#block-content" th:href="@{__${owner.id}__/edit}"
class="btn btn-primary">Edit
Owner</a>
<a hx:get="@{__${owner.id}__/pets/new}" th:href="@{__${owner.id}__/pets/new}" hx-target="#block-content" class="btn btn-primary">Add
<a hx:get="@{__${owner.id}__/pets/new}" hx-push-url="true" th:href="@{__${owner.id}__/pets/new}" hx-target="#block-content" class="btn btn-primary">Add
New Pet</a>
</div>

Expand Down Expand Up @@ -122,9 +122,13 @@ <h2>Pets and Visits</h2>
</tr>
<tr>
<td><a th:href="@{__${owner.id}__/pets/__${pet.id}__/edit}"
hx-get="@{__${owner.id}__/pets/__${pet.id}__/edit}" hx-target="#block-content">Edit Pet</a></td>
hx:get="@{__${owner.id}__/pets/__${pet.id}__/edit}"
hx-push-url="true"
hx-target="#block-content">Edit Pet</a></td>
<td><a th:href="@{__${owner.id}__/pets/__${pet.id}__/visits/new}"
hx:get="@{__${owner.id}__/pets/__${pet.id}__/visits/new}" hx-target="#block-content">Add Visit</a></td>
hx:get="@{__${owner.id}__/pets/__${pet.id}__/visits/new}"
hx-push-url="true"
hx-target="#block-content">Add Visit</a></td>
</tr>
</table>
</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,28 @@

package org.springframework.samples.petclinic.owner;

import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.springframework.samples.petclinic.htmx.HtmxTestUtils.toggleHtmx;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

import java.time.LocalDate;
import java.util.List;

import org.assertj.core.util.Lists;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
Expand All @@ -32,19 +49,6 @@
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;

import java.time.LocalDate;
import java.util.List;

import static org.hamcrest.Matchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.given;
import static org.springframework.samples.petclinic.htmx.HtmxTestUtils.toggleHtmx;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

/**
* Test class for {@link OwnerController}
Expand Down Expand Up @@ -151,7 +155,7 @@ void testProcessFindFormSuccess(boolean hxRequest, String expectedViewName) thro
Mockito.when(this.owners.findByLastName(anyString(), any(Pageable.class))).thenReturn(tasks);
mockMvc.perform(toggleHtmx(get("/owners?page=1"), hxRequest))
.andExpect(status().isOk())
.andExpect(view().name(expectedViewName));
.andExpect(view().name(expectedViewName.contains("::") ? null : expectedViewName));
}

@ValueSource(booleans = { false, true })
Expand All @@ -161,7 +165,7 @@ void testProcessFindFormByLastName(boolean hxRequest) throws Exception {
Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
mockMvc.perform(toggleHtmx(get("/owners?page=1"), hxRequest).param("lastName", "Franklin"))
.andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
.andExpect(view().name(!hxRequest ? "redirect:/owners/" + TEST_OWNER_ID : null));
}

@CsvSource({ "false,owners/findOwners", "true,fragments/owners :: find-form" })
Expand All @@ -173,7 +177,7 @@ void testProcessFindFormNoOwnersFound(boolean hxRequest, String expectedViewName
.andExpect(status().isOk())
.andExpect(model().attributeHasFieldErrors("owner", "lastName"))
.andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound"))
.andExpect(view().name(expectedViewName));
.andExpect(view().name(expectedViewName.contains("::") ? null : expectedViewName));

}

Expand Down

0 comments on commit e51d734

Please sign in to comment.