Skip to content

Commit

Permalink
sample Spring MVC/Angular App
Browse files Browse the repository at this point in the history
  • Loading branch information
JHades committed Jan 17, 2015
1 parent e5b6a27 commit 01caa8a
Show file tree
Hide file tree
Showing 65 changed files with 7,513 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .bowerrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"directory" : "src/main/webapp/resources/bower_components"
}
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules
.tmp
.sass-cache
.idea
*.iml
target/
target/**
bower_components

104 changes: 103 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,103 @@
# spring-mvc-angularjs-sample-app
# Spring MVC | AngularJs Sample App #

A test project for a daily calories consumption tracker. The project can be run with two different profiles, one using a postgresql database and the other is a test mode that starts an in-memory database and fills it with some test data.

### How to run the project in test mode ###

After cloning the repository, run the following command on the root folder of the repository:

mvn clean install tomcat7:run-war -Dspring.profiles.active=test

After the server starts, the application is accessible at the following URL:

http://localhost:8080/

To see a user with existing data (16 meals, 8 days from 1st of January 2015 to the 8th), login with the following credentials:

username: test123 / password: Password2

This is how the application looks like after login:

![alt Calories Tracker](TODO)

### Frontend Overview ###

The test project is a web application with an AngularJs-based frontend and a Spring/Hibernate based backend. The application is responsive, as it adapts to different screen sizes.

On the frontend, these libraries where used (besides Angular): [Yahoo PureCss](http://http://purecss.io/) (pure CSS baseline) and [lodash](https://lodash.com/) for functional data manipulation. The module system [require.js](http://requirejs.org/) was used to load frontend dependencies. The dependencies where obtained via [bower](http://bower.io/).

The angular module [angular-messages](https://egghead.io/lessons/angularjs-introduction-to-ng-messages-for-angularjs) was used for frontend form validation, and this [jQuery plugin](http://plugins.jquery.com/datetimepicker/) was used as the datetimepicker component.

### Backend Overview ###

The backend is based on Java 8, Spring 4, JPA 2/ Hibernate 4. The Spring configuration is based on Java. The main Spring modules used where Spring MVC and Spring Security. The backend was built using the DDD approach, which includes a domain model, services, repositories and DTOs for frontend/backend data transfer.

The REST web services are based on Spring MVC and JSON. The unit tests are made with spring test and the REST API functional tests where made using [Spring test MVC](http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test-framework).

#### Backend Security ####

The Spring Security module was used to secure the REST backend (these [guidelines](https://www.owasp.org/index.php/REST_Security_Cheat_Sheet) are in general applied). The application can be made to run in HTTPS-only mode via a server parameter, meaning no pages will be served if the user tries to access it via HTTP.

The Spring Security Form Login mode was used, with fallback to HTTP-Basic Authentication for non-browser based HTTP clients. Protection is in-place against CSRF ([cross-site request forgery](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29)).

Frontend validations are for user convenience only, and where also made on the backend. The use of Angular gives good protection against common problems like [cross-site scripting or HTML injection](https://docs.angularjs.org/misc/faq). The queries on the backend are made using either named queries or the criteria API, which gives good protection against SQL injection.

The password policy is of at least 6 characters with minimum one lower case, one upper case and one numeric. The passwords are not stored in the database in plain text but in a digested form, using the Spring Security [Bcrypt](http://docs.spring.io/autorepo/docs/spring-security/3.2.0.RELEASE/apidocs/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.html) password encoder (transparently includes a salt).

#### REST API ####

The REST API of the backend is composed of 3 services:

##### Authentication Service #####

Url |Verb | Description
--------------|------------- | -------------
/authenticate |POST | authenticates the user
/logout |POST | ends the current session


##### User Service #####

Url |Verb | Description
--------------|------------- | -------------
/user |GET | retrieves info for the currently logged-in user (number of calories of today, etc.)
/user| PUT| Used to save the user max calories per day
/user|POST| creates a new user



##### Meal Service #####

Url |Verb | Description
--------------|------------- | -------------
/meal |GET | searches meals for the current user by date/time
/meal|POST|saves a modified set of meals, might included new ones
/meal|DELETE| deletes a set of meals




### Testing code coverage ###

This snapshot shows the test code coverage for the app package:

![alt Calories Tracker test Code Coverage](http://d2huq83j2o5dyd.cloudfront.net/CodeCoverage.png)


### How to run the project against a non-in-memory database ###

This command starts the application with a local postgresql database:

mvn clean install tomcat7:run-war -Dspring.profiles.active=development

### How to run the project in HTTPS-only mode ###

The application can be started in HTTPS only mode by using the flag httpsOnly=true. This works in both modes, this is an example of how to start the application in test mode and HTTPS only:

mvn clean install tomcat7:run-war -Dspring.profiles.active=test -DhttpsOnly=true

The project can be accessed via this URL:

https://localhost:8443/

A warning message is displayed because the test certificate is not accepted by the browser, by accepting the certificate the login page is then displayed.
16 changes: 16 additions & 0 deletions bower.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "spring-mvc-angularjs-calories-tracker",
"version": "1.0",
"authors": [
"jhades.dev@gmail.com"
],
"description": "Sample Spring MVC | AngularJs project - Calories tracker",
"dependencies": {
"angular": "1.3.8",
"angular-messages": "1.3.8",
"lodash": "2.4.1",
"pure": "0.5.0",
"requirejs": "2.1.15",
"spring-security-csrf-token-interceptor": "0.1.5"
}
}
Binary file added other/keystore.jks
Binary file not shown.
40 changes: 40 additions & 0 deletions other/test-data.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

insert into users values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546013', 0, 'test@test.com', 650, 'xpto', 'test123');

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546000', 0, 1000, '2015-01-01', '1 - Mitraillette', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546001', 0, 2000, '2015-01-01', '1 - Eggplant Parmesan', '19:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546002', 0, 1000, '2015-01-02', '2 - Chickpea with roasted cauliflower', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546003', 0, 2000, '2015-01-02', '2 - Chicken Stew with Turnips & Mushrooms', '19:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546004', 0, 1000, '2015-01-03', '3 - Rosemary Lentils & Greens on Toasted Bread', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546005', 0, 2000, '2015-01-03', '3 - Salmon Cakes with Olives, Lemon & Dill', '19:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546006', 0, 1000, '2015-01-04', '4 - Cowboy Beef & Bean Chili', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546007', 0, 2000, '2015-01-04', '4 - Duck Chiles Rellenos', '19:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546008', 0, 1000, '2015-01-05', '5 - Brussels Sprout & Potato Hash', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546009', 0, 2000, '2015-01-05', '5 - Creamy Green Chile Chicken Soup', '19:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546010', 0, 1000, '2015-01-06', '6 - Duck Chiles Rellenos', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546011', 0, 2000, '2015-01-06', '6 - Apricot-Chile Glazed Salmon', '19:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546012', 0, 1000, '2015-01-07', '7 - Creamy Mustard Chicken', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546013', 0, 2000, '2015-01-07', '7 - Grape Chutney', '19:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546014', 0, 1000, '2015-01-08', '8 - Broccoli Rabe', '12:00', 1);

insert into meals values(nextval ('hibernate_sequence'), 'de305d54-75b4-431b-adb2-eb6b9e546015', 0, 1000, '2015-01-08', '8 - Moules Frites', '19:00', 1);






210 changes: 210 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.spring.mvc.angularjs</groupId>
<artifactId>calories-tracker</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>calories-tracker Maven Webapp</name>

<properties>
<java-version>1.8</java-version>
<org.springframework-version>4.1.3.RELEASE</org.springframework-version>
<spring-security-version>3.2.5.RELEASE</spring-security-version>
<hibernate.version>4.3.7.Final</hibernate.version>
<org.slf4j-version>1.6.1</org.slf4j-version>
<jackson-version>2.4.4</jackson-version>
<postgres.driver.version>9.3-1100-jdbc41</postgres.driver.version>
</properties>


<dependencies>

<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework-version}</version>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgres.driver.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>

<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>

<!-- Spring security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security-version}</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security-version}</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security-version}</version>
</dependency>

<dependency>
<groupId>com.allanditzel</groupId>
<artifactId>spring-security-csrf-token-filter</artifactId>
<version>1.1</version>
</dependency>

<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>

<!-- Jackson JSON Processor -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>

<!-- servlet container provided dependencies -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>7.0.30</version>
<scope>provided</scope>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
</dependency>

</dependencies>

<build>
<finalName>calories-tracker</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>install</id>
<phase>install</phase>
<goals>
<goal>sources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<path>/</path>
<httpsPort>8443</httpsPort>
<keystoreFile>${basedir}/other/keystore.jks</keystoreFile>
<keystorePass>secret</keystorePass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading

0 comments on commit 01caa8a

Please sign in to comment.