Tuesday, December 22, 2015

Working with users and security (springdo-6)

Working with different users

So far, the User Interface of our application has behaved as if there is only one user. All todo items share one space and there is no login necessary. In reality, we do want our fantastic application to have more than one user, in fact we want 1000s of them! And our users probably do not want to share their todo items for all to see. So let's go to the other extreme and make the notes private to a user. We will later want to introduce note sharing between users, but that will be another episode.
In essence the user story here is there be users. It is useful to split that up in smaller parts: First we will secure our site, so only authenticated users can log in. They will still see the one large list of todo items because items are not linked to users yet.
User Story 6a: A user can log in with a password to see the otherwise secured site.
After we have users and a login method, we can link items to users:
User Story 6b: A user should only see the items that belong to them.
and of course
User Story 6c: When a user creates a new item, it belongs to them.

Spring Components

The nice part of Spring is that there are many, many components to it. If you have a problem that is more or less commonplace, like authentication, there probably is a solution for it somewhere in the larger Spring framework. The Spring sitehttps://spring.io/projects is dedicated to the projects and you can see which projects are available and what they do (in its current form (Fall 2015), the site fails to live up on its promises a bit: Subprojects are not directly visible and it is low on actual information what the projects do, but this will surely be improved upon soon).
Luckily, Spring Security is there and a few clicks later, you will find the usual '5 minute' introduction to Spring Authorization and you can find more info on Spring Security in general. Note that this introduction is not necessary for this tutorial and it also was build with normal Spring in mind, not Spring Boot, but it makes for good reading regardless. Much of what follows below is based on it.

Adding Spring Boot Security

To add a new part of Spring to our project we have to first tell our build tool (Maven) about the new dependency and then have it download the necessary files. To do so, double click the pom.xml file on the top level of the project.
In IntelliJ, you can right click anywhere in the file and choose 'Generate', then 'Managed Dependency'. IntelliJ will show a scrollable list of all available dependencies, from which you should choose 'spring-boot-starter-security'. This should generate a new dependency in your pom.xml, like so:
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Aside: There is also a 'Generate: Dependency' option and the difference does not seem to be covered the in the IntelliJ documentation. According to this older question, the position in the pom.xml differs between managed and normal dependencies (for the curious, managed dependencies explained by Maven). For us, the interface of 'Generate: Dependency' is easier but some dependencies cannot be included that way, so I advise to use 'Generate: Managed Dependency'.
If you were to run the project now, you would likely see the following error:
Screen+Shot+2015-10-16+at+1.34.45+PM.png
After you have made any changes to the pom.xml, you need to run the Maven targets (harder) or tell IntelliJ to act upon the new information (easier). Often, IntelliJ will pop up a message saying that the Maven project files need to be reread. Say yes to this. Alternatively, enable automatic reimporting under the settings menu, "Import Maven projects automatically":
Screen+Shot+2015-10-16+at+12.32.26+PM.png
A third way to have IntelliJ sync to the maven settings is to open the Maven tab (View menu, then Tool windows, Maven project) and use the shortcuts presented there. To alert IntelliJ to your maven changes, click the first button (two arrows in a circle). To download files, click the second button (folder icon with two arrows). Always make sure the 7th item, offline mode (representing by a disconnected cable icon) is in its deselected state, as Maven will not download any required dependencies in offline mode.
Screen+Shot+2015-10-16+at+1.35.39+PM.png
If all else fails, you can run the project from Maven directly, using the 'spring-boot:run' action. On the command line, you would do this by typing: `mvn spring-boot:run`. Before running the project, Maven will want to compile it and to do that, it will first pull in (download) all the dependencies.
TLDR We use Maven here but we like Gradle better / Working with Maven is not always very intuitive. The original version of this project was set up with Gradle instead of Maven. These two build systems are both very good, exhaustively documented and more than a little challenging when you first use them. Luckily IntelliJ (and any other IDE) makes things easier for you by giving you attractive buttons to click on. Gradle has the distinct advantage that its configuration file build.gradle is precisely 100 times more readable than Maven's pom.xml and there are a few other advantages too. However, almost all of the Spring (Boot) docs are made with Maven in mind, so we stuck to that convention here.

What is inside spring-boot-starter-security?

We used the dependency 'spring-boot-starter-security'. The 'starter' packages are convenient high-level dependencies supplied by Spring Boot: Instead of having to include a number of things, you just include the starter which will pull in the packages you would normally need for this task. In IntelliJ, you can inspect the file springdo.iml to see how IntelliJ has interpreted this. Here, I went to the 'version control' tab to do a diff on that file, showing that next to 'spring-boot-starter-security', nine other packages were pulled in:
Screen+Shot+2015-10-16+at+1.33.57+PM.png
Because the way Spring Boot works, reasonable defaults are assumed whenever no configuration is given. For the starter-security this means that without any further confirmation, your site will be locked down. When you go to any page, a browser-native login screen will be shown like this one:
Screen+Shot+2015-10-19+at+11.18.59+AM.png
This is not very helpful, but it is a reasonable default. The security suite can also create a default login form and other goodies, but we will not explore those here as they depend on Thymeleaf, a template engine which can help you serve pages from the server directly. Because we use Angular for the front end, our back end only outputs 'information', in the form of JSON packets, and never actual pages.
This reflects a change in style that has happened over the last few years: It used to be the case that frontend systems were minimal or non-existent, the server would simply produce a page which the browser would display as-is. Servers used technology like JSPor Thymeleaf to take a page template and populate it with user specific information ("Welcome User4829! You have 0 new messages"). Every click or input action made by the user would be sent back to the server and would produce a new page.
This system is still useful for static pages, but front end systems like Angular (and it predecessors BackboneFoundation and others) have now taken over the role of interacting with the user. The obvious benefit is speed of interaction: A click can be handled by javascript much faster by cutting out two network trips and replacing a busy server with a dedicated and lean javascript engine in the user's browser. This is even more true on mobile devices using a cellular data connection: When I turn my phone sideways, I want an instantaneous animation from portrait to landscape which would be impossible to do from the server side over a relatively slow link.

Making login work

To make login work, we need to give the users a place to login. A page 'credentials.html' was created (specifically named like that because Spring security will automatically provide a 'login' page and we wanted to avoid confusion). I have deleted some source (marked // ..) to provide focus on the essential parts of credentials.html:
<html ng-app="credentials">
  // ...
<div class="container">
    <div ng-controller="credentials" ng-cloak class="ng-cloak">
 <h1>Please log into SpringDo</h1>

 <form name="credentialform" ng-submit="gosubmit()" role="form">
     <div><label> User Name : <input type="text" ng-model="username" autofocus="true"/> </label></div>
     <div><label> Password: <input type="password" ng-model="password" /> </label></div>
     <div><input type="submit" value="Sign In"/></div>
 </form>
 <div></div>
   // ...
    </div>
</div>
We set up a simple form which is bound to the javascript variables username and password. The 'Submit' button is tied to the gosubmit function, defined in the matching javascript, credentials.js:
 1: angular.module('credentials', ['ngRoute'])
 2:     .controller('credentials', function($scope, $http, $window) {
 3:  $scope.username = "navya";
 4:  $scope.password = "secret";
 5:  $scope.reminder = false;
 6: 
 7:  $scope.gosubmit = function() {
 8:      var request = {
 9:   method: 'POST',
10:   url: '/credentials.html',
11:   data: $.param({username: $scope.username, password: $scope.password}),
12:   headers: {
13:       'Content-Type': 'application/x-www-form-urlencoded', }, };
14:      $http(request).then(
15:   function (success) {
16:       console.log("success", success);
17:       $window.location.href = "/index.html";  },  // rather heavy handed
18:   function (failure) {
19:       console.log("failure", failure); });
20:      };
21:     });
By setting the javascript variables $scope.username and .password, we populate the form elements, which makes testing much faster. Obviously we would remove this for a production version of the app. A nicer approach would only populate these fields if we are on localhost.
The work is done in the gosubmit function, which has to check the supplied username and password with the backend. It does that through a POST back to the same endpoint, with the username and password in the body of the POST. The $param function will format a javascript map into a http body for us. We need to set the content-type correctly otherwise Spring will not accept the POST (this is a gotcha that took me at least one full hour to fix).
If the authorization is successful the success function is called and we forward the user to the main page with the $window.location.href function. This causes a rather ugly refresh, but we will leave that for now. If you are interested, there is a matching$location directive in Angular that is more lightweight and will create a smoother transition. It is a little more work to make it work in this context, so we left it out here.
Aside on persistent console logs: It is hard to follow the console messages that are displayed during login, as the page changes. To make this easier, have the log persist over page changes: In the Chrome developer tools, go to the settings menu (three vertical dots) and check 'Preserve log upon navigation'. The same setting can be found in Firebug.
If the authorization fails, a counter-intuitive thing happens: The post action is still succesful but the user is not authorized. Because the post is successful, the 'success' branch is run and the user's browser is forwarded to index.html. However, because authorization has not succeeded, the Spring security framework will throw a 302 error and redirect the user back to the credentials page. This does cause a fair amount of flashing and on the whole is not much of a great user experience, but we will leave it in place for now.
The failure branch is only reached if the server has gone offline. In general, it is hard to handle that situation correctly: The server was up when the page we were on was served but has gone down since. The best we can do is tell the user to try again and not showing any reaction will (implicitly) give the same message. Only in large javascript apps that maintain extensive local state (like Gmail), does it make sense to try to retry the server and send the new information after all. Keep in mind that at that point, you will also have to disambiguate between two possible causes of this error: Either the data never reached the server; or the data did reach the serve and the server state was adjusted but the server reply never reached us.
Aside on cookies and browser state: When working with authentication, you will sometimes run into weird errors. My favorite one is an AngularJS message in the console showing that an AngularJS assertion (areq) had failed. If this happens to you, often the solution is as simple as opening a fresh anonymous browser window to make sure you do not have any tokens and cookies floating around. A hard refresh (shift-control-R or shift-command-R) will not do the same thing.

Backend changes

This login function does not work without a large number of changes in the backend.
First of all, we need to create a database of users. To this end, we create a User class, which is also a JPA Entity. To later interface with the Spring Security framework, we inherit from UserDetails, a Spring Security Core interface which requires a number of methods like getPasswordgetUsername, and isEnabled (see the Spring docs for full details).
The essence of the User class is only a few lines of code:
@Entity
public class User implements org.springframework.security.core.userdetails.UserDetails {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    public long id;

    @OneToMany(mappedBy = "user")  // what this is called on the 'many' side
    private List<Item> items = new ArrayList<>();

    @JsonView(JsonViews.ItemList.class)
    private String username;  // login credential

    @JsonView(JsonViews.UserDetails.class)
    private String password;

    @JsonView(JsonViews.UserDetails.class)
    private String email;  // for password reminders etc

    @JsonView(JsonViews.UserDetails.class)
    private Boolean isAdmin = false;  // has access to admin pages?
We declare the data fields we need, they will be given getters further down in the code. There are no setters currently, as we are not modifying users yet, only creating them. The items field warrants some explanation: Each todo item is owned by one user, and each user has many items. Hence we have a many-to-one relationship between items and user. On the user side, we express this with a @OneToMany, which takes as an argument the name of the variable on the Item side. On the Item.java side, we have a matching line:
@ManyToOne
public User user;
Together, these two code fragments allow us to use a natural interface on both entities: Items have a user variable which holds the User class that owns this item, from the outside we can do anItem.getUser to access it. Similarly, we a User object has a list of entities accessible via items, or aUser.getItems, neither of which are currently used but may come in handy soon.
Note that there is no actual User variable on an Item, and there is no List<Item> on the User. Instead, these are magically supplied by the JPA from a hidden join table which has two columns, one for the UserId and one for the ItemId. AccessinganItem.getUser() is doing a query on that table for that ItemId, similar to SELECT userid from JOINTABLE where itemid = ?1. The returned UserId is converted into a User object. Similarly, accessing aUser.getItems() is equivalent to doing a query for all items by that user, SELECT itemid from JOINTABLE where userid = ?1, followed by a map to convert the itemIds into their matching Item objects.
A few more details need to be taken care of: Each object of the Item class now has a user field as we just saw. And when the ListOfItems endpoint is hit, we return the JSON representations of a number (zero or more) items to the front end. However, we do not want this user field to contain all the information we have on the user: Specifically, we would like to omit the password and the email. However, on a (future) admin view, we would like to see those fields?
How do we return two different types of JSON from the same object, depending on the context it was called in? Jackson JSON, the JSON generator that is used by Spring, has created JsonViews for that. Views are first declared as interfaces on a small class,JsonViews.java in our project:
public class JsonViews {
    interface ItemList {}
    interface UserDetails extends ItemList {}   }
Now we have two views with a hierarchical relationship, such that UserDetails also includes all fields that that ItemList declares. Next, we annotate each of the data fields to be in one of these two views, as shown above: Username is part of the ItemList view, and the password and email are part of the UserDetails view. We wil see below how you request these views on a controller.

The admin page

To edit, add and remove users, we will need an admin page. We will not be adding the functionality to edit users just yet, but this is also a good place to display the list of all items by all users. This is basically the functionality that ListOfItems used to provided, before it changed to only return the items of this user.
Three endpoints will be implemented in AdminController.java: First, /resource/admin/list/: list all items, regardless of user. Second, /resource/userlist/: returns a list of all usernames (strings) on the system. Third, /resource/user/{username}/: shows full details of the user, including password. Finally, we need to see who is currently logged in and we do this via the /who/ endpoint, which shows name of the currently logged in user.
For full details, please consult the AdminController.java file. Below is the code for one endpoint, the others are very similar:
@RequestMapping(value = "/resource/admin/userlist/", method = RequestMethod.GET)
List<String> userList(Principal principal) {
    User user = userRepository.findByUsername(principal.getName());
    if (user != null && user.isAdmin()) {
 Iterable <User> iterable = userRepository.findAll();
 List<String> result = new ArrayList<>();
 iterable.iterator().forEachRemaining((User u) -> {result.add(u.getUsername()); });
 return result;
    } else {
 System.err.println("Unauthorized request to /resource/admin/userlist/ by " + principal.getName());
 return new ArrayList<String>();
    }
}
We connect this function to an endpoint with RequestMapping as usual. This function will return a List of Strings (usernames), and it takes a Principal as an argument. When Spring Security is active, any function can have Principal as an argument and the value, a Spring object describing the currently logged in user, will be filled in automatically.
To work with principal, we usually just do a getName(). In this code, we proceed to look up that user in our own UserRepository, using an automatically created JPA function findUserByUsername (the interface for this function is defined in the UserRepository; the implementation is provided under the hood). Only admin users should be able to see the full list of users, so we check whether our user lookup was successful (non-null answer) and whether that user has isAdmin set.
In this first pass, we have put some code in SpringdoApplication.java to create two users, Navya and Dirk, both with password "secret". Navya is an admin user, and Dirk is not.
If the authorization is successful, we can retrieve all users on the system and return a list of their names. This is done via a findAll on the UserRepository. findAll is a built-in JPA function which does what it says on the can. FindAll returns an iterable, from which we derive an iterator. A lambda function is applied to each item with forEachRemaining, using a simple Java 8 lambda which takes a user u as an input and runs the add method on our result array. This is a little convoluted, partially because of Java design choices regarding iterators and iterables, and partially because we can only return a list of real items to the front end so we have to populate an arraylist with string values instead of passing the iterator or iterable.
If the authorization is not successful, we print an error on the console and return an empty list. Ideally, the front end would check for this with an ng-if and display some placeholder text ("No users found, are you admin?") in case the list is empty. This has not been implemented in the current version, but it should be less than 10 minutes work for you.
Lesson Learned If you look at the AdminController.java file, you see that each method is wrapped in an if, checking whether the user is authorized to call this routine. This is getting tedious and we will explore a Spring method to do this more efficiently later.
Back on the admin page, we show the list of users on the system. Each name has a details button next to it, and clicking that will show the user details after retrieving them from the backend. This separation between a user list and user details is good style: If the user list is long, only returning a minimal amount of information speeds up the page load. Secondly, sensitive information is only sent over the wire when explicitly requested.
The bottom half of the admin page shows all the todo items, their item IDs and the user who owns them. I have not implemented the ability to click on an item here to expand it inline, but you should be able to create this swiftly using my code for the user list as an example.
The admin html page uses two ng-repeat statements to loop over users and items. It also uses two partials to render the users and items inside the loops, consistent with the pattern we used on the main page. On the javascript side, admin.js does all the expected contacting of the backend and storing the information in variables on the scope.
The only interesting code is the userQuery array shown below: This is initialized to {} and filled with user details when requested via the button press. The buttons are wired to userDetails(username) calls (with the username filled in) and the routine will contact the backend and populate the userQuery map. On the html side (admin.html), an ng-if"userQuery[user]"= hides details that have not been retrieved yet: If there are no data yet, userQuery[user] will return undefined, which is falsy and will surpress the html fragment.
angular.module('admin', []).controller('ad1', function($scope, $http) {
 // .. 
    $scope.userQuery = {};
    $scope.userDetails = function (username) {
 $http.get('/resource/user/' + username + '/').then(
     function (success) {
  $scope.userQuery[success.data['username']] = success.data;
     }
 );
    };
We have not defined a 'failure' branch in the javascript above. This is consistent with my stance on this: I do not know how to substantially help the user if the request failed. Clearly, the username must exist on the system because our usernames have been retrieved from the server earlier. A failing call to /resource/user/<id> means that the server is down, the connection is very slow, or the user was since removed by another admin. So there are three reasons, which I cannot distinguish between and none of them can be easily addressed by my user.
What does the User Experience look like in case of failure? If no result is returned, the user details will stay hidden (thanks to the ng-if in the admin.html). The user will probably click on the username again, thinking that somehow their first click did not register. If the request is successful the second time, they will not think much of it; if it isn't, they will think the button is somehow broken. In either case, I don't think the user experience will be enhanced by flashing a message like "Sorry, but I cannot connect to the server right now, or maybe the user has been deleted? If you are on a mobile device, move to an area with better reception. If you are on a desktop, check your wifi connection or reinsert your IP cable. " This is adding unnecessary information to an already confusing situation, not a better User Experience. It reminds me of this (infamous) Windows 8 screen:
win8-stuck-download-dialog.png

Security settings

We have wrapped the endpoints in the AdminController file in authentication testing logic. However, an unauthenticated user should not even be able to reach these methods. The same holds for the endpoints in ListOfItemsController: Without a valid login, we should not be able to access these methods. Spring Security provides a convenient method to enforce this: We basically block access to all of the site (with some exceptions for static content) unless the users is authenticated. If an unauthenticated request is made, Spring Security will redirect to the login page for a smooth user experience.
To set this up, we need to create a class that extends WebSecurityConfigurerAdapter and that is annotated with @EnableWebMvcSecurity (for Spring Boot 1.2.5 and earlier only, see below). That class should contains some 'fluent' declarations that do precisely what we outlined above:
@Configuration
@EnableWebMvcSecurity
// only exactly one class should have this annotation and it should override 'configure'

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers("/js/**").permitAll()
                    .antMatchers("/css/**").permitAll()
                    .antMatchers("/fonts/**").permitAll()
                    .antMatchers("/who").permitAll()
                    .anyRequest().authenticated()
                    .and()
                .formLogin()
                    .loginPage("/credentials.html")
                    .permitAll()
                    .and()
                .logout()
                    .permitAll()
                    .and()
                .csrf().disable()  // not great
        ;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(inMemoryUserDetailsManager());
    }

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager () {
        Properties users = new Properties();
        return new InMemoryUserDetailsManager(users);
    }

}
The first configure method allows access to pages as outlined, allows everybody to access the login and logout pages, and tell Spring that our login can be found at credentials.html. Finally, it disables Cross Site Request Forgery (Wikipedia), which is not a good thing to disable but makes our life much easier for now.
The 2nd and 3rd methods set up an in-memory database of users to be used by Spring. We will discuss this database in a minute, but in a nutshell: The InMemoryUserDetailsManager is a fast and easy way to set up authentication for first versions of your app and for testing-specific setups.

Further changes

Overview of all changes, please browse through the commit to read all the changes:
  • index.html: Minor changes to show the currently logged in user and to log out.
  • index.js: Matching changes to angular.
  • credentials.html and .js: Login page as discussed above.
  • admin.html: Page to view all items (regardless of user). In the future, this page will also allow us to add and remove users.
  • admin.js: Matching angular code.
  • AdminController.java: A new backend for the admin pages.
  • AdminControlerTest.java: A test for the backend.
  • Item.java: Added a user to each todo list item. Also changed the items so that the user name is included in the JSON for the normal view (itemlist view). Finally, added a factory method, Item.empty(User user), which will create a new empty item that belongs to the given user.
  • User.java: A new entity and class that extends Spring's UserDetails class. It basically just stores the name, password and email of each user, with some added methods to satisfy the UserDetail interface.
  • JsonViews.java: Creates a new class with two hierarchical interface: ItemList (the default view) and UserDetails (a more detailed view showing confidential user details).
  • ListOfItemsController.java: The main endpoint (/resource/list/) will now return a list of items owned by the current user. Nex to that, a few GET requests changed to PUT requests to be more conformant with REST. Spring Security will work better when creating and deleting items is done via PUT.
  • SpringDoApplication.java: Small changes to include the UserReposity (autowire), to set up the authentication framework and to add two users and five todo items to the database on startup. The five todo items were there previously, but now they are tied to users.
  • MvcConfig.java: Configuration to show login pages with Thymeleaf, no longer used.
  • WebSecurityConfig.java: Configuration settings as per above.
A short note about the Empty factory function in Item.java: This class already has several constructors (a zero-argument constructor is required by the JPA and we wrote two further constructors in the course of these tutorials). Why a factory function instead of another constructor? The main advantage here is that we can give it a good name instead of having to remember what the constructor with 1,2,3 or 4 arguments does again. We use a similar factor method to create an AdminUser in User.java, check it out.
At this point, we have a working login system: The user can log in and we have two users (hardcoded into the application for now). Stories belong to a user internally and only the stories owned by a user are visible to that user.

Upgrading Spring Boot to run tests

Building tests to work with this secured site is not as easy as it should be: The latest Spring security version (4.0.3 at the time of writing) has support for mocking logged in users, but current spring-boot pulls in a much older version (3.2.8). You can check this by opening the Maven project tab in IntelliJ and clicking on the spring-security. It is also shown in the first screenshot above, on line 66, you can see security core being included at version 3.2.8. I tried overriding Spring Boot's defaults and manually pulled in the latest Spring Security version, but this lead to version inconsistency errors.
Luckily, a new version of Spring Boot (1.3.0) was released while I was writing this, and upgrading is as simple as changing the version number in the pom.xml file and refreshing Maven (see directions above):
<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.3.0.RELEASE</version>
If you look at the springdo.iml file, you can see that Spring uses different version numbering schemes for each of its components: Spring Boot 1.3.0 pulls in the Spring Framework 4.2.3, Spring Data JPA 1.9.1 and Spring Security 4.0.3, among others.
Lesson Learned: Keep this in mind when looking at the documentation for Spring components: Consult the iml file or otherwise determine the exact version number used before you hit the internet. I wasted a good amount of time trying to make Spring Security 4.0 features work in the Spring Security 3.2 setting, because I didn't realize Spring Security has a different versioning system from Spring Framework (which was at 4.x already).
The migration does not come for free: We use a @EnableWebMcvSecurity annotation in our WebSecurityConfig.java class, but this annotation is now deprecated and replaced by @EnableWebMcvSecurity:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
There are many other migration concerns (see resources section below), but none of those affect us directly. Our previous commit had the security section excluded from the autoconfiguration, because this was necessary for that version. Now this line of code has to be removed or Spring will not start up (this is not mentioned in any of the migration documents, as far as I can tell). In SpringdoApplication.java, remove the line that reads:
@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)

Setting up and writing tests

In a real world application, we should write tests before we write the implementation but this would make this tutorial harder to read. Also note that we are still not testing any frontend functionality: We will do all of that in a later episode of this tutorial. Because we have no tool to test the front end, we cannot do end-to-end testing either, which is really starting to become a bit of an issue. But again, more on that later.
As it is, we do not have any tests for the new admin endpoints, so these need to be written. We do not have any logic in our other java files (which contain repositories, entities, and such) so there is nothing to test there. But before we write the new tests, let's run the old ones: Our one functional test is ListOfItemsControllerTest, and it currently has one failing test: 'whenCreateIsHitANewItemIsCreatedAndReturned' fails with the following (compressed) logs:
Unauthorized request to /resource/create/ by null
...
java.lang.IllegalArgumentException: root can not be null
The problem here is that the routine to create a new todo item (code shown below) requires a user. In the production version, Spring will pass in a user (called a Principal in Spring security parlance) automatically and it will be picked up by our method argument principal. So if you test this by hand on the server, the code works. However, in the test, we are not logged in as a user and we therefore get an error on the terminal and a null object is passed into principal, leading to the subsequent NullPointer exception because we cannot do a getName on null:
@RequestMapping(value="/resource/create/", method=RequestMethod.POST)
Item postCreate(Principal principal) {
    User user = userRepository.findByUsername(principal.getName());
The reason we upgraded to the new version is that Spring Security has a new annotation in version 4.x: @WithMockUser. This should give us an easy way to mock a standard user (username 'user', password 'password') for this situation. We can optionally supply a username, which is what we do here. However, adding this to the test code still does not work, the principal argument does not get populated in the test. We will see below how to fix this the right way, here let's first step over a quick fix.
Here is the first part of the test code:
@WithMockUser("Navya")
@Test
public void whenCreateIsHitANewItemIsCreatedAndReturned() throws Exception {
    mvc.perform(post("/resource/create/"))
     .andExpect(status().isOk())
This test currently fails again with a NullPointer exception. I debugged the test by putting a breakpoint on the first line of the end point handler postCreate and we again have a null value for principal. To validate that this is a valid login problem, I temporarily allowed access to the /resource/** path in the WebSecurityConfig.java and then accessed the create endpoint (/resource/create/) with the excellent Postman tool, without logging into the site first: The result is the same NullPointer exception, but this time it is expected/valid as this endpoint should not be accessed without being authenticated. (You can have an interesting Friday afternoon discussion on whether the app should handle this error, or a NullPointer is good enough for something that is not supposed to happen. I suggest doing this over cheese and wine so it is enjoyable, because IMHO there is no right answer to this question.)
As the docs point out, another way to access the currently logged in user is to access the global SecurityContextHolder with a call like:
principal = SecurityContextHolder.getContext().getAuthentication();
This is not particularly pretty and it is, as we just saw, it is only necessary for tests. I 'solved' it by creating a helper function and calling it on the first line of all Test functions that involve principal:
public Principal fixPrincipalForTest(Principal principal) {
    if (principal == null) {
 principal = SecurityContextHolder.getContext().getAuthentication();
 if (principal == null) {
     System.err.println("Unauthorized request, principal is null");
 }
    }
    return principal;
}
If you debug this code, you can see that the principal object has inner principal data, which actually holds the username and password. Apparently this is how Spring has chosen to organize its principal data structure, but it is at least slightly confusing: Running principal.getPrincipal() will not return a Principal object, but a UserDetails.User object. Keep this in mind if IntelliJ tells you that you are dealing with incompatible types. We will encounter that 'User' datatype again below.
Using fixPrincipalForTest we can make things work and I actually have left it in the code of ListOfItemsController, even though it creates a (minimal) amount of overhead in the runtime. For AdminController, it is worth exploring a better way as there are four endpoints that all need this treatment. More importantly, in AdminControllerTest, all tests require authentication of some type.
On the Spring-Security channel of Slack, Rob Winch pointed me to the very obvious solution for this problem with principal: We need to tell the MockMvc that we use for testing to use the security framework. In AdminControllerTest, you will find the following code:
@Before
public void setUp() throws Exception {
    mvc = MockMvcBuilders
     .webAppContextSetup(context)
     .apply(springSecurity())
     .build();
}
The apply(..) line make the MockMvc use Spring Security. The @WithMockUser annotations will work as advertised and the principal argument will be set in the tests. A drawback of kinds is that all interactions with the Mvc now use security, which means that we cannot access any endpoints under /resource/ without logging in. For this reason, I stuck with the unsecured MockMvc and the fixPrincipalForTest hack in the ListOfItemsController.
Once security is working in the MockMvc, Spring provides some goodies for smooth testing. For example, we can easily test whether an unauthorized request indeed returns the correct response from the webserver. You can see this in action in this test, which confirms that user details cannot be viewed when you have not logged in:
@Test
public void AdminShowUser_failsWithoutAuthentication() throws Exception {
    mvc.perform(get("/resource/user/Navya/"))
     .andExpect(unauthenticated());
}

AdminControllerTest

With that we can finally write the code for the new Admin endpoints. We can use the handy IntelliJ Navigate: Test Subject method for navigating to the matching test file: If the test file does not exist, it will prompt you to generate the test file. A popup will show you the four functions in this file ask for which ones to create tests: I checked all of them. Next, I copied the test setup from ListOfItemsControllerTest and started modifying the provided empty tests. Instead of one test per function, we prefer more 'behavior based' tests that tell a user story and its outcome.
Here is a the first two, which test the outcome of the admin/list endpoint: Recall that this endpoint returns all items in the database for the admin users, but only the users own items for non-admin.
@WithMockUser("Navya")
@Test
public void AdminList_returnsAListOfAllItemsForAdmin() throws Exception {
    mvc.perform(get("/resource/admin/list/"))
     .andExpect(status().isOk())
     .andExpect(jsonPath("$", hasSize(5)))
     .andExpect(jsonPath("$[0].title", containsString("swim")))  // owned by Navya
     .andExpect(jsonPath("$[4].title", containsString("sleep")));  // owned by Dirk
}

@WithMockUser("Dirk")
@Test
public void AdminList_returnsAListOfOwnItemsForUser() throws Exception {
    mvc.perform(get("/resource/admin/list/"))
     .andExpect(status().isOk())
     .andDo(print())
     .andExpect(jsonPath("$", hasSize(2)))
     .andExpect(jsonPath("$[1].title", containsString("sleep")));
}
Note again that these tests will not work unless we use the fixPrincipalForTest function above (as we did for ListOfItemsController) or set up our MockMvc mvc to be aware of Spring Security (what we do here). The reason for these two different approaches are historical and educational, but setting up the MockMvc with Spring Security also means that each test in AdminControllerTest has to be wired up with MockUser etc. Because ListOfItemsController contains secured and unsecured content, it is easier to use the fixPrincipalForTest helper there. (You could argue that it is not optimal and confusing to have secured and unsecured functions in ListOfItemsController; on the other hand it is good to keep related functionality together).
#+COMMENT TODO update the ListOfItemsController file at a later stage.
The tests assume the setup created in 'SpringdoApplication.java': 2 users with 5 items between them. We could make more robust tests here by actually asserting that items belong to more than one user etc, but this is the simplest thing that works. For the user test, I quickly researched whether we could write a simple jsonPath to assert no item is owned by Navya, but because our json is an array with maps inside it, jsonPath's quite expressive syntax does not work here.

Lesson Learned: Although an array as the outer element is perfectly valid JSON, many tools expect a map as the outer element with named inner elements, or a 'data' key to hold the array. In other words:
valid:      [ {"map": 1}, {"map": 2} ]
expected:   { "data": [ {"map": 1}, {"map": 2} ] }

While testing the /who/ endpoint (which is not under the protected /resource/ tree, ie. accessible by anybody), I discovered that although the intention was that /who/ would show who, if anybody was logged in, it did not really deal with the case of not being logged in. Nice catch, and another good reason to write the tests first next time.

Improving the user database

There is one issue about this setup that is particularly not satisfactory to me: We have a UserRepository, but the Spring Security framework is not using it to authenticate the users. Instead, Spring has its own in-memory database of users and at startup, we copy all users from our repository into the Spring's database (this is done in SpringDoApplication.java, relevant code shown below). This is not only duplication of data, but it will also become quite hard to manage once we add the ability to edit, add or remove users.
@Override
public void run(String... strings) throws Exception {
    User defaultUser = userRepository.save(User.AdminUser("Navya", "secret", "n@example.com"));
    User secondUser = userRepository.save(new User("Dirk", "secret", "t@example.com"));
    //  ..
    userRepository.findAll().forEach(user -> inMemoryUserDetailsManager.createUser(user));
The underlying issue here is that Spring's concept of users more or less hardcoded into the framework. Spring provides an interface UserDetails which has specifies all the things you would expect: Name, password, enabled and a bunch of other reasons why a user may be considered inactive, and finally a list of GrantedAuthority to signify which roles the user has in the system. Our User class extends the UserDetails class, but Spring Security wants to talk to a UserDetailsManager class.
There are two major ways forward: Create an extra layer of indirection to the system, which will wrap around our UserRepository and supply the methods Spring expects from a UserDetailsManager. Alternatively, we can have a two-layer User representation, with an inner User that is a org.springframework.security.core.userdetails.UserDetails type, an contains all the administrative fields like isCredentialsNonExpired. This option is well discussed in this blog. We stay on our course and follow the first option, as our User class already has the necessary security 'cruft'. Personally, I would argue that it is good style to keep all the user data in one place, even if that means that we need to store some variables that we are pretty unlikely to ever use (and have ugly names to boot). Regrettably for us, Spring Security requires these and, to me, the easiest way forward it to implement them.
First, we create a new class the UserManager, which interacts with the UserRepository:
@Component
public class UserManager implements UserDetailsManager {
    @Autowired
    UserRepository userRepository;

    public void createUser(UserDetails user){
 userRepository.save((io.pivotal.User) user);
    }
    // ... code deleted
Because our user class implements UserDetails, we can pass our users into these functions, which are required to have UserDetail parameters by the interface. However, the repository does not know how to handle UserDetails so we have to cast them back to the User class they are. I have used the full path on User in this code to disambiguate the many versions of the User class here. Below is the code for creating a user, the other methods are similar:
public void createUser(UserDetails user) {
    userRepository.save((io.pivotal.User) user);
}
In the setup of the security component (WebSecurityConfig.java), we need to initialize our new UserManager. This setup is not particularly pretty, but apparently it is what Spring requires and it gives us the flexibility to specify the UserManager at runtime. We can use this later on to only start up with our 2 users and 5 stories for tests, and use an empty database for production use.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(myUserManager());
}

@Bean
public UserManager myUserManager () {
    return new UserManager();
}
One final change is on the frontend, of all places: We have so far pre-filled the username field with "navya" (lowercase), which was apparently enough to satisfy the inMemoryUserDetailsManager, but our new login system does not convert case so we have to change it to "Navya". Similarly, use "Dirk" to login as the second user.
With that, we have a very long episode finished: Our three stories have all been addressed and can be accepted. Creating a secure app is not even that hard, it just requires a lot of background reading and understanding of how Spring Security works.
We have not implemented some straightforward things like adding, editing and removing users, but this is quite similar to the same operations on todo items. It does make for some great practice to implement this.
A related issue is that anybody can delete items at the moment if they are willing to create a custom url: All they have to do is to log in and then hit the delete endpoint with the todo item id. A better implementation would only allow the owner of the item to delete it.

Resources

Thanks

A big thanks to everyone who read and commented on this tutorial (Ehren Murdick; Michael Oleske), to Pivotal Labs for letting me write these tutorials, and to Navyasri Canumalla, who sat down with me to learn Spring and wrote half the code you see here.

Monday, November 30, 2015

Filtering standup mail at Pivotal

One of the great traditions at Pivotal is daily standup, which is not only filled with business things but also contains some nice updates on local events, new people and general interesting things.  In the good spirit that we should be aware and know about the other Pivotal Labs around the world, standup notes from all labs are sent to all pivots.

To declutter my otherwise pretty empty inbox I decided to filter these other standup messages in a 'standupOther' label, so I can read them when I have the time.  This is not as simple as it should be in Gmail:  There are many labs and they number is constantly increasing, so the right solution is to filter my local standup to one folder and then all others to another.  However, Gmail filters are not cool with NOT and they are also not great a keeping a dependency within filters.

The solution is to first assign the label to the local standup emails, then filter on standup messages that have not been labeled in a second rule (Gmail will warn you that this may not work, but it will). A third rule is necessary to filter out the announcement that I am going to run standup next week.  

You have to enter these filters exactly in this order:  New filters go to the bottom of the stack. Although Google does not guarantee the order of the filter application, it has been top to bottom for the last few years.  If you have to edit one of these filter later, the order will be upset.  Simply edit the other filters too (adding requirements on the things you know will be there, like 'Interestings', as gmail is smart about detecting changes).






Wednesday, November 25, 2015

A Git workflow

In my current project, we are introducing branches both at the server level and at the local level.  We have server level branches when our master is being released and we want to add functionality while the release process is going on.  I personally want a local branch for each story I am working on so that I can quickly switch back to the previous state to check things out.  This has led to some confusion and pain:  Essentially, we constantly forget which git command to use when.

Git is not intuitive when it comes to this workflow (or any workflow, if you ask the skeptics), especially as the team prefers to do rebase over merge.  Here is one of the many possible solutions.  I describe the case with the local work in progress branch, but you can leave that part out as described below

Workflow with a local work-in-progress branch

Step 1:  Create a work in progress branch and commit to it.  I still have not settled on always calling this branch 'wip', as it is ephemeral anyway, or giving it a decent name.   For short and simple stories, I definitely recommend wip.
git checkout -b wip
git commit -am 'done'

Step 2: You can squash or otherwise modify your commits with a
git rebase -i <REVISION>.  You can use various short cuts to refer to the revision where you branched off, I personally like to actually use the revision id (sha) there.  
We use 'git lola' a lot, which is an alias for
git log --graph --decorate --oneline --all

I realized I often only want to see the last few commits, so here is a bash alias (it does not show the coloring as git lola does, which is a shame)
alias lola='git log --graph --decorate --oneline --all | head'

Step 3: Update master to receive any new work your colleagues may have done.
git checkout master
git pull -r 
The -r option on the rebase is not required as we should not have any updates to master, but my teammates are in the good habit of always typing pull -r.

Step 4: Rebase the wip branch on the updated master, solving any merge conflicts as you go.
git checkout wip
git rebase master

If there are any merge errors, fire up your merge tool and use 'git rebase --continue'.  The commit should now be good to go and pass all your tests.

Step 5: Merge the wip branch into master.
git checkout master
git merge wip
Although this team does not like merges, this merge is actually a fast-forward so it is acceptable to them.  I guess it leaves the wip branch as a parent in the merge comment, but as we will be shortly deleting that branch this is harmless.

Step 6: Rewrite the commit and do a duet-commit / pair-commit:  To clearly distinguish our temporary commits (early, often) from the real deal, I only do a duet-commit when I am actually ready to ship this code.
git reset HEAD^
git duet-commit 

This assumes your work was all contained in one commit.  You will have to do some extra git magic if you have more than one commit to convert to a duet-commit.

Step 6: Ship the code and delete the branch
<magic-shipping-command>
git branch -d wip



Step 7: Enjoy your fresh commit

As one of my current team mates says: "I hate branches, they really blow. Stop reading this."  Sounds a lot like this guy to me :-)


Tuesday, October 13, 2015

Adding and deleting notes (springdo-5)



Adding a note

At this point, we can edit a todo note. We have a delete button, which we will deal with below. We would like to create new notes so we have something to mark as done and then later delete, right?
User Story The user can add a todo item by clicking on a button. The item is initially empty and not marked 'done'.
We will attack this problem in a few steps. First, we are going to create the frontend functionality, which includes: 1/ A button to add a todo item; 2/ a javascript function which creates an extra item in our $scope.listofitems array; 3/ some magic to show that new item.

Inserting items to the UI

A button is easily made and we make no attempt to make it look nice:
<div ng-click="plusbutton()">Plus</div>
But when the plusbutton function is run, how are we going to add an item to the UI? In a pre-AngularJS world, you may have thought to manipulate the DOM (the text on the browser's page) and insert an extra html item. The most popular library for this is jQuery, which gives you convenient functions to locate the insertion point (here is a brief overview of the things you won'tneed) and to create new items and insert them at some point (jQuery tutorial).
AngularJS has some minimal version of jQuery built in, but for simple projects you won't need it as AngularJS can do the work for you. Remember that AngularJS has a two-way data binding? Changing the UI state (via user input) immediately changes the javascript variables, and changing the javascript variables immediately changes the UI. This last point is obviously true for variable insertion, like <p>{{item.content}}</p>: If we change item.content, the user's content will be updated. Interestingly, this is also true for more complex AngularJS constructs, like:
<ul>
  <div ng-repeat="item for listofitems">
     <li>{{item.title}</li>
  </div>
</ul>
This will create a bulleted list of item titles. If we add an item to the javascript variable listofitems, AngularJS will also expand the list displayed to the user. In fact, if we remove the 3rd item of the list, AngularJS will remove that item from the display and nicely animate its removal. I have created a JSFiddle that shows how this works with much of the complexity of our current html removed, play around with it to see how changing variables affects the UI. You can also checkout this other tutorial.
As you can see, AngularJS uses a short animation to make it clear to the user what is happening: If you instantly remove an item from a list, chances are the user will never notice it. The animation support in Angular has changed quite a bit over time and the new 2.0 version is supposed to be fully centered around it. In this second fiddle, you can explore how changing the animation parameters is a matter of adding a class on the ng-repeat element and then using CSS to supply animation parameters for that transition. More explanation and demos of some quite over the top animations (like 3D rotation) are shown over at nganimate.org.

Templates

In the example above and in the JSFiddle, our loop looks very neat and tidy because we just write the item's title with <li>{{item.title}}. The current code in index.html is much harder to read because of the viewing and editing state of each item, and the logic for showing and hiding content. Wouldn't it be nice if our loop could be written like
<div ng-repeat="item in listofitems">
     <div ng-include src="'itemform.html'"></div>
</div>
In fact, we can do just that by providing a 'partial', an html fragment for insertion into our current pages. Note the double quotation on the partial: The name is a constant that needs quoted once for html and once more for AngularJS. The canonical source of a partial is to download it separately, here we inline it:
<script type="text/ng-template" id="itemform.html">
   <div class="list-group-item row">  <!-- rest of template goes here -->
The partial makes our html much more readable and it would also be a nice solution if we generated items in two places (which we currently don't do). Another approach would be to create your own AngularJS directive, ie. a new pseudo html element like <myitem ..>. Directives are very powerful and recommended when you are rewriting the DOM; they are overkill for this simple application. The main advantage of a directive is that it has its own scope, just like a javascript function has its own local variables. This allows you to write reusable code that does not depend on the parent scope, like an include does. This stackoverflow answer discusses the differences in more detail.

New versus create

TLDR A create endpoint is better than a new endpoint.
Something slipped into the last commit that should technically not have been there (splitting up historical commits to make a good tutorial is more complicated than you think). At the bottom of ListOfItemsController, I wrote a note to myself on a future endpoint for creating a new note:
post('/resource/new/title/content/done/')
That seemed like a logical choice, but there are a few problems that we encountered when we thought it through.
Let's first talk about the big benefit of this approach, namely unconnected operation. The frontend can create a note and allow the user to edit it without contacting and possibly waiting for the backend. That is a big gain, but it also means that if the backend is unavailable, we can only signal an error when the user has entered and edited the complete note. It is not hard to predict what users will think, say or shout when a note disappears into virtual reality upon hitting 'Submit'. It is much better to not let them create a new note if the server is unavailable, as the users will not lose any work. In the worst case, they will pick up their pencil-and-paper todo list again.
We could of course design some fancy local storage for the note and have the frontend and backend talk asynchronously. This sounds like a great project if you want to learn more about asynchronous operation! But before you start, this may work very well for mobile apps (where this type of operation is quite common), but it is much less well suited for a webbrowser. The infrastructure is there: we can use localStorage from the WebStorage API, which will allow us to store a key-value pair that persists over browser restarts.
However, the modern user often uses different browsers at different points over the day. They will seamlessly switch from Chrome to Firefox and onward to the browser on their phone or tablet. Imagine we used localStorage on a tablet browser, but the tablet was shut down soon after the note was created so the change is never propagated to the server. The user will not understand what happened with that note and be even more surprised when it resurfaces a day or so later, when the tablet was started up again for completely different reasons. There is only one way in which we can provide persistent storage over browsers: using our own backend.
Instead of a save endpoint, we will be using a create endpoint which returns the ID and contents of an empty note. This has the aforementioned disadvantage of not being able to start a new note if you are offline. It solves an issue we had not addressed yet: Each note has to have a unique ID and in an offline operation, we would have to rely on large random numbers to avoid collision (for example using UUID.randomUUID). The current system makes our backend the only source of IDs so it is easy to maintain uniqueness. Why does the create endpoint return the contents of an empty node when that is, well, empty?? In the future, the server could add all kinds of metadata to the note (like date created, user profile) or it could serve several types of skeleton notes (shopping lists, holiday todos etc). We don't use this functionality now but it is good to keep that option in there.
TLDR Server generated unique IDs scale / Now what happens if our excellent ToDo service goes viral, we have 10,000s of users and we need multiple many backend servers? How do we maintain uniqueness under that scenario? We could just tie each user to one a specific backend server and that server would continue to provide unique IDs for those user, just like in the simple case.

Can we finally see some code?

This is all fine and well but our User Story has not been addressed at all. Let's fix that fast, by first making a frontend only solution that just shows that we can do this. We added a simple button to our html above, that calls the plusbutton function. A simple-minded way to implement that function would be:
$scope.plusbutton = function () {
 $scope.listofitems.push({"id": 6, "title": "new item", "content": "Hi", "done": "no"});
};
Full code is in the commit below. When you check it out, it will work just fine and add a dummy item when you click the button. You can hit the button multiple times and get multiple new items. Try opening one of the new items and you see we have introduced a bug. Can you figure out what causes this behavior?
That was great, we are ready to actually move this to the backend. Let's do this right and start by writing a test for the backend functionality:
@Test
public void whenCreateIsHitANewItemIsCreatedAndReturned() throws Exception {
    mvc.perform(get("/resource/create/"))
     .andExpect(status().isOk())
     .andDo(print())
     .andExpect(jsonPath("$.title", is("")))
     .andExpect(jsonPath("$.content", is("")))
     .andExpect(jsonPath("$.done", is("no")));
}
The new javascript function is a little more complicated than our old one, but nothing too fancy: We do a GET request to the new endpoint and put a success function in that captures the returning data. As a slight novelty, the data is not only the item's ID but the full item this time (this assumes all our items are JSON serializable). We add the item to listofitems (javascript), expand the new item and start edit mode on it.
$scope.plusbutton = function () {
 $http.get('resource/create/').then(function(success) {
  newitem = success.data;
  $scope.listofitems.push(newitem);
  $scope.toggleContent(newitem.id);
  $scope.goedit(newitem);
 });
};
The definition of the endpoint is even shorter: All we have to do is create a new Item, save it to the database and return it.
@RequestMapping(value="/resource/create/", method=RequestMethod.GET)
Item postSaveUpdate() {
    Item item = itemRepository.save(new Item());
    return item;
}
If you run the example, you can see that you can reload the page and your changes persist. We have implemented saving new notes.
There is a small UI error with our version so far: When you create a new item and hit 'Submit' right away, you get an empty title field. There is nothing inherently wrong with that, other than that that means there is nothing to click on any more, which means we cannot open the item, which in turn means that we cannot edit it anymore. Before you read on, take a moment to think how you would solve this, or maybe even solve it. Two solutions are sketched next.
A quick fix would be to add a bit of space (non breaking space, &nbsp;) to the end of the title, so there is always something clickable there:
<div ng-click="toggleContent(item.id)"><b>{{item.title}}</b>&nbsp;</div>
A better solution is to check whether there is a title and display a substitute title if there is none. This can be done with the ng-if directive, which removes a html element if the 'if' is not true:
<div ng-click="toggleContent(item.id)">
    <b>{{item.title}}</b>
    <span ng-if="item.title.length == 0">-No Title-</span>
</div>
This is what the result looks like:
Screen+Shot+2015-10-13+at+2.45.16+PM.png

Deleting a note

Deleting a note is the 'D' in the CRUD acronym of common interfaces. This means that JPA is ready to deal with it, it provides a delete(id) call to handle item deletion (there are several other ways to call delete, see the docs).
Before we start writing the code, we should write the test. We then run the test and watch it fail, because the endpoint does not exist yet.
@Test
public void whenDeleteIsHitItemIsTrashed() throws Exception {
    // setup
    String newtitle = "New Test Title";
    Item item = new Item("Test Todo", "Do Lots of stuff - then delete");
    itemRepository.save(item);
    // now remove it
    mvc.perform(post(String.format("/resource/delete/%d/", item.id)))
     .andExpect(status().isOk());
    Item newItem = itemRepository.findOne(item.id);  // returns Null if not found
    assertNull(newItem);
}
Now for the implementation: On the html side, we add a click function to the trash can icon: godelete(item). This javascript function will contact the endpoint and if deletion was successful, delete the item locally:
$scope.godelete = function (item) {
    $http.post('/resource/delete/' + item.id + '/').then(
        function (success) {
            listofitems = $scope.listofitems;
            for (i = listofitems.length - 1; i >= 0; i--) {
                if (listofitems[i].id == item.id) {
                    listofitems.splice(i, 1);
                } } }); };
In a future version, we should get an update from the server telling us what the change in the listofitems is, for now we just remove the item ourselves. This is not great because we are duplicating the actions on the backend in the frontend, and this will surely come back and bite us one day. But it is really ok for now.
The endpoint is a simple application of JPA delete, as promised:
@RequestMapping(value="/resource/delete/{id}") //, method=RequestMethod.POST)
String deleteItem(@PathVariable long id) {
    itemRepository.delete(id);
    return "[\"ok\"]";
}
See you next time!

Thanks

A big thanks to everyone who read and commented on this tutorial, to Pivotal Labs for letting me write these tutorials, and to Navyasri Canumalla, who sat down with me to learn Spring and wrote half the code you see here.

Monday, October 12, 2015

Dynamic content and editing entries (springdo-4)



Hiding and showing content

You may have noticed that we are only showing the notes titles, IDs and the done status so far (and those IDs are only there for us, they will be removed in future version). That leaves the content of the notes hidden from the user's view. Recall, the content was background information to the note, so it is a great design decision to leave it hidden at first. However, our user wants to be able to view it at times:
User Story The user can click on a title to make the content visible.
The story is not complete, we imagine that
  1. all items come up as closed first (content hidden)
  2. opening one item closes any other open items
  3. clicking on the title of an already open item closes it again.
We will dive a little deeper into AngularJS to see how we can accomplish this task. As showing and hiding parts of a page is very standard functionality, AngularJS built a great system to hide/show almost any part of a webpage.
 1: <div ng-controller="home" ng-cloak class="ng-cloak">
 2:     <div class="list-group">
 3:  <div ng-repeat="item in listofitems">
 4:      <div class="list-group-item row">
 5:   <div ng-click="toggleContent(item.id)" class="col-md-6">{{item.title}} ({{item.id}})
 6:       <p ng-show="content[item.id]">{{item.content}}</p>
 7:   </div>
 8:   <form name="form{{item.id}}">
 9:       <div class="col-md-6">Done
10:    <input type="checkbox" ng-model="item.done"
11:           ng-true-value="'yes'" ng-false-value="'no'"
12:           ng-click='onDoneClick(item)'>
This is the same controller we saw before, but in line 6 we see a new <p> which wraps {{item.content}}. Crucially, this paragraph is shown or hidden under control of some javascript variable thanks to ng-show. If the value of ng-show is true, the tag that ng-show is part of is shown, otherwise it is hidden (documentation). If a transition between shown and hidden occurs, the transition is animated. Like all AngularJS, the webpage will (almost) immediately reflect any changes you make to the variable.
The easiest way to use ng-show would be with a simple variable, like so
<div ng-show="showit">Hide or show me </div>
Note that showit is a variable at the javascript level, but it should be quoted in the ng-show statement because HTML will not know about it. In the matching AngularJS we will refer to the same variable as $scope.showit. Because we have more than one item to show and hide, we use an array content instead (again, we did not choose the best possible name here,contentVisible[item.id] would have been much better).
Why does the array variable have to be quoted? What AngularJS does under the hood is to have javascript evaluate content[item.id] in the context stored in $scope. Different controllers have different scopes, which gives you a way to separate namespaces without having to name those spaces: Another controller could also have a content variable, but that would be in another scope. This concept of scope is very similar to dynamic languages like Python.
Two asides: First, in the code above we have added Bootstrap formatting classes like col-md-6 in places. Second, if you want to use a constant in an angular directive you have to double quote it, once for HTML and once for the javascript evaluate, like so:
<div ng-show="'constant'"> Text will be shown </div>
Hiding the content of the item is great, now we need a means to show it. Line 5 has a div with the an ng-click attribute, and a function to call when this div is clicked on, the function will have to be defined in the home controller as $scope.toggleContent. Note that any item can have a ng-click attribute (thanks to the HTML onClick event, which can basically be attached to any html element), we do not have to create a button.
Here is part of that function:
$scope.content = [];
$scope.toggleContent = function(itemid) {
 $scope.content[itemid] = !$scope.content[itemid]; };
In javascript, 'undefined' is falsy which means that all content will be hidden by default. The boolean negator ! will turn anything falsy into true and the relevant item content will be shown. The initialization of the content array in line 1 is crucial, without that the code will not run (Whereas accessing a non-existing item on an existing array returns undefined, accessing a non-existing item on a non-existing array will throw a 'Uncaught reference error'; gone are the good old days of Awk where this was totally fine).

Closing the other items

TLDR Front end testing really helps to check that we do not introduce regressions when adding new functionality.
This solves the user story and our considerations 1 and 3 above. We have not dealt with consideration 2: "Opening one item closes any other open items". If you look at the interface like this, it seems nice enough. But apparently on further probing, our product manager wanted the app to look more like a mobile app, where this 'one thing open at a time' behavior is very common. From a UI perspective, it makes the display less busy and cluttered, but it means users cannot compare the full contents of two todo items easily. On the web, this kind of UI also exists: jQuery calls this the Accordion Widget and you can easily create a very similar widget in pure Bootstrap.
Implementing it is not hard but the there is a small catch. Before I go over those I suggest you take the previous commit and try to implement this behavior for yourself: We managed it in three lines of javascript and you may be able to do it in less.
OK, the catch that we missed at first (and did not find during our testing either), is conderation 3: "Clicking on an already open item should close it again". The code at the commit above has this behavior, but we implemented consideration 2 by closing all items before we toggle the current one, like this code but without line 2.
1: for (var key in $scope.content) {
2:  if (key != itemid) {
3:   $scope.content[key] = false; }}
4: $scope.content[itemid] = !$scope.content[itemid];
Closing all items first and toggling the current one will give us the correct behavior when we open an item, but it will make it impossible to close one. So the if becomes necessary to guard against this. We did not notice this for a while because we had tested at earlier stage, when it still worked, and we never tested it again as we were focussing on closing the other items. Again, we will not be adding test to our AngularJS code to keep this tutorial focussed, but for any serious coding exercise, you should.

Debugging front end code

Restarting the spring backend for every small change in the front end code is slow and tedious. IntelliJ has some support for live editing of html and css files, but it is hard to set up and (in our setup) prone to breaking. Because you can easily write a mock version of your controllers in AngularJS (simply add a couple of hard-coded variable assignments), it is often more practical to use JSFiddle, Plunker, or similar tools to test your code (read this nice comparison). Make your changes there and then copy and paste the new parts back into your project. You can view some experimentation we did with this code here: https://jsfiddle.net/dirkjot/gjtmpksw/
You can also find and explore some darker corners of AngularJS that way: In the code on the fiddle, we added a div to make the title bold. The item id now goes to the next line, so a sensible thing would be to change the div into a span. Try this and click around on the first and then the second title, and you will see the first title disappear when the first item's content is hidden. After some probing, we found that wrapping a div around the the p that contains the content helps; without part of the ng-show seems to leak out of the p into the surrounding div. (If you have a more precise explanation, please leave a comment!)
Either way, this closes the user story so we make a commit of these three lines of code.

Editing todo items (front end)

Our Product Manager is very happy with us as we are making great progress. Let's move on to user contributed content, in the form of this story:
User Story The user can edit the title and content of a story
The story does not specify how this edit is started. We cannot tie it to a click on the title, as that already expands the item. We thought it was most appropriate to add some well-known icons to the expanded todo item, so we keep a very clean interface. Here is an artist impression of what we want to create:
Screen+Shot+2015-10-09+at+12.16.31+PM.png
Obviously, the placement of the icons and the done checkbox is not perfect, but we can improve on this later and focus on writing code for now.
Our first step is to pull in the font-awesome icons so we can use them here. We simply put four glyphicons files in the correct directory (main/resource/static/fonts) and we are good to go. To use them, we add two lines of html to our index.html:
<span class="glyphicon glyphicon-pencil" ng-click="goedit(item.id)"></span>
<span class="glyphicon glyphicon-trash"></span>
We already added a javascript function to a click on the edit icon; we leave the delete icon for later. You can see this code in action on this JSFiddle (we removed some experimentation we added in the previous fiddle).

Actual editing

To actually edit the form we have to write a fair amount of html, as each of the items will be in one of three interface states, listed below with the combination of variables that determines the state:
  • collapsed: Only title and done checkbox shown. Shown when content[item.id] is falsy.
  • opened: Title, content and done shown, edit and trash icons visible. Shown when content[item.id] is truthy and editorEnabled is false.
  • editing: Title and content turn into input boxes, submit button, no other elements. Shown when content[item.id] is truthy and editorEnabled is true.
This image shows an item in 'editing' state. Because you can only move to 'editing' via 'opened', all other items are necessarily 'collapsed' at this time.
Screen+Shot+2015-10-09+at+2.24.09+PM.png
The html for each item will now have three parts, one for each of the three states. I refer to the full html in index.html, lines 15-40 on this github commit, here are the highlights:
 1: <div class="list-group-item row">
 2:     <!-- not editing: -->
 3:     <form name="form{{item.id}}"   
 4:  ng-show="!content[item.id] || (content[item.id] && !editorEnabled)">
 5:      <div ng-click="toggleContent(item.id)"><b>{{item.title}}</b></div>
 6:      <div ng-show="content[item.id] && !editorEnabled">
 7:   <p>{{item.content}}</p>
 8:     [...]
 9:     <!-- editing: -->
10:     <div ng-show="content[item.id] && editorEnabled">
11:  <form name="formedit{{item.id}}"  ng-submit="saveedit(item)" role="form">
12:      <div class="form-group">
13:   <input type="text" ng-model="item.titleField" class="form-control"><br>
14:   <textarea type="text" ng-model="item.contentField"  class="form-control">
15:      </textarea><br>
16:   <input type="submit" id="submit" value="Submit"  class="form-control btn btn-warning">
What happened:
  • The html page has become quite complex: Each item has two forms, one for the not-editing state (with only a checkbox as a form element) and one for editing the title and content. Each item has these forms, so there are a lot of forms open at the same time.
  • The two forms have names specific to the item they are working on: One form is form.. (insert item.id at the dots) and the editing form is formedit... For each item, only one of them is visible at a time; and only one item can be opened (or editing) at the same time.
  • Aside While debugging, we actually disabled this ng-show mechanism to make it clearer what was happening. I encourage you do the same.
  • The ng-show on line 4 controls the heading (the title). The heading should be shown when this item is collapsed, or when it is open but we are not editing.
  • We bolded all the item titles and removed the id numbers. We decided against angular magic to make only the title of the currently open todo item bold. This would look nice but was more work than we were willing to invest.
  • On line 13, we create an input field and connect it to the item.titleField attribute. Angular will 'live' update that variable as the user is typing. We could have used item.title here instead and there would be no need to copy values back and forth between item.title and item.titleField (see index.js). However, the drawback of that method is that you cannot undo your edits. Even though we did not have a Cancel button in the original design, we will be adding one soon. To be able to cancel, we need to either save the old value, or run the input box on another variable.
  • in line 14, we do the same for the content, using a textarea.
As you may recall, the edit button calls goedit, and the submit button (line 16 and 11 above) calls saveedit. We will write these functions now. The first one, goedit is not complicated as we have to change state and populate the titleField and contentField variables:
// editing content
$scope.editorEnabled = false;

$scope.goedit = function (item) {
 $scope.editorEnabled = true;
 item.titleField = item.title;
 item.contentField = item.content;
};
The saveedit function has two parts: Moving the edit from the editing fields into the AngularJS model, then moving the changes into the backend. We will only write the first part for now. The code is not hard, you could probably write it (or jot it down on a piece of paper) in a minute or so. The one addition I made below is a promise to myself to what I want the backend update to look like: A POST to /resource/save/.
Save edit therefore is more or less the opposite of goedit:
$scope.saveedit = function (item) {
 item.title = item.titleField;
 item.content = item.contentField;
 $scope.editorEnabled = false;
 // TODO post this stuff
 // post('/resource/save/id/title/content/done/')
};
Lesson Learned This is actually not a great choice for posting material. It would be safer and better to put the item content in the body of the POST, instead of the URL. Then again, we are not sending sensitive information around and the length of our notes is not being constraint too much by the maximum recommended length of a URL, 2000~characters (and Chrome can easily go over 150,000 characters)
At this point, you can make edits and hit 'submit'. If you open and close the item, you will see that your saved content has been saved locally. However, a page reload will contact the server again and you will get the original items back. We are finished with our story, but we can see the need to persist changes in the backend.

Updating the backend

User Story The user's edits are persisted.
The third story in this episode is going to be very quick: All we have to do is communicate the edits from the frontend to the backend and then persist those edits on the backend. We just have to apply what we already know about endpoints and the JPA.
In 'index.js', we make the post that we sketched above more concrete:
$http.post('/resource/save/' + item.id + '/' + item.title + '/' + item.content + '/' + item.done + '/');
In 'ListOfItems.java', we add the endpoint. There is not that much code here: First some java boilerplate to get the parts of the URL into variables. Next, we retrieve the old item from the database. We do a check whether the 'done' status is indeed only yes or no (but we don't really know what to do if it is not, a common problem in programming). The actual saving of the new item is one line of code thanks to the JPA.
@RequestMapping(value="/resource/save/{id}/{title}/{content}/{done}/", method=RequestMethod.POST)
String postSaveUpdate(@PathVariable long id, 
        @PathVariable String title, 
        @PathVariable String content, 
        @PathVariable String done) {
    Item item = itemRepository.findOne(id);
    if (done.equals("yes") || done.equals("no")) {
 item.done = done; }
    else {
 System.out.println("Invalid argument to postSaveUpdate:  " + done); }
    item.title = title;
    item.content = content;
    itemRepository.save(item);
    return "[\"ok\"]";
}
It is nice to see how we can add major user-facing functionality with a few lines of code. In our enthusiasm to implement this, we forgot to do proper Test Driven Development: We should have written a test for the new endpoint before implementing it. Writing a test after the fact is still better than not writing a test at all, so off we go. We should
  • create an new item (for testing purposes)
  • submit it to the database, holding on to its ID
  • POST to the endpoint with a changed title and contents
  • retrieve the new item from the database
  • confirm that title and content have changed.
The code is almost as few lines as that and I recommend you try to write it yourself before you peek at the committed version (near the end of the file).
Final state at the end of this tutorial:
See you next time!