Sunday, May 19, 2019

#CodeConfident: Spring PetClinic

My last weeks were super busy. As a result, I had to neglect my code-confident challenge and was feeling sad about it. I knew already knew for longer what my next practice project should be about: extending an existing backend feature. I found, however, that getting it started was a lot more difficult than I expected. When I finally could start, I got intrigued yet realized I need to learn a lot more. In the end, I did the biggest part just on one day as I couldn't let go anymore!


April 27


May 12


May 13


May 14

  • pairing session with Amitai Schleier
  • presented him my current problem, first lack of time and now stuck in researching projects trying to decide which one to work on
  • told him about my criteria and desired tech stack
  • started to present my latest list of 5 candidates
  • Amitai: likes to look at tests first to see if they are readable and to see what the app is supposed to be doing
  • after the first project he stopped me and suggested a project he just found by using Duck Duck Go search "open source spring-based application": Pet Clinic, a Spring demo project
    https://github.com/spring-projects/spring-petclinic
    http://projects.spring.io/spring-petclinic/
  • cloned the project
  • Amitai: let's try to run it first
  • ran it using the run configuration detected by IntelliJ; ran but did not find css
  • the readme showed how to run the app --> worked this time including building of css
    ./mvnw package
    java -jar target/*.jar
  • wanted to use IntelliJ and make it faster, found we could add the package step as Maven goal step before launch: "package"; tried out different variants, found we needed to keep the build step before
  • discussed general approach to tackle an existing app; I shared it depends what I am looking for, if I'm reviewing other people's code or if I'd like to contribute myself, etc.; Amitai: wants to see if he can quickly get something to change, see the cause and effect; he could read a lot and model and everything but rather prefers changing something as better test of understanding; wrote a tweet thread about cause & effect
  • we decided to change the pet picture on the welcome page to a different one; used this as valid reason to google for cute animal pictures ^^; replaced the image, replaced the image reference, verified the change got applied after application restart
  • before going further, we felt that packaging took too long as it was running all tests; wanted to comment them out, but could not find them in the pom; looked upMaven lifecycle (https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html) and found that tests are included by default; found we can skip them by calling "package -Dmaven.test.skip=true"; startup now a lot faster, still a bit slow for actual fast feedback during development, yet doable
  • had a look at what the app already provided, like "find owner"; decided to add "find pets"
  • added a new menu item (copy & paste from find owners), restarted - nice, we land on the error page as expected
  • added a controller, copied over from owners; copied the owners html page, adapted as well; our "hopothesis" would have been that now we would be able to access a find pets page with a form to search for owners; yet we knew we probably missed something and indeed, we got the error page instead
  • we realized the relationship of owners and pets was not modeled parallel as assumed, but having pets subordinate to owners
  • Retrospective:
    • Lisi: thank you so much, you got me unstuck in a few minutes! Amitai: seems like my super power, as I have to always get myself unstuck: I know how to orient myself in my problem space, and know how to help people orient themselves in their problem spaces
    • Amitai stayed in the navigator role, Lisi as driver; both thought it went well, were not sure if it was okay for the other one but only addressed it in the end; Amitai: felt you picked up things, needed to instruct less and less, you already knew what I wanted; Lisi: when I want to learn, I try to keep myself on the driver seat; I also learn a lot as navigator, having to express my intent and decide where to go next, yet now it's about hands-on practice; Amitai: agree, it's always good to practice; agreed to switch roles next time
    • learned that copy & paste is not always working, we have to learn more; was a good reminder to think about the model and how we would want it to be; having pets subordinate to owners did not feel right; what if a pet changes the owner? We would not want to loose the record for the pet, or the past owners; also from a moral standpoint a partnership would be better
    • I will have to decide where to go next and define my scope for this practice project; Amitai: Gilded Rose is known as refactoring kata, but he likes to call it the "when to stop" kata; what's the cheapest thing you can do? It's about business, not about the cleanest code; it's really important to zoom out, zoom back in, zoom out again, and realize when to stop
    • Lisi: last session and this one as well I gained a lot of benefit from what you shared, bits and pieces of knowledge here and there, and especially from your approaches, how to tackle things; they work well for the concrete thing, but are also applicable in a generic way, will also help me later
    • Amitai: last time you gave me energy, happy to give it back this time!
    • agreed to do a third session end of June / July
  • TODO:
    • set up practice project
    • define scope
    • do it

May 15


May 16

  • tried to understand app better
  • need debugging to understand more
  • not much time today

May 18

  • debugging the owner controller: did not stop in debugger - why? still haven't found out; would be something to investigate in a pairing session
  • learned more about the demo project; https://projects.spring.io/spring-petclinic/ provided lots of info, however, for an outdated version
  • found the community built a lot more versions: https://github.com/spring-petclinic among them also one for Angular :) https://github.com/spring-petclinic/spring-petclinic-angular (could be the perfect target for my next challenge!)
  • learned more about MVC (Model View Controller): https://spring.io/guides/gs/serving-web-content/
  • saw our get mapping is a bit different, realized I need to look that up and learn more
  • learned more about Thymeleaf inputs https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#inputs
  • trying to adjust controller to have correct result collection for binding, adding results from search by first name to the results from search by last name https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#addAll-java.util.Collection-; assumed the resulting collection will contain duplicates --> assumption was correct! keeping fields empty to get full list is now showing duplicates; also, existing first name is still showing full list
  • https://www.geeksforgeeks.org/how-to-remove-duplicates-from-arraylist-in-java/ tried the Stream version and found it cannot be cast to a collection
  • used set instead of collection and it works! bye bye duplicates :)
  • found that when I provide values for both fields, search by first name does work now indeed! --> found I always list all in case one field is empty... o_O changed this to only show all in case both are empty --> still, one empty field will show the list of all owners
  • cases:
    • last name empty, first name empty => list all owners
    • last name exists once, first name empty => show owner details (does not work yet)
    • last name empty, first name exists once => show owner details (does not work yet)
    • last name exists multiple times, first name empty => list selection of owners (does not work yet)
    • last name empty, first name exists multiple times => list selection of owners (does not work yet)
    • last name does not exist, first name exists once => show owner not found (does not work yet)
    • last name exists once, first name does not exist => show owner not found (does not work yet)
    • last name does not exist, first name exists multiple times => show owner not found (does not work yet)
    • last name exists multiple times , first name does not exist => show owner not found (does not work yet)
    • last name does not exist, first name does not exist => show owner not found
    • last name empty, first name does not exist => show owner not found (does not work yet)
    • last name does not exist, first name empty => show owner not found (does not work yet)
    • ...
    • more cases:
    • both names exist, but for different records => show owner not found (now shows both records)
    • parameterless GET request /owners should still list all
  • feel the database query should already take care of filtering everything; implemented look up by full name for results list --> oh my it works!!
  • what about exact same full name, but different entries? should still list both entries --> and it does!
  • not flawless but worth to commit it :D
  • added tests for new controller feature; test for not existing first name fails, not sure why yet --> experimenting made me finally understand better what the "rejectValue" method does in the controller! added handling in case first name is not found; now error message is displayed twice! ^^
  • researched a lot more regarding Thymeleaf and error messages
    https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#validation-and-error-messages
    https://stackoverflow.com/questions/50005969/thymeleaf-or-operator-in-thif?rq=1
    https://spring.io/guides/gs/validating-form-input/
    https://teamtreehouse.com/community/where-does-thymeleaf-get-the-error-messages-from
    https://howtodoinjava.com/spring-mvc/spring-mvc-display-validate-and-submit-form-example/
  • found how to display an error for a specific field:
    <p th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">Error</p>
  • however, wanted to have a combined message if an owner is not found, not separated per field; found how to add and display global error:
    • Controller:
      result.reject("ownerNotFound", "not found");
    • Template:
      <p th:if="${#fields.hasGlobalErrors()}" th:each="err : ${#fields.globalErrors()}" th:text="${err}">Global Errors</p>
  • downside: the test for the global error is super generic, did not find a way to test for exact error
    https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html
    https://blog.codeleak.pl/2014/08/spring-mvc-test-assert-given-model-attribute-global-errors.html
  • tested the tests, made them fail, added assertions; how to assert at least for the number of results found? haven't found an answer yet
  • still, the scope of this challenge is fulfilled for now, I implemented a small new feature :D can still extend it, improve it further and clarify the open questions when pairing with others

No comments:

Post a Comment