├── .gitignore ├── .travis.yml ├── Jenkinsfile ├── LICENSE ├── README.md ├── Vagrantfile ├── angular-spring-boot-webapp ├── .bowerrc ├── .jshintrc ├── Gruntfile.js ├── bower.json ├── karma.conf.ci.js ├── karma.conf.js ├── lombok.config ├── package.json ├── pom.xml ├── serenity.properties └── src │ ├── main │ ├── docker │ │ ├── mysql │ │ │ ├── .dockerignore │ │ │ ├── Dockerfile │ │ │ └── conf.yml │ │ └── ngspring │ │ │ ├── .dockerignore │ │ │ ├── Dockerfile │ │ │ ├── bootstrap.sh │ │ │ ├── conf.yml │ │ │ └── run.sh │ ├── frontend │ │ ├── .buildignore │ │ ├── .htaccess │ │ ├── 404.html │ │ ├── images │ │ │ └── angularjs-logo.png │ │ ├── index.html │ │ ├── robots.txt │ │ ├── scripts │ │ │ ├── app.js │ │ │ ├── controllers │ │ │ │ ├── ctrl.about.js │ │ │ │ ├── ctrl.event.js │ │ │ │ ├── ctrl.main.js │ │ │ │ └── ctrl.nav.js │ │ │ ├── services │ │ │ │ ├── srv.auth.js │ │ │ │ ├── srv.domain.js │ │ │ │ ├── srv.errorHandling.js │ │ │ │ ├── srv.event.js │ │ │ │ ├── srv.redirect.js │ │ │ │ └── srv.user.js │ │ │ └── util │ │ │ │ └── proto.string.js │ │ ├── styles │ │ │ ├── _mixins.scss │ │ │ ├── _theme.scss │ │ │ ├── font-awesome.min.css │ │ │ └── main.scss │ │ ├── templates │ │ │ └── user-management.html │ │ └── views │ │ │ ├── about.html │ │ │ ├── event.html │ │ │ └── main.html │ ├── java │ │ └── ngSpring │ │ │ └── demo │ │ │ ├── AngularSpringApplication.java │ │ │ ├── controllers │ │ │ ├── EventController.java │ │ │ ├── LoginController.java │ │ │ └── UserController.java │ │ │ ├── domain │ │ │ ├── AbstractBaseEntity.java │ │ │ ├── DTO.java │ │ │ ├── dto │ │ │ │ ├── EventDTO.java │ │ │ │ ├── UserDTO.java │ │ │ │ ├── UserProfileDTO.java │ │ │ │ └── UserRoleDTO.java │ │ │ └── entities │ │ │ │ ├── Event.java │ │ │ │ └── User.java │ │ │ ├── errorhandling │ │ │ ├── GlobalControllerExceptionHandler.java │ │ │ └── ValidationMessage.java │ │ │ ├── exceptions │ │ │ ├── BusinessException.java │ │ │ ├── EntityNotFoundException.java │ │ │ └── ValidationException.java │ │ │ ├── mvc │ │ │ └── MvcConfig.java │ │ │ ├── repositories │ │ │ ├── EventRepository.java │ │ │ └── UserRepository.java │ │ │ ├── security │ │ │ ├── AngularSpringAuthenticationProvider.java │ │ │ ├── AppSecurityConfiguration.java │ │ │ └── AuthenticationSecurity.java │ │ │ ├── services │ │ │ └── impl │ │ │ │ └── UserDetailsServiceImpl.java │ │ │ ├── transformer │ │ │ ├── GenericTransformer.java │ │ │ ├── Transformer.java │ │ │ └── impl │ │ │ │ ├── EventTransformer.java │ │ │ │ ├── UserProfileTransformer.java │ │ │ │ └── UserTransformer.java │ │ │ ├── util │ │ │ └── Message.java │ │ │ └── validation │ │ │ ├── AbstractBeanValidator.java │ │ │ ├── DateRangeValidator.java │ │ │ ├── TimeRangeValidator.java │ │ │ ├── ValidateDateRange.java │ │ │ └── ValidateTimeRange.java │ └── resources │ │ ├── ValidationMessages_de.properties │ │ ├── application-heroku.properties │ │ ├── application-test.properties │ │ ├── application.properties │ │ ├── db │ │ └── migration │ │ │ └── V1_0_01__Initial_db_version.sql │ │ └── templates │ │ └── login.html │ └── test │ ├── frontend │ ├── .jshintrc │ ├── mocks │ │ ├── mock.app.js │ │ ├── mock.srv.auth.js │ │ ├── mock.srv.event.js │ │ ├── mock.srv.redirect.js │ │ └── mock.srv.user.js │ └── spec │ │ ├── controllers │ │ ├── ctrl.eventSpec.js │ │ ├── ctrl.mainSpec.js │ │ └── ctrl.navSpec.js │ │ └── services │ │ ├── srv.authSpec.js │ │ ├── srv.domainEventSpec.js │ │ ├── srv.domainValidationSpec.js │ │ ├── srv.errorHandlingSpec.js │ │ └── srv.eventSpec.js │ ├── java │ └── ngSpring │ │ └── demo │ │ ├── AngularSpringApplicationTest.java │ │ ├── domain │ │ ├── EventDomainTest.java │ │ ├── UserDomainTest.java │ │ ├── fixtures │ │ │ ├── EventFixture.java │ │ │ └── UserFixture.java │ │ └── validation │ │ │ ├── EventValidationTest.java │ │ │ └── UserValidationTest.java │ │ ├── errorhandling │ │ └── ControllerErrorHandlingTestBase.java │ │ ├── integration │ │ ├── rest │ │ │ └── EventControllerIT.java │ │ └── ui │ │ │ ├── LoginSerenityIT.java │ │ │ ├── pages │ │ │ ├── CmsPage.java │ │ │ └── LoginPage.java │ │ │ ├── steps │ │ │ └── LoginSteps.java │ │ │ └── util │ │ │ └── AbstractSerenityITTestBase.java │ │ ├── transformer │ │ ├── EventTransformerTest.java │ │ └── UserTransformerTest.java │ │ └── util │ │ ├── MockedMvcTestBase.java │ │ ├── RestITBase.java │ │ └── ValidationTestBase.java │ └── resources │ ├── setup │ └── test.sql │ └── testdata │ └── login.csv ├── install.sh ├── pom.xml └── sample.png /.gitignore: -------------------------------------------------------------------------------- 1 | angular-spring-boot-webapp/tmp/ 2 | angular-spring-boot-webapp/.tmp/ 3 | angular-spring-boot-webapp/.sass-cache/ 4 | angular-spring-boot-webapp/bower_components/ 5 | angular-spring-boot-webapp/node/ 6 | angular-spring-boot-webapp/node_modules/ 7 | angular-spring-boot-webapp/src/main/resources/static/* 8 | 9 | # Eclipse files 10 | **/.project 11 | **/.settings/ 12 | **/.classpath 13 | *.class 14 | **/target 15 | **/bin 16 | **/.springBeans 17 | Procfile 18 | 19 | # IDEA files 20 | .idea/* 21 | *.iml 22 | *.ipr 23 | *.iws 24 | *.log 25 | 26 | # Vagrant 27 | .vagrant/* 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | 4 | services: 5 | - docker 6 | 7 | language: java 8 | jdk: 9 | - oraclejdk8 10 | 11 | install: 12 | - export MAVEN_SKIP_RC=true 13 | - export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=512m" 14 | - "export PATH=`pwd`/bin:$PATH" 15 | - echo $HOME 16 | - "export DISPLAY=:99.0" 17 | - "sh -e /etc/init.d/xvfb start" 18 | 19 | script: 20 | - export DISPLAY=:99.0 && mvn clean test 21 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | node { 2 | env.JAVA_HOME = tool 'jdk-8-oracle' 3 | def mvnHome = tool 'Maven 3.3.1' 4 | env.PATH="${env.JAVA_HOME}/bin:${mvnHome}/bin:${env.PATH}" 5 | 6 | stage 'Checkout' 7 | git url: 'https://github.com/hypery2k/angular-spring-boot-sample.git' 8 | 9 | stage 'Build' 10 | sh "${mvnHome}/bin/mvn clean package" 11 | 12 | stage 'Unit-Tests' 13 | sh "${mvnHome}/bin/mvn test" 14 | 15 | stage 'Integration-Tests' 16 | wrap([$class: 'Xvfb']) { 17 | sh "${mvnHome}/bin/mvn -Pdocker clean verify" 18 | } 19 | 20 | step([ 21 | $class: 'ArtifactArchiver', 22 | artifacts: '**/target/*.jar', 23 | fingerprint: true 24 | ]) 25 | step([ 26 | $class: 'JUnitResultArchiver', 27 | testResults: 'angular-spring-boot-webapp/target/surefire-reports/TEST*.xml,target/failsafe-reports/TEST*.xml' 28 | ]) 29 | publishHTML(target: [ 30 | reportDir:'angular-spring-boot-webapp/target/site/serenity/', 31 | reportFiles:'index.html', 32 | reportName:'Serenity Test Report', 33 | keepAll:true, 34 | alwaysLinkToLastBuild:true, 35 | allowMissing: false 36 | ]) 37 | 38 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Martin Reinhardt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Spring Demo Application 2 | [![Build Status](https://travis-ci.org/hypery2k/angular-spring-boot-sample.svg)](https://travis-ci.org/hypery2k/angular-spring-boot-sample) 3 | 4 | ## Development-Setup 5 | 6 | ### Setup 7 | 8 | * Install JDK 8+ and Maven 3.3+ 9 | * Install MySQL5.5+ Server 10 | * Install NodeJS and npm (http://nodejs.org/download/) 11 | * Install Build Tools 12 | ```bash 13 | $ sudo npm install -g bower grunt-cli karma 14 | ``` 15 | * Install [docker](http://docs.docker.com) (optional) 16 | 17 | 18 | #### local setup 19 | 20 | Install MySQL and run the following SQL: 21 | ``` 22 | CREATE USER 'ngspring'@'localhost' IDENTIFIED BY 'password'; 23 | GRANT ALL PRIVILEGES ON * . * TO 'ngspring'@'localhost'; 24 | CREATE DATABASE NGSPRING; 25 | ``` 26 | 27 | Run maven 28 | 29 | ```bash 30 | $ mvn clean install idea:idea eclipse:eclipse 31 | ``` 32 | 33 | Projects can now imported in your favourite IDE 34 | 35 | ### Development 36 | 37 | 38 | 1. start the backend: 39 | 40 | ```bash 41 | $ vagrant up 42 | $ cd angular-spring-boot-webapp 43 | $ mvn spring-boot:run 44 | ``` 45 | 46 | 2. start the frontend: 47 | 48 | ```bash 49 | $ cd angular-spring-boot-webapp 50 | $ npm start 51 | ``` 52 | 53 | Browser now opens [localhost:9000](http://localhost:9000) and you can add some events ;) 54 | ![](sample.png) 55 | 56 | 57 | >Note: 58 | Any changes in the frontend will be lead to a reload in the browser 59 | 60 | Backend is available at [](http://localhost:9080) with user/password 61 | 62 | API is available at [](http://localhost:9080/swagger-ui.html) 63 | 64 | ### Docker 65 | 66 | #### Run 67 | 68 | ```bash 69 | $ mvn -Pdocker spring-boot:run 70 | ``` -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | config.vm.box = "precise64" 9 | config.vm.box_url = "http://files.vagrantup.com/precise64.box" 10 | config.vm.network :forwarded_port, guest: 3306, host: 33060 11 | config.vm.provision :shell, :path => "install.sh" 12 | config.vm.synced_folder "./angular-spring-boot-webapp/src/test/resources/setup", "/setup", :mount_options => ["dmode=777", "fmode=666"] 13 | config.vm.synced_folder ".", "/vagrant", :mount_options => ["dmode=777", "fmode=666"] 14 | config.vm.network "private_network", ip: "33.33.33.10" 15 | end 16 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "globals": { 21 | "app": false, 22 | "angular": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngSpring", 3 | "version": "0.2.0", 4 | "description": "ngSpring app", 5 | "dependencies": { 6 | "angular": "1.4.9", 7 | "angular-animate": "1.4.9", 8 | "angular-block-ui": "0.2.0", 9 | "angular-bootstrap": "0.13.0", 10 | "angular-cookies": "1.4.9", 11 | "angular-invocation-handler": "1.3.2", 12 | "angular-resource": "1.4.9", 13 | "angular-route": "1.4.9", 14 | "angular-sanitize": "1.4.9", 15 | "angular-touch": "1.4.9", 16 | "angular-translate": "2.7.2", 17 | "bootstrap-sass-official": "3.2.0", 18 | "compass-mixins": "1.0.2", 19 | "ng-table": "0.8.3" 20 | }, 21 | "devDependencies": { 22 | "angular-mocks": "1.4.9" 23 | }, 24 | "resolutions": { 25 | "angular": "1.4.9", 26 | "angular-sanitize": "1.4.9", 27 | "angular-animate": "1.4.9" 28 | }, 29 | "appPath": "src/main/frontend", 30 | "bowerPath": "bower_components", 31 | "testPath": "src/test/frontend", 32 | "resPath": "src/main/resources", 33 | "distPath": "target/dist", 34 | "docPath": "target/docs" 35 | } 36 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/karma.conf.ci.js: -------------------------------------------------------------------------------- 1 | var baseConfig = require('./karma.conf.js'); 2 | 3 | module.exports = function (config) { 4 | // Load base config 5 | baseConfig(config); 6 | 7 | // Override base config 8 | config.set({ 9 | junitReporter: { 10 | outputDir: 'target/surefire-reports', // results will be saved as $outputDir/$browserName.xml 11 | suite: 'ng-spring-boot' 12 | }, 13 | singleRun: true, 14 | autoWatch: false 15 | }); 16 | }; -------------------------------------------------------------------------------- /angular-spring-boot-webapp/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.12/config/configuration-file.html 3 | // Generated on 2015-06-18 using 4 | // generator-karma 1.0.0 5 | 6 | module.exports = function (config) { 7 | 'use strict'; 8 | 9 | config.set({ 10 | // enable / disable watching file and executing tests whenever any file changes 11 | autoWatch: true, 12 | 13 | // base path, that will be used to resolve files and exclude 14 | basePath: '.', 15 | 16 | // testing framework to use (jasmine/mocha/qunit/...) 17 | // as well as any additional frameworks (requirejs/chai/sinon/...) 18 | frameworks: [ 19 | "jasmine" 20 | ], 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | // bower:js 25 | 'bower_components/jquery/dist/jquery.js', 26 | 'bower_components/es5-shim/es5-shim.js', 27 | 'bower_components/angular/angular.js', 28 | 'bower_components/angular-animate/angular-animate.js', 29 | 'bower_components/angular-block-ui/dist/angular-block-ui.js', 30 | 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js', 31 | 'bower_components/angular-cookies/angular-cookies.js', 32 | 'bower_components/angular-invocation-handler/dist/angular-invocation-handler.js', 33 | 'bower_components/angular-resource/angular-resource.js', 34 | 'bower_components/angular-route/angular-route.js', 35 | 'bower_components/angular-sanitize/angular-sanitize.js', 36 | 'bower_components/angular-touch/angular-touch.js', 37 | 'bower_components/angular-translate/angular-translate.js', 38 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/affix.js', 39 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/alert.js', 40 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/button.js', 41 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/carousel.js', 42 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/collapse.js', 43 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/dropdown.js', 44 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tab.js', 45 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/transition.js', 46 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/scrollspy.js', 47 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/modal.js', 48 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tooltip.js', 49 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/popover.js', 50 | 'bower_components/ng-table/dist/ng-table.min.js', 51 | 'bower_components/angular-mocks/angular-mocks.js', 52 | // endbower 53 | "src/main/frontend/scripts/**/*.js", 54 | "src/test/frontend/mocks/**/*.js", 55 | "src/test/frontend/spec/**/*.js" 56 | ], 57 | 58 | // list of files / patterns to exclude 59 | exclude: [], 60 | 61 | // web server port 62 | port: 9080, 63 | 64 | // Start these browsers, currently available: 65 | // - Chrome 66 | // - ChromeCanary 67 | // - Firefox 68 | // - Opera 69 | // - Safari (only Mac) 70 | // - PhantomJS 71 | // - IE (only Windows) 72 | browsers: [ 73 | "PhantomJS" 74 | ], 75 | 76 | // Which plugins to enable 77 | plugins: [ 78 | "karma-phantomjs-launcher", 79 | "karma-jasmine", 80 | "karma-junit-reporter" 81 | ], 82 | 83 | reporters: [ 84 | "progress", 85 | "junit" 86 | ], 87 | 88 | junitReporter: { 89 | outputDir: 'target', // results will be saved as $outputDir/$browserName.xml 90 | suite: 'ng-spring-boot' 91 | }, 92 | 93 | // Continuous Integration mode 94 | // if true, it capture browsers, run tests and exit 95 | singleRun: false, 96 | 97 | colors: true, 98 | 99 | // level of logging 100 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 101 | logLevel: config.LOG_INFO, 102 | 103 | // Uncomment the following lines if you are using grunt's server to run the tests 104 | // proxies: { 105 | // '/': 'http://localhost:9000/' 106 | // }, 107 | // URL root prevent conflicts with the site root 108 | // urlRoot: '_karma_' 109 | }); 110 | }; 111 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/lombok.config: -------------------------------------------------------------------------------- 1 | lombok.nonNull.exceptionType = IllegalArgumentException 2 | lombok.log.fieldName = LOG -------------------------------------------------------------------------------- /angular-spring-boot-webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngSpring", 3 | "version": "0.2.0", 4 | "description": "ngSpring app", 5 | "license": "MIT", 6 | "repository": {}, 7 | "dependencies": {}, 8 | "devDependencies": { 9 | "bower": "1.7.2", 10 | "findup-sync": "0.3.0", 11 | "grunt": "0.4.5", 12 | "grunt-autoprefixer": "3.0.3", 13 | "grunt-banner": "0.6.0", 14 | "grunt-cli": "0.1.13", 15 | "grunt-concurrent": "2.1.0", 16 | "grunt-connect-proxy": "*", 17 | "grunt-contrib-clean": "0.7.0", 18 | "grunt-contrib-compass": "1.0.4", 19 | "grunt-contrib-concat": "0.5.1", 20 | "grunt-contrib-connect": "0.11.2", 21 | "grunt-contrib-copy": "0.8.2", 22 | "grunt-contrib-cssmin": "0.14.0", 23 | "grunt-contrib-htmlmin": "0.6.0", 24 | "grunt-contrib-imagemin": "1.0.0", 25 | "grunt-contrib-jshint": "0.11.3", 26 | "grunt-contrib-uglify": "0.11.0", 27 | "grunt-contrib-watch": "0.6.1", 28 | "grunt-filerev": "2.3.1", 29 | "grunt-google-cdn": "0.4.3", 30 | "grunt-karma": "0.12.1", 31 | "grunt-newer": "1.1.1", 32 | "grunt-ng-annotate": "1.0.1", 33 | "grunt-ngdocs": "0.2.9", 34 | "grunt-open": "0.2.3", 35 | "grunt-raml2html": "0.3.2", 36 | "grunt-sass": "1.1.0", 37 | "grunt-svgmin": "3.1.2", 38 | "grunt-text-replace": "0.4.0", 39 | "grunt-usemin": "3.1.1", 40 | "grunt-wiredep": "2.0.0", 41 | "imagemin": "4.0.0", 42 | "jasmine-core": "2.3.4", 43 | "jshint-stylish": "1.0.0", 44 | "karma": "0.13.3", 45 | "karma-jasmine": "*", 46 | "karma-junit-reporter": "0.3.1", 47 | "karma-phantomjs-launcher": "*", 48 | "load-grunt-tasks": "3.1.0", 49 | "phantomjs": "1.9.17", 50 | "node-sass": "2.1.1", 51 | "serve-static": "1.10.0", 52 | "time-grunt": "1.0.0", 53 | "vinyl-fs": "2.4.2" 54 | }, 55 | "engines": { 56 | "node": ">=0.10.0" 57 | }, 58 | "scripts": { 59 | "postinstall": "bower install && npm rebuild node-sass && grunt build", 60 | "start": "npm install && grunt serve", 61 | "test": "grunt check && grunt test" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/serenity.properties: -------------------------------------------------------------------------------- 1 | # more details on http://thucydides.info/docs/serenity-staging/#_running_serenity_tests_from_the_command_line 2 | serenity.browser.width = 1280 3 | serenity.browser.height = 1024 4 | serenity.verbose.steps = true 5 | serenity.timeout=3000 6 | serenity.step.delay = 800 7 | serenity.store.html.source = true 8 | serenity.reports.show.step.detail=true 9 | serenity.report.show.manual.tests=false 10 | serenity.report.show.releases=false 11 | serenity.take.screenshots = FOR_EACH_ACTION 12 | webdriver.timeouts.implicitlywait = 5000 13 | webdriver.driver = firefox 14 | webdriver.base.url = http://localhost:9000 15 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/mysql/.dockerignore: -------------------------------------------------------------------------------- 1 | # SPIN 2 | *.trail 3 | */pan 4 | 5 | # Generated by T4 6 | */StonTest.cs 7 | 8 | # MonoDevelop 9 | *.userprefs 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | build/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # MSTest test Results 26 | [Tt]est[Rr]esult*/ 27 | [Bb]uild[Ll]og.* 28 | 29 | *_i.c 30 | *_p.c 31 | *.ilk 32 | *.meta 33 | *.obj 34 | *.pch 35 | *.pdb 36 | *.pgc 37 | *.pgd 38 | *.rsp 39 | *.sbr 40 | *.tlb 41 | *.tli 42 | *.tlh 43 | *.tmp 44 | *.tmp_proj 45 | *.log 46 | *.vspscc 47 | *.vssscc 48 | .builds 49 | *.pidb 50 | *.log 51 | *.scc 52 | 53 | # Visual C++ cache files 54 | ipch/ 55 | *.aps 56 | *.ncb 57 | *.opensdf 58 | *.sdf 59 | *.cachefile 60 | 61 | # Visual Studio profiler 62 | *.psess 63 | *.vsp 64 | *.vspx 65 | 66 | # Guidance Automation Toolkit 67 | *.gpState 68 | 69 | # ReSharper is a .NET coding add-in 70 | _ReSharper*/ 71 | *.[Rr]e[Ss]harper 72 | 73 | # TeamCity is a build add-in 74 | _TeamCity* 75 | 76 | # DotCover is a Code Coverage Tool 77 | *.dotCover 78 | 79 | # NCrunch 80 | *.ncrunch* 81 | .*crunch*.local.xml 82 | 83 | # Installshield output folder 84 | [Ee]xpress/ 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish/ 98 | 99 | # Publish Web Output 100 | *.Publish.xml 101 | *.pubxml 102 | 103 | # NuGet Packages Directory 104 | packages/* 105 | 106 | # Windows Azure Build Output 107 | csx 108 | *.build.csdef 109 | 110 | # Windows Store app package directory 111 | AppPackages/ 112 | 113 | # Others 114 | sql/ 115 | *.Cache 116 | ClientBin/ 117 | [Ss]tyle[Cc]op.* 118 | ~$* 119 | *~ 120 | *.dbmdl 121 | *.[Pp]ublish.xml 122 | *.pfx 123 | *.publishsettings 124 | */bin/* 125 | */obj/* 126 | 127 | # RIA/Silverlight projects 128 | Generated_Code/ 129 | 130 | # Backup & report files from converting an old project file to a newer 131 | # Visual Studio version. Backup files are not needed, because we have git ;-) 132 | _UpgradeReport_Files/ 133 | Backup*/ 134 | UpgradeLog*.XML 135 | UpgradeLog*.htm 136 | 137 | # SQL Server files 138 | App_Data/*.mdf 139 | App_Data/*.ldf 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac crap 156 | .DS_Store 157 | 158 | # Xcode Generated Files 159 | *.xcodeproj/* 160 | DerivedData/* 161 | xcuserdata 162 | *~.nib 163 | *.pbxuser 164 | *.perspective 165 | *.perspectivev2 166 | #!xcuserdata/ 167 | #*.xcuserstate 168 | UserInterfaceState.xcuserstate 169 | *xcshareddata* 170 | 171 | # Vim & Sublime Text 172 | *~ 173 | *.sublime-workspace 174 | *.sublime-project -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/mysql/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:5.6 2 | 3 | ENV MYSQL_ROOT_PASSWORD Passw0rd! 4 | ENV MYSQL_USER ngspring 5 | ENV MYSQL_PASSWORD password 6 | ENV MYSQL_DATABASE NGSPRING 7 | 8 | EXPOSE ${mysql.port} 9 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/mysql/conf.yml: -------------------------------------------------------------------------------- 1 | ports: 2 | - ${mysql.port} 3306 3 | healthChecks: 4 | logPatterns: 5 | - "ready for connections" 6 | tag: ngspring/db:${project.version} 7 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/ngspring/.dockerignore: -------------------------------------------------------------------------------- 1 | # SPIN 2 | *.trail 3 | */pan 4 | 5 | # Generated by T4 6 | */StonTest.cs 7 | 8 | # MonoDevelop 9 | *.userprefs 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | build/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # MSTest test Results 26 | [Tt]est[Rr]esult*/ 27 | [Bb]uild[Ll]og.* 28 | 29 | *_i.c 30 | *_p.c 31 | *.ilk 32 | *.meta 33 | *.obj 34 | *.pch 35 | *.pdb 36 | *.pgc 37 | *.pgd 38 | *.rsp 39 | *.sbr 40 | *.tlb 41 | *.tli 42 | *.tlh 43 | *.tmp 44 | *.tmp_proj 45 | *.log 46 | *.vspscc 47 | *.vssscc 48 | .builds 49 | *.pidb 50 | *.log 51 | *.scc 52 | 53 | # Visual C++ cache files 54 | ipch/ 55 | *.aps 56 | *.ncb 57 | *.opensdf 58 | *.sdf 59 | *.cachefile 60 | 61 | # Visual Studio profiler 62 | *.psess 63 | *.vsp 64 | *.vspx 65 | 66 | # Guidance Automation Toolkit 67 | *.gpState 68 | 69 | # ReSharper is a .NET coding add-in 70 | _ReSharper*/ 71 | *.[Rr]e[Ss]harper 72 | 73 | # TeamCity is a build add-in 74 | _TeamCity* 75 | 76 | # DotCover is a Code Coverage Tool 77 | *.dotCover 78 | 79 | # NCrunch 80 | *.ncrunch* 81 | .*crunch*.local.xml 82 | 83 | # Installshield output folder 84 | [Ee]xpress/ 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish/ 98 | 99 | # Publish Web Output 100 | *.Publish.xml 101 | *.pubxml 102 | 103 | # NuGet Packages Directory 104 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 105 | packages/* 106 | 107 | # Windows Azure Build Output 108 | csx 109 | *.build.csdef 110 | 111 | # Windows Store app package directory 112 | AppPackages/ 113 | 114 | # Others 115 | sql/ 116 | *.Cache 117 | ClientBin/ 118 | [Ss]tyle[Cc]op.* 119 | ~$* 120 | *~ 121 | *.dbmdl 122 | *.[Pp]ublish.xml 123 | *.pfx 124 | *.publishsettings 125 | */bin/* 126 | */obj/* 127 | 128 | # RIA/Silverlight projects 129 | Generated_Code/ 130 | 131 | # Backup & report files from converting an old project file to a newer 132 | # Visual Studio version. Backup files are not needed, because we have git ;-) 133 | _UpgradeReport_Files/ 134 | Backup*/ 135 | UpgradeLog*.XML 136 | UpgradeLog*.htm 137 | 138 | # SQL Server files 139 | App_Data/*.mdf 140 | App_Data/*.ldf 141 | 142 | # ========================= 143 | # Windows detritus 144 | # ========================= 145 | 146 | # Windows image file caches 147 | Thumbs.db 148 | ehthumbs.db 149 | 150 | # Folder config file 151 | Desktop.ini 152 | 153 | # Recycle Bin used on file shares 154 | $RECYCLE.BIN/ 155 | 156 | # Mac crap 157 | .DS_Store 158 | 159 | # Xcode Generated Files 160 | *.xcodeproj/* 161 | DerivedData/* 162 | xcuserdata 163 | *~.nib 164 | *.pbxuser 165 | *.perspective 166 | *.perspectivev2 167 | #!xcuserdata/ 168 | #*.xcuserstate 169 | UserInterfaceState.xcuserstate 170 | *xcshareddata* 171 | 172 | # Vim & Sublime Text 173 | *~ 174 | *.sublime-workspace 175 | *.sublime-project -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/ngspring/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | 3 | WORKDIR / 4 | 5 | RUN apt-get update && apt-get install -y mysql-client 6 | 7 | ADD ${project.build.finalName}.jar / 8 | ADD run.sh / 9 | 10 | RUN chmod +x run.sh 11 | 12 | ENTRYPOINT /run.sh ${project.build.finalName} 13 | 14 | EXPOSE ${server.port} 15 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/ngspring/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # restart mysql 4 | /etc/init.d/mysql restart 5 | 6 | # run spring boot 7 | 8 | java -jar /tmp/ng-spring-boot.jar --server.port=40080 2> boot-error.log 1> boot-info.log -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/ngspring/conf.yml: -------------------------------------------------------------------------------- 1 | # additional data require to create the Docker image 2 | packaging: 3 | # files to add to the build, usually used with ADD in the Dockerfile 4 | add: 5 | - target/${project.build.finalName}.jar 6 | # optional list of port to expose on the host 7 | ports: 8 | - ${server.port} 9 | healthChecks: 10 | pings: 11 | - url: http://localhost:${server.port}/ 12 | timeout: 60000 13 | links: 14 | - mysql:db 15 | # how long in milliseconds to sleep after start-up (default 0) 16 | sleep: 5000 17 | # tag to use for images 18 | tag: ngspring/app:${project.version} -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/docker/ngspring/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -version 3 | 4 | serverPort=${server.port} 5 | dbUrl="jdbc:mysql://"$DB_PORT_${mysql.port}_TCP_ADDR"/NGSPRING?useUnicode=true&characterEncoding=utf8" 6 | 7 | 8 | echo "App name: "$1 9 | echo "Server Port: "$serverPort 10 | echo "DB URL: "$dbUrl 11 | 12 | echo "starting app" 13 | java -Djava.security.egd=file:/dev/./urandom -jar /$1.jar --flyway.url=${dbUrl} --spring.datasource.url=${dbUrl} --server.port=${serverPort} 2> /boot-error.log 1> /boot-info.log 14 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/images/angularjs-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypery2k/angular-spring-boot-sample/02073c15169a5344722878efc781aed7dd75a327/angular-spring-boot-webapp/src/main/frontend/images/angularjs-logo.png -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ 'GENERAL_TITLE' | translate }} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 |
25 | 52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 | 60 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc overview 3 | * @name ngSpringBootApp 4 | * @description 5 | * 6 | * Main module of the application. 7 | */ 8 | 9 | /*global app: true*/ 10 | var app = angular.module('ngSpringBootApp', [ 11 | 'ngAnimate', 12 | 'ngCookies', 13 | 'ngResource', 14 | 'ngRoute', 15 | 'ngSanitize', 16 | 'ui.bootstrap', 17 | 'blockUI', 18 | 'pascalprecht.translate', 19 | 'ngTouch', 20 | 'ngTable', 21 | 'ngIH.core', 22 | 'ngIH.ui' 23 | ]); 24 | 25 | 26 | app.config(function ($provide, ngIHServiceProvider, ngIHConfig) { 27 | 'use strict'; 28 | 29 | // enable UI feedback attach 30 | ngIHConfig.feedbackAttach = true; 31 | ngIHConfig.customErrorHandler = 'ErrorHandlingService'; 32 | // decorate the mentioned [services] with automatic error handling. 33 | ngIHServiceProvider.decorate($provide, ['EventService']); 34 | ngIHServiceProvider.decorate($provide, ['UserService']); 35 | }); 36 | 37 | 38 | app.config(function ($routeProvider) { 39 | 'use strict'; 40 | 41 | $routeProvider 42 | .when('/home', { 43 | templateUrl: 'views/main.html', 44 | controller: 'MainCtrl' 45 | }) 46 | .when('/event', { 47 | templateUrl: 'views/event.html', 48 | controller: 'EventCtrl' 49 | }) 50 | .otherwise({ 51 | redirectTo: '/event' 52 | }); 53 | }); 54 | 55 | app.config(function ($translateProvider) { 56 | 'use strict'; 57 | 58 | $translateProvider.translations('de_DE', { 59 | GENERAL_LOADING: 'Lade', 60 | GENERAL_TITLE: 'ngSpringBoot', 61 | GENERAL_EDIT: 'Bearbeiten', 62 | GENERAL_DELETE: 'Löschen', 63 | GENERAL_ALL: 'Alle', 64 | GENERAL_NEW: 'Neu', 65 | GENERAL_SAVE: 'Speichern', 66 | GENERAL_CANCEL: 'Abbrechen', 67 | GENERAL_CLOSE: 'Schließen', 68 | GENERAL_TODAY: 'Heute', 69 | GENERAL_BEGIN: 'Beginn', 70 | GENERAL_END: 'Ende', 71 | GENERAL_YES: 'Ja', 72 | GENERAL_NO: 'Nein', 73 | GENERAL_CHOOSE: 'Bitte auswählen', 74 | GENERAL_ADD: 'Neuer Eintrag', 75 | GENERAL_UPLOAD: 'Hochladen', 76 | 77 | MAIN_HEADLINE: 'AngularJS Spring Boot Demo', 78 | MAIN_GREETING: 'Demo Anwendung die die Verwendung von AngularJS und SpringBoot zeigt', 79 | MAIN_FIRST_HEADLINE: 'AngularJS', 80 | MAIN_FIRST_TEXT: 'AngularJS – oft einfach als Angular bezeichnet – ist ein Open-Source-Framework des US-amerikanischen Unternehmens Google Inc. Mit AngularJS kann man in HTML und JavaScript Single-page-Webanwendungen nach einem Model View ViewModel-Muster erstellen. Die Softwareentwicklung und das Komponententesten können damit vereinfacht werden.', 81 | MAIN_SECOND_HEADLINE: 'SpringBoot', 82 | MAIN_SECOND_TEXT: 'Spring Boot heißt das neue Projekt der Entwickler des Spring Frameworks für die Java-Plattform. Spring Boot soll also eine Art Einstiegspunkt für Entwickler bieten, von dem aus sie auf die Funktionen von Spring zugreifen können, um damit Anwendungen und Dienste zu erstellen.', 83 | MAIN_THIRD_HEADLINE: 'Swagger', 84 | MAIN_THIRD_TEXT: 'm Funktionalität und Möglichkeiten einer API zu überschauen, ist die Qualität der API-Dokumentation ausschlaggebend. Mit Swagger UI behalten wir immer den Überblick, können andere Personen die API bereitstellen oder häufig genutzte Schnittstellen dokumentieren.', 85 | 86 | EVENT_HEADLINE: 'Eventdaten', 87 | EVENT_SELECTION_HEADING: 'Eventdaten auswählen', 88 | EVENT_CREATION_DATE: 'Eintragung', 89 | EVENT_DESCRIPTION: 'Ereignis', 90 | EVENT_DESCRIPTION_REQUIRED: 'Die Ereignisbezeichnung ist erforderlich', 91 | EVENT_START_DATE: 'Beginn', 92 | EVENT_END_DATE: 'Ende', 93 | EVENT_EDIT_HEADING: 'Eventdaten bearbeiten', 94 | EVENT_DELETE_SUCCESS: 'Event wurde gelöscht.', 95 | EVENT_SAVE_SUCCESS: 'Event wurde erfolgreich gespeichert.', 96 | 97 | LOGIN_HEADLINE: 'Willkommen zur Angular SpringBoot Demo', 98 | LOGIN_GREETING: 'Melden Sie sich an um das Portal zu nutzen.', 99 | LOGIN_FORM_USERNAME: 'Nutzer', 100 | LOGIN_FORM_PASSWORD: 'Passwort', 101 | LOGIN_FORM_SUBMIT: 'Anmelden', 102 | LOGIN_ERROR: 'Anmeldefehler. Bitte versuchen Sie erneut sich anzumelden.', 103 | LOGIN_DENIED: 'Anmeldefehler. Der angebene Nutzer ist nicht berechtigt für die Oberfläche.', 104 | NAV_EVENTS: 'Events', 105 | NAV_LOGOUT: 'Abmelden', 106 | 107 | LOGOUT_ERROR: 'Fehler beim Abmelden', 108 | 109 | HTTP_STATUS_CODE_0: 'Der Server antwortet nicht.', 110 | HTTP_STATUS_CODE_400: 'Validierungsfehler.', 111 | HTTP_STATUS_CODE_403: 'Zugriff verweigert.', 112 | HTTP_STATUS_CODE_404: 'Dieser Datensatz existiert nicht.', 113 | HTTP_STATUS_CODE_405: 'Ungültige Anfrage.', 114 | HTTP_STATUS_CODE_409: 'Es besteht ein Datenkonflikt.', 115 | HTTP_STATUS_CODE_500: 'Unbekannter Serverfehler.', 116 | 117 | // bean validation messages 118 | 'VALIDATION_ERROR_validation.jpa.unique': 'Ein ähnlicher Datensatz existiert bereits', 119 | 'VALIDATION_ERROR_validation.jpa.unknown': 'Prüfen Sie ihre Daten', 120 | 'VALIDATION_ERROR_validation.passwords_not_match': 'Die Passwordbestätigung ist nicht korrekt', 121 | 'VALIDATION_ERROR_validation.date.range_error': 'Das Startdatum liegt nicht vor dem Enddatum.', 122 | 'VALIDATION_ERROR_validation.time.range_error': 'Der Beginn der Uhrzeit liegt nicht vor dem Ende.', 123 | 'VALIDATION_ERROR_javax.validation.constraints.NotNull.message': 'Bitte füllen Sie alle Pflichtfelder aus. ', 124 | 'VALIDATION_ERROR_javax.validation.constraints.Size.message': 'Die maximale Länge wurde überschritten.', 125 | }); 126 | 127 | $translateProvider.preferredLanguage('de_DE'); 128 | $translateProvider.useSanitizeValueStrategy('escape'); 129 | }); 130 | 131 | app.config(function (blockUIConfig) { 132 | 'use strict'; 133 | 134 | // Change the default overlay message 135 | blockUIConfig.message = 'Lade ...'; 136 | 137 | // Change the default delay to 100ms before the blocking is visible 138 | blockUIConfig.delay = 10; 139 | }); 140 | 141 | app.run(function ($rootScope, AuthenticationService) { 142 | 'use strict'; 143 | 144 | // register listener to watch route changes 145 | $rootScope.$on('$routeChangeStart', function (event, next, current) { 146 | AuthenticationService.validateUser(event, next, current); 147 | }); 148 | }); 149 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/controllers/ctrl.about.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc function 3 | * @name ngSpringBootApp.controller:AboutCtrl 4 | * @description 5 | * About controller of the normalizerBaseDateHours 6 | */ 7 | app.controller('AboutCtrl', function ($scope) { 8 | 'use strict'; 9 | 10 | $scope.awesomeThings = [ 11 | 'HTML5 Boilerplate', 12 | 'AngularJS', 13 | 'Karma' 14 | ]; 15 | }); 16 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/controllers/ctrl.event.js: -------------------------------------------------------------------------------- 1 | app.controller('EventCtrl', function ($scope, $log, $translate, ngTableParams, feedbackUI, EventService, Domain) { 2 | 'use strict'; 3 | 4 | // internal functions 5 | 6 | function init() { 7 | $scope.currentEvent = false; 8 | loadEvents(); 9 | } 10 | 11 | function loadEvents() { 12 | EventService.listAllEvents(function (events) { 13 | $scope.allEvents = []; 14 | if (events) { 15 | for (var i = 0; i < events.length; i++) { 16 | 17 | $scope.allEvents.push(events[i]); 18 | } 19 | } 20 | // init pagination 21 | $scope.tableParams = new ngTableParams({ // jshint ignore:line 22 | page: 1, // show first page 23 | count: 10 // count per page 24 | }, { 25 | total: $scope.allEvents.length, // length of data 26 | getData: function ($defer, params) { 27 | $defer.resolve($scope.allEvents.slice((params.page() - 1) * params.count(), params.page() * params.count())); 28 | } 29 | }); 30 | }); 31 | } 32 | 33 | // init the controller 34 | init(); 35 | 36 | // view API 37 | 38 | // datepicker ui bootstrap 39 | $scope.openStartDate = function ($event) { 40 | $event.preventDefault(); 41 | $event.stopPropagation(); 42 | $scope.startDateOpened = true; 43 | }; 44 | $scope.openEndDate = function ($event) { 45 | $event.preventDefault(); 46 | $event.stopPropagation(); 47 | $scope.endDateOpened = true; 48 | }; 49 | 50 | $scope.getEvent = function (eventId) { 51 | EventService.getEvent(eventId, function (event) { 52 | $scope.currentEvent = event; 53 | }); 54 | }; 55 | $scope.deleteEvent = function (event) { 56 | EventService.deleteEvent(event, function () { 57 | // success 58 | init(); 59 | $translate('EVENT_DELETE_SUCCESS').then(function (translatedValue) { 60 | feedbackUI.appendInfoMsg(translatedValue); 61 | }); 62 | }); 63 | }; 64 | 65 | $scope.reset = function () { 66 | init(); 67 | }; 68 | 69 | $scope.createEvent = function () { 70 | $scope.currentEvent = Domain.Event.build(); 71 | }; 72 | 73 | $scope.saveEvent = function (form) { 74 | // only submit valid from 75 | if (form.$valid) { 76 | EventService.saveEvent($scope.currentEvent, function () { 77 | // success 78 | init(); 79 | $translate('EVENT_SAVE_SUCCESS').then(function (translatedValue) { 80 | feedbackUI.appendInfoMsg(translatedValue); 81 | }); 82 | }); 83 | } 84 | }; 85 | } 86 | ); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/controllers/ctrl.main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc function 3 | * @name ngSpringBootApp.controller:MainCtrl 4 | * @description 5 | * Main controller of the ngSpringBootApp 6 | */ 7 | app.controller('MainCtrl', function ($scope) { 8 | 'use strict'; 9 | 10 | $scope.awesomeThings = [ 11 | 'HTML5 Boilerplate', 12 | 'AngularJS', 13 | 'Karma' 14 | ]; 15 | }); 16 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/controllers/ctrl.nav.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @ngdoc function 3 | * @name ngSpringBootApp.controller:NavCtrl 4 | * @description 5 | * Login Controller of the navigation bar 6 | */ 7 | app.controller('NavCtrl', function ($rootScope, $scope, $log, $translate, $location, feedbackUI, AuthenticationService) { 8 | 'use strict'; 9 | 10 | $scope.isActive = function (path) { 11 | if ($rootScope.user) { 12 | if ($location.path().substr(0, path.length) === path) { 13 | return true; 14 | } 15 | // add user check here 16 | } else { 17 | return false; 18 | } 19 | return false; 20 | }; 21 | $scope.logout = function () { 22 | AuthenticationService.logout(function (response) { 23 | $rootScope.user = response; 24 | $log.info('Logout complete.'); 25 | }, function () { 26 | $log.error('Logout failure for user.'); 27 | $translate('LOGOUT_ERROR').then(function (translatedValue) { 28 | feedbackUI.appendErrorMsg(translatedValue); 29 | }); 30 | }); 31 | }; 32 | }); 33 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/services/srv.auth.js: -------------------------------------------------------------------------------- 1 | app.factory('AuthenticationService', function ($rootScope, $log, $location, RedirectService, $resource, $http, Domain) { 2 | 'use strict'; 3 | 4 | var currentUser, 5 | whiteListedUrls = [], 6 | resource = $resource('./api/login/user'), 7 | loginHandlerSpring = './login', 8 | logoutHandlerSpring = './logout'; 9 | 10 | function redirectToLoginPage(event, next, current) { 11 | if (event && next && current) { 12 | // already logged in, no redirect needed 13 | } else { 14 | // fallback 15 | RedirectService.redirect(loginHandlerSpring); 16 | } 17 | } 18 | 19 | function validateUser(event, next, current) { 20 | getUserDetails(function (user) { 21 | currentUser = user; 22 | $rootScope.user = user; 23 | if (user.isValid()) { 24 | $log.info('Login revalidation was sucessfull for user ' + user.username); 25 | $rootScope.$broadcast('userUpdate', user); 26 | } else { 27 | // check white listed URLs 28 | if (whiteListedUrls.indexOf(next.loadedTemplateUrl) === -1) { 29 | redirectToLoginPage(event, next, current); 30 | } 31 | } 32 | }, function (user) { 33 | $rootScope.user = user; 34 | redirectToLoginPage(event, next, current); 35 | }); 36 | } 37 | 38 | function getUserDetails(callback, errorCallback) { 39 | return resource.get({}, 40 | function (response) { 41 | $log.info('Got a valid user response'); 42 | callback(Domain.User.build(response.username, response.userId, response.permissions, response.customers)); 43 | }, 44 | function () { 45 | $log.error('Could not read user details.'); 46 | errorCallback(Domain.User.build()); 47 | }); 48 | } 49 | 50 | return { 51 | login: function (credentials, callback, errorCallback) { 52 | $http({ 53 | method: 'POST', 54 | url: loginHandlerSpring, 55 | data: credentials, 56 | transformRequest: function (obj) { 57 | var str = []; 58 | for (var p in obj) { 59 | str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p])); 60 | } 61 | return str.join('&'); 62 | }, 63 | headers: {'Content-Type': 'application/x-www-form-urlencoded'} 64 | }).success(function () { 65 | getUserDetails(callback, errorCallback); 66 | }).error(function () { 67 | $log.error('Got a login error'); 68 | errorCallback(Domain.User.build()); 69 | }); 70 | }, 71 | checkUser: function (callback, errorCallback) { 72 | getUserDetails(callback, errorCallback); 73 | }, 74 | getCurrentUser: function () { 75 | if (!currentUser || !currentUser.customers) { 76 | validateUser(); 77 | } 78 | if (!currentUser.isValid()) { 79 | redirectToLoginPage(); 80 | } 81 | return currentUser; 82 | }, 83 | validateUser: validateUser, 84 | logout: function (callback, errorCallback) { 85 | $http.post(logoutHandlerSpring). 86 | success(function () { 87 | callback(Domain.User.build()); 88 | }). 89 | error(function () { 90 | $log.error('Got a logout error'); 91 | errorCallback(Domain.User.build()); 92 | }); 93 | redirectToLoginPage(); 94 | } 95 | }; 96 | }); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/services/srv.domain.js: -------------------------------------------------------------------------------- 1 | app.factory('Domain', function () { 2 | 'use strict'; 3 | 4 | // reference for base 5 | var referenceBaseDateHours = new Date(1970, 1, 2, 0, 0, 0, 0), 6 | // use for date DTO normalization 7 | normalizerBaseDateHours = new Date(1970, 1, 2, 12, 0, 0, 0); 8 | 9 | // HELPER functions 10 | 11 | function convertDateStringToDate(time) { 12 | if (time) { 13 | var date = new Date(referenceBaseDateHours.getTime()), 14 | res = time.split('-'); 15 | date.setYear(res[0]); 16 | date.setMonth(res[1]); 17 | date.setDate(res[2]); 18 | return date; 19 | } else { 20 | return null; 21 | } 22 | } 23 | 24 | /** 25 | * User Object 26 | */ 27 | function User(username, userId, permissions, customers) { 28 | this.username = username; 29 | this.userId = userId; 30 | this.permissions = permissions; 31 | this.customers = customers; 32 | } 33 | 34 | 35 | //Public object methods, assigned to prototype 36 | 37 | 38 | User.prototype.isValid = function () { 39 | if (this.username && this.username !== 'anonymousUser') { 40 | return true; 41 | } else { 42 | return false; 43 | } 44 | }; 45 | // Static method, assigned to class, e.g. builders 46 | // Instance ('this') is not available in static context 47 | User.build = function (username, userId, permissions, customers) { 48 | return new User(username, userId, permissions, customers); 49 | 50 | }; 51 | 52 | /** 53 | * Event Object 54 | */ 55 | function Event() { 56 | this.eventId = null; 57 | this.eventDescription = null; 58 | this.insertDate = null; 59 | this.startDate = null; 60 | this.endDate = null; 61 | } 62 | 63 | 64 | Event.prototype.convertToDTO = function () { 65 | var date = new Date(normalizerBaseDateHours.getTime()); 66 | this.startDate.setHours(date.getHours()); 67 | this.startDate.setMinutes(date.getMinutes()); 68 | if (this.endDate) { 69 | this.endDate.setHours(date.getHours()); 70 | this.endDate.setMinutes(date.getMinutes()); 71 | } 72 | return this; 73 | }; 74 | 75 | Event.prototype.convertFromDTO = function () { 76 | // easy way to get a clean copy 77 | var copy = Event.build(this); 78 | // convert times 79 | if (copy.startDate) { 80 | copy.startDate = convertDateStringToDate(this.startDate); 81 | } 82 | if (copy.endDate) { 83 | copy.endDate = convertDateStringToDate(this.endDate); 84 | } 85 | return copy; 86 | }; 87 | 88 | function UserProfile(){ 89 | this.userName = null; 90 | this.password = null; 91 | this.passwordConfirmation = null; 92 | this.customerName = null; 93 | this.customerId = null; 94 | this.branchNumber = null; 95 | } 96 | 97 | UserProfile.prototype.isValid = function () { 98 | if(this.userName && this.password && this.passwordConfirmation && this.customerName && this.password === this.passwordConfirmation){ 99 | return true; 100 | } 101 | return false; 102 | }; 103 | 104 | UserProfile.build = function(data){ 105 | if(!data) { 106 | return new UserProfile(); 107 | }else{ 108 | var userProfile = new UserProfile(); 109 | for(var key in data){ 110 | if (key.charAt(0) !== '$' && data.hasOwnProperty(key)) { 111 | userProfile[key] = data[key]; 112 | } 113 | } 114 | return userProfile; 115 | } 116 | }; 117 | 118 | // Static method, assigned to class, e.g. builders 119 | // Instance ('this') is not available in static context 120 | Event.build = function (data) { 121 | if (!data) { 122 | return new Event(); 123 | } else { 124 | var event = new Event(); 125 | for (var key in data) { 126 | if (key.charAt(0) !== '$' && data.hasOwnProperty(key)) { 127 | event[key] = data[key]; 128 | } 129 | } 130 | return event; 131 | } 132 | }; 133 | 134 | 135 | 136 | return { 137 | User: User, 138 | Event: Event, 139 | UserProfile: UserProfile 140 | }; 141 | }); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/services/srv.errorHandling.js: -------------------------------------------------------------------------------- 1 | app.factory('ErrorHandlingService', function ($log, $translate, blockUI) { 2 | 'use strict'; 3 | 4 | function buildValidationMessages(error, status, msg, callback, i) { 5 | var errorDetails = error.data[i]; 6 | $translate('VALIDATION_ERROR_' + errorDetails.messageTemplate).then(function (translatedValue) { 7 | msg = msg + ' ' + translatedValue; 8 | 9 | // replace placeholder if set 10 | if (errorDetails.propertyList) { 11 | msg = msg.format(errorDetails.propertyList); 12 | } 13 | 14 | // callback when complete 15 | if (i === error.data.length - 1) { 16 | $log.debug(status + '=>' + msg); 17 | callback(msg); 18 | } 19 | }, function (err) { 20 | $log.error(err); 21 | callback(msg); 22 | }); 23 | } 24 | 25 | return { 26 | resolve: function (details, callback) { 27 | if (details.error) { 28 | var error = details.error; 29 | var status = details.status; 30 | // read by http code 31 | $translate('HTTP_STATUS_CODE_' + status).then(function (translatedValue) { 32 | var msg = translatedValue; 33 | // handle violation errors 34 | if (status === 400 && error.data && error.data.length) { 35 | for (var i = 0; i < error.data.length; i++) { 36 | blockUI.stop(); 37 | buildValidationMessages(error, status, msg, callback, i); 38 | } 39 | } else { 40 | blockUI.stop(); 41 | $log.debug('Got unkown error status ' + status + ':' + msg); 42 | callback(msg); 43 | } 44 | }); 45 | } 46 | } 47 | }; 48 | }); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/services/srv.event.js: -------------------------------------------------------------------------------- 1 | app.factory('EventService', function ($resource, Domain) { 2 | 'use strict'; 3 | 4 | var eventResource = $resource('/api/events/:eventId', {eventId: '@eventId'}, {update: {method: 'PUT'}}); 5 | 6 | return { 7 | listAllEvents: function (callback, errorCallback) { 8 | return eventResource.query({}, function (events) { 9 | var result = []; 10 | if (events && events.length > 0) { 11 | for (var i = 0; i < events.length; i++) { 12 | result.push(Domain.Event.build(events[i]).convertFromDTO()); 13 | } 14 | } 15 | callback(result); 16 | }, errorCallback); 17 | }, 18 | getEvent: function (eventId, callback, errorCallback) { 19 | return eventResource.get({eventId: eventId}, function (event) { 20 | var result = null; 21 | if (event) { 22 | result = Domain.Event.build(event).convertFromDTO(); 23 | } 24 | callback(result); 25 | }, errorCallback); 26 | }, 27 | saveEvent: function (event, callback, errorCallback) { 28 | if (event.eventId) { 29 | return eventResource.update({eventId: event.eventId}, event.convertToDTO(), callback, errorCallback); 30 | } else { 31 | return eventResource.save({}, event.convertToDTO(), callback, errorCallback); 32 | } 33 | }, 34 | deleteEvent: function (event, callback, errorCallback) { 35 | return eventResource.delete({eventId: event.eventId}, event.convertToDTO(), callback, errorCallback); 36 | } 37 | }; 38 | }); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/services/srv.redirect.js: -------------------------------------------------------------------------------- 1 | app.factory('RedirectService', function ($window) { 2 | 'use strict'; 3 | 4 | return { 5 | redirect: function (path) { 6 | $window.location.href = path; 7 | } 8 | }; 9 | }); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/services/srv.user.js: -------------------------------------------------------------------------------- 1 | app.factory('UserService', function ($resource) { 2 | 'use strict'; 3 | 4 | var userResource = $resource('/api/user/:userId', {userId: '@userId',}, {update: {method: 'PUT'}}); 5 | 6 | return { 7 | saveUser: function (user, callback, errorCallback) { 8 | if (user.userId) { 9 | return userResource.update({userId: user.userId}, user, callback, errorCallback); 10 | } else { 11 | return userResource.save({}, user, callback, errorCallback); 12 | } 13 | }, 14 | deleteUser: function(userId, callback, errorCallback){ 15 | return userResource.delete({userId: userId}, callback, errorCallback); 16 | } 17 | }; 18 | }); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/scripts/util/proto.string.js: -------------------------------------------------------------------------------- 1 | String.prototype.format = function (args) { 2 | 'use strict'; 3 | var str = this; 4 | return str.replace(String.prototype.format.regex, function (item) { 5 | var intVal = parseInt(item.substring(1, item.length - 1)); 6 | var replace; 7 | if (intVal >= 0) { 8 | replace = args[intVal]; 9 | } else if (intVal === -1) { 10 | replace = '{'; 11 | } else if (intVal === -2) { 12 | replace = '}'; 13 | } else { 14 | replace = ''; 15 | } 16 | return replace; 17 | }); 18 | }; 19 | String.prototype.format.regex = new RegExp('{-?[0-9]+}', 'g'); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin vertical-align { 2 | position: relative; 3 | top: 50%; 4 | -webkit-transform: translateY(-50%); 5 | -ms-transform: translateY(-50%); 6 | transform: translateY(-50%); 7 | } 8 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/styles/_theme.scss: -------------------------------------------------------------------------------- 1 | //=== Shared nav styles 2 | $nav-link-padding: 10px 10px !default; 3 | $panel-heading-padding: 10px 10px !default; 4 | 5 | /* Custom page header */ 6 | .navbar { 7 | .navbar-nav > li > a.active { 8 | color: #555555; 9 | background-color: #fff; 10 | border: 1px solid #ddd; 11 | border-bottom-color: transparent; 12 | cursor: default; 13 | margin: 0px; 14 | } 15 | 16 | .navbar-brand { 17 | padding-top: 5px; 18 | padding-bottom: 5px; 19 | 20 | .logo { 21 | width: 35px; 22 | margin-top: 5px; 23 | margin-left: 10px; 24 | 25 | @media screen and (min-width: 767px) { 26 | width: 50px; 27 | margin-top: 0px; 28 | margin-left: 0; 29 | padding-left: 0; 30 | height: 50px; 31 | } 32 | } 33 | } 34 | 35 | } 36 | 37 | .container-fluid { 38 | .container { 39 | .table-responsive { 40 | width: 100%; 41 | border: 0; 42 | margin-left: 2px; 43 | 44 | // pagination 45 | .ng-table-pagination, 46 | .ng-table-counts { 47 | margin: 0px 0px 20px 0px;; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/styles/main.scss: -------------------------------------------------------------------------------- 1 | $icon-font-path: "../fonts/"; 2 | 3 | // bower:scss 4 | @import "bootstrap"; 5 | // endbower 6 | 7 | @import "./font-awesome.min"; 8 | @import "./theme"; 9 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/templates/user-management.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 7 |
8 |
9 |
10 |
11 | 12 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 23 | 26 |
27 |
28 |
-------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/views/about.html: -------------------------------------------------------------------------------- 1 |

This is the about view.

2 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/views/event.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | 5 |
6 | 7 | 8 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | 29 | 30 | 33 | 36 | 39 | 42 | 47 | 52 | 53 |
10 | {{'EVENT_CREATION_DATE'| translate}} 11 | 13 | {{'EVENT_DESCRIPTION'| translate}} 14 | 16 | {{'EVENT_START_DATE'| translate}} 17 | 19 | {{'EVENT_END_DATE'| translate}} 20 | 22 | {{'GENERAL_EDIT'| translate}} 23 | 25 | {{'GENERAL_DELETE'| translate}} 26 |
31 | {{::event.insertDate}} 32 | 34 | {{::event.eventDescription}} 35 | 37 | {{::event.startDate | date: 'dd.MM.yy'}} 38 | 40 | {{::event.endDate | date: 'dd.MM.yy'}} 41 | 43 | 46 | 48 | 51 |
54 |
55 |
56 |
57 |
58 | 62 |
63 |
64 |
65 |
66 | 67 |
68 |
69 |
70 |
71 | 72 | 75 |
76 |
77 |
78 |
79 |
80 |
81 | 82 |
83 |
84 | 85 | 86 |

87 | 93 | 94 | 98 | 99 |

100 |
101 |
102 |
103 |
104 | 105 | 106 |

107 | 113 | 114 | 118 | 119 |

120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | 133 |
134 |
135 |
136 |
137 | 139 |
140 |
141 |
142 |
143 |
144 |
145 |
-------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/frontend/views/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 |

5 | 6 |

7 | 9 |

10 |
11 |
12 | 13 |
14 |

15 |

16 | 17 |

18 |

19 | 20 |

21 |

22 |
23 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/AngularSpringApplication.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo; 2 | 3 | import com.google.common.base.Predicate; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.boot.context.web.SpringBootServletInitializer; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import springfox.documentation.builders.ApiInfoBuilder; 11 | import springfox.documentation.service.ApiInfo; 12 | import springfox.documentation.spi.DocumentationType; 13 | import springfox.documentation.spring.web.plugins.Docket; 14 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 15 | 16 | import static com.google.common.base.Predicates.or; 17 | import static springfox.documentation.builders.PathSelectors.regex; 18 | 19 | //tag::spring-app[] 20 | @SpringBootApplication 21 | @ComponentScan 22 | @EnableAutoConfiguration 23 | //end::spring-app[] 24 | //tag::swagger-docs[] 25 | @EnableSwagger2 26 | //tag::spring-app[] 27 | public class AngularSpringApplication extends SpringBootServletInitializer { 28 | //end::spring-app[] 29 | 30 | @Bean 31 | public Docket ngSpringApi() { 32 | return new Docket(DocumentationType.SWAGGER_2) 33 | .groupName("ng-spring-boot-api") 34 | .apiInfo(apiInfo()) 35 | .select() 36 | .paths(apiPaths()) 37 | .build(); 38 | } 39 | 40 | private Predicate apiPaths() { 41 | return or( 42 | regex("/api/event.*"), 43 | regex("/api/login.*"), 44 | regex("/api/user.*") 45 | ); 46 | } 47 | 48 | private ApiInfo apiInfo() { 49 | return new ApiInfoBuilder() 50 | .title("Angular SpringBoot Demo API") 51 | .description("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum " + 52 | "has been the industry's standard dummy text ever since the 1500s, when an unknown printer " 53 | + "took a " + 54 | "galley of type and scrambled it to make a type specimen book. It has survived not only five " + 55 | "centuries, but also the leap into electronic typesetting, remaining essentially unchanged. " + 56 | "It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum " + 57 | "passages, and more recently with desktop publishing software like Aldus PageMaker including " + 58 | "versions of Lorem Ipsum.") 59 | .termsOfServiceUrl("https://github.com/hypery2k/angular-spring-boot-sample") 60 | .contact("contact@martinreinhardt-online.de") 61 | .license("MIT") 62 | .licenseUrl("https://raw.githubusercontent.com/hypery2k/angular-spring-boot-sample/master/LICENSE") 63 | .version("0.2.0") 64 | .build(); 65 | } //end::swagger-docs[] 66 | 67 | //tag::spring-app[] 68 | public static void main(String[] args) { 69 | SpringApplication.run(AngularSpringApplication.class, args); 70 | } 71 | //end::spring-app[] 72 | } 73 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/controllers/EventController.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.controllers; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import io.swagger.annotations.ApiResponse; 6 | import io.swagger.annotations.ApiResponses; 7 | import ngSpring.demo.domain.dto.EventDTO; 8 | import ngSpring.demo.domain.entities.Event; 9 | import ngSpring.demo.exceptions.BusinessException; 10 | import ngSpring.demo.exceptions.EntityNotFoundException; 11 | import ngSpring.demo.exceptions.ValidationException; 12 | import ngSpring.demo.repositories.EventRepository; 13 | import ngSpring.demo.transformer.impl.EventTransformer; 14 | import ngSpring.demo.util.Message; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.data.domain.Sort; 17 | import org.springframework.data.domain.Sort.Order; 18 | import org.springframework.http.HttpStatus; 19 | import org.springframework.http.ResponseEntity; 20 | import org.springframework.web.bind.annotation.*; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Iterator; 24 | import java.util.List; 25 | 26 | 27 | //tag::swagger-docs[] 28 | @Api(basePath = "/api/events", value = "Events", description = "Operations with Events", produces = "application/json") 29 | //end::swagger-docs[] 30 | //tag::events-rest-api[] 31 | @RestController 32 | @RequestMapping(value = "/api/events") 33 | @ResponseStatus(HttpStatus.OK) 34 | public class EventController { 35 | //end::events-rest-api[] 36 | 37 | @Autowired 38 | private EventTransformer eventTransformer; 39 | 40 | @Autowired 41 | private EventRepository eventRepository; 42 | 43 | private Sort sort = new Sort(new Order(Sort.Direction.ASC, "startDate")); 44 | 45 | @RequestMapping(method = RequestMethod.GET) 46 | public List getEvents(@RequestParam(required = false) boolean includeDeleted) { 47 | Iterator iterator = eventRepository.findAll().iterator(); 48 | List events = new ArrayList<>(); 49 | while (iterator.hasNext()) { 50 | events.add(iterator.next()); 51 | } 52 | return eventTransformer.transformToDTOs(events); 53 | } 54 | 55 | //tag::events-rest-api[] 56 | @ApiOperation(value = "Update an event", notes = "Updates an existing event dataset") 57 | @ApiResponses(value = {@ApiResponse(code = 400, message = "Fields are with validation errors")}) 58 | @RequestMapping(method = RequestMethod.POST) 59 | public ResponseEntity setEvent(@RequestBody EventDTO dto) throws EntityNotFoundException { 60 | save(dto); 61 | return new ResponseEntity(new Message("The event has been properly entered"), HttpStatus.OK); 62 | } 63 | //end::events-rest-api[] 64 | 65 | @ApiOperation(value = "Get event", notes = "Read event dataset") 66 | @RequestMapping(value = "/{eventId}", method = RequestMethod.GET) 67 | public EventDTO getEventByEventId(@PathVariable String eventId) throws EntityNotFoundException { 68 | Event event = loadEvent(eventId); 69 | return eventTransformer.transformToDTO(event); 70 | } 71 | 72 | @ApiOperation(value = "Create new event", notes = "Creates new event dataset") 73 | @ApiResponses(value = {@ApiResponse(code = 400, message = "Fields are with validation errors")}) 74 | @RequestMapping(value = "/{eventId}", method = RequestMethod.PUT) 75 | public ResponseEntity updateEventByEventId(@PathVariable String eventId, 76 | @RequestBody EventDTO event) throws BusinessException { 77 | validateEventId(eventId, event); 78 | save(event); 79 | return new ResponseEntity(new Message("The event has been properly updated"), HttpStatus.OK); 80 | } 81 | 82 | @ApiOperation(value = "Delete anevent", notes = "Delete an event dataset") 83 | @RequestMapping(value = "/{eventId}", method = RequestMethod.DELETE) 84 | public ResponseEntity deleteEventByEventId(@PathVariable String eventId) throws EntityNotFoundException { 85 | Event event = loadEvent(eventId); 86 | event.setDeleted(true); 87 | eventRepository.save(event); 88 | return new ResponseEntity(new Message("The event has been properly deleted"), HttpStatus.OK); 89 | } 90 | 91 | private Event loadEvent(String eventId) throws EntityNotFoundException { 92 | Event event = eventRepository.findByEventIdAndDeleted(eventId, false, sort); 93 | if (event == null) { 94 | throw new EntityNotFoundException(eventId); 95 | } 96 | return event; 97 | } 98 | 99 | private void validateEventId(String eventId, EventDTO event) throws ValidationException { 100 | if (!event.getEventId().equals(eventId)) { 101 | throw new ValidationException("The eventId does not match the given event in body"); 102 | } 103 | } 104 | 105 | private Event save(EventDTO dto) throws EntityNotFoundException { 106 | return eventRepository.save(eventTransformer.transformToEntity(dto)); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/controllers/LoginController.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.controllers; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import io.swagger.annotations.ApiResponse; 6 | import io.swagger.annotations.ApiResponses; 7 | import ngSpring.demo.domain.dto.UserDTO; 8 | import ngSpring.demo.domain.entities.User; 9 | import ngSpring.demo.transformer.impl.UserTransformer; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.security.core.userdetails.UserDetailsService; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestMethod; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import javax.security.auth.login.LoginException; 18 | import java.security.Principal; 19 | 20 | @Api(basePath = "/api/login", value = "Login", description = "Login operations", produces = "application/json") 21 | @RestController 22 | @RequestMapping(value = "/api/login") 23 | public class LoginController { 24 | 25 | @Autowired 26 | UserTransformer userTransformer; 27 | 28 | @Autowired 29 | UserDetailsService userDetailsService; 30 | 31 | @ApiOperation(value = "Get user details", notes = "Get existing user dataset") 32 | @ApiResponses(value = {@ApiResponse(code = 401, message = "Login errors")}) 33 | @RequestMapping(value = "/user", method = RequestMethod.GET) 34 | public UserDTO getUserDetails() throws LoginException { 35 | Principal principal = SecurityContextHolder.getContext().getAuthentication(); 36 | if (principal != null) { 37 | User user = (User) userDetailsService.loadUserByUsername(principal.getName()); 38 | UserDTO userDTO = userTransformer.transformToDTO(user); 39 | return userDTO; 40 | } else { 41 | throw new LoginException(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/controllers/UserController.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.controllers; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import io.swagger.annotations.ApiResponse; 6 | import io.swagger.annotations.ApiResponses; 7 | import ngSpring.demo.domain.dto.UserProfileDTO; 8 | import ngSpring.demo.domain.entities.User; 9 | import ngSpring.demo.errorhandling.ValidationMessage; 10 | import ngSpring.demo.exceptions.EntityNotFoundException; 11 | import ngSpring.demo.exceptions.ValidationException; 12 | import ngSpring.demo.repositories.UserRepository; 13 | import ngSpring.demo.transformer.impl.UserProfileTransformer; 14 | import ngSpring.demo.util.Message; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.http.HttpStatus; 17 | import org.springframework.http.ResponseEntity; 18 | import org.springframework.security.access.AccessDeniedException; 19 | import org.springframework.security.core.context.SecurityContextHolder; 20 | import org.springframework.util.StringUtils; 21 | import org.springframework.web.bind.annotation.*; 22 | 23 | import java.security.Principal; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | @Api(basePath = "/api/user", value = "User", description = "User operations", produces = "application/json") 28 | @RestController 29 | @RequestMapping(value = "/api/user") 30 | public class UserController { 31 | 32 | @Autowired 33 | UserRepository userRepository; 34 | 35 | @Autowired 36 | UserProfileTransformer userProfileTransformer; 37 | 38 | 39 | @ApiOperation(value = "Update user details", notes = "Update existing user dataset") 40 | @ApiResponses(value = {@ApiResponse(code = 400, message = "Validation errors")}) 41 | @RequestMapping(method = RequestMethod.POST) 42 | public ResponseEntity postUser(@RequestBody UserProfileDTO userProfileDTO) throws ValidationException { 43 | 44 | User loggedInUser = checkUser(); 45 | validateUserProfileDto(userProfileDTO); 46 | User newUser = this.userProfileTransformer.transformToEntity(userProfileDTO); 47 | 48 | newUser.setEnabled(true); 49 | this.userRepository.save(newUser); 50 | return new ResponseEntity(new Message("The user has been properly created."), HttpStatus.OK); 51 | } 52 | 53 | @RequestMapping(value = "/{userId}", method = RequestMethod.PUT) 54 | public ResponseEntity putUser(@PathVariable String userId, @RequestBody UserProfileDTO userProfileDTO) throws ValidationException, EntityNotFoundException { 55 | 56 | User loggedInUser = checkUser(); 57 | validateUserProfileDto(userProfileDTO); 58 | User currentUser = this.userRepository.findByUserIdAndDeletedFalse(userId); 59 | if (currentUser == null) { 60 | throw new EntityNotFoundException(); 61 | } 62 | User updatedUser = this.userProfileTransformer.transformToEntity(userProfileDTO); 63 | updatedUser.setEnabled(currentUser.isEnabled()); 64 | updatedUser.setInsertDate(currentUser.getInsertDate()); 65 | this.userRepository.save(updatedUser); 66 | return new ResponseEntity(new Message("The user has been properly updated."), HttpStatus.OK); 67 | } 68 | 69 | @RequestMapping(value = "/{userId}", method = RequestMethod.DELETE) 70 | public ResponseEntity deleteUser(@PathVariable String userId) throws EntityNotFoundException { 71 | User loggedInUser = checkUser(); 72 | User toBeDeleted = this.userRepository.findByUserIdAndDeletedFalse(userId); 73 | 74 | if (toBeDeleted == null) { 75 | throw new EntityNotFoundException(); 76 | } 77 | 78 | this.userRepository.save(toBeDeleted); 79 | return new ResponseEntity(new Message("The user has been properly deleted."), HttpStatus.OK); 80 | } 81 | 82 | @RequestMapping(value = "/changePassword", method = RequestMethod.PUT) 83 | public ResponseEntity changePassword(@RequestBody UserProfileDTO dto) throws ValidationException { 84 | User loggedInUser = checkUser(); 85 | validateUserProfileDto(dto); 86 | if (loggedInUser.getUserId() == dto.getUserId()) { 87 | User updatedUser = this.userProfileTransformer.transformToEntity(dto); 88 | this.userRepository.save(updatedUser); 89 | } else { 90 | throw new AccessDeniedException("Not allowed"); 91 | } 92 | return new ResponseEntity(new Message("The password has been properly changed."), HttpStatus.OK); 93 | } 94 | 95 | @RequestMapping(value = "/controller/{customerId}", method = RequestMethod.GET) 96 | public ResponseEntity getControllerByCustomerId(@PathVariable String customerId) throws EntityNotFoundException { 97 | String userName = this.userRepository.findControllerUserNameByCustomerId(customerId); 98 | User user = this.userRepository.findByUsernameAndDeletedFalse(userName); 99 | if (user == null) { 100 | throw new EntityNotFoundException(); 101 | } 102 | UserProfileDTO userProfileDTO = this.userProfileTransformer.transformToDTO(user); 103 | return new ResponseEntity(userProfileDTO, HttpStatus.OK); 104 | } 105 | 106 | @RequestMapping(value = "/planer/{branchId}", method = RequestMethod.GET) 107 | public ResponseEntity getPlanerByBranchId(@PathVariable String branchId) throws EntityNotFoundException { 108 | String userName = this.userRepository.findPlanerUserNameByBranchId(branchId); 109 | User user = this.userRepository.findByUsernameAndDeletedFalse(userName); 110 | if (user == null) { 111 | throw new EntityNotFoundException(); 112 | } 113 | UserProfileDTO userProfileDTO = this.userProfileTransformer.transformToDTO(user); 114 | return new ResponseEntity(userProfileDTO, HttpStatus.OK); 115 | } 116 | 117 | private User checkUser() { 118 | Principal principal = SecurityContextHolder.getContext().getAuthentication(); 119 | if (principal != null) { 120 | return userRepository.findByUsernameAndDeletedFalse(principal.getName()); 121 | } else { 122 | throw new AccessDeniedException("Not logged in."); 123 | } 124 | } 125 | 126 | 127 | private void validateUserProfileDto(UserProfileDTO dto) throws ValidationException { 128 | // check password 129 | if (dto.getUserId() == null && !StringUtils.pathEquals(dto.getPassword(), dto.getPasswordConfirmation())) { 130 | List validationMessages = new ArrayList<>(); 131 | validationMessages.add(ValidationMessage.builder() 132 | .entity(dto.getUsername()) 133 | .messageTemplate("validation.passwords_not_match") 134 | .build()); 135 | throw new ValidationException("", validationMessages); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/AbstractBaseEntity.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | 5 | import javax.persistence.MappedSuperclass; 6 | import java.util.Date; 7 | 8 | @MappedSuperclass 9 | public abstract class AbstractBaseEntity { 10 | 11 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "CET") 12 | protected Date insertDate; 13 | 14 | protected boolean deleted; 15 | 16 | public Date getInsertDate() { 17 | return insertDate; 18 | } 19 | 20 | public void setInsertDate(Date insertDate) { 21 | this.insertDate = insertDate; 22 | } 23 | 24 | public boolean isDeleted() { 25 | return deleted; 26 | } 27 | 28 | public void setDeleted(boolean deleted) { 29 | this.deleted = deleted; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/DTO.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain; 2 | 3 | /** 4 | * Commont DTO API 5 | * 6 | * @author hypery2k 7 | */ 8 | public class DTO { 9 | } 10 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/dto/EventDTO.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.*; 5 | 6 | import javax.validation.constraints.NotNull; 7 | import javax.validation.constraints.Size; 8 | import java.util.Date; 9 | 10 | @Data 11 | @Getter 12 | @Setter 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @ToString 17 | /** 18 | * @author hypery2k 19 | */ 20 | public class EventDTO { 21 | 22 | @NotNull 23 | private String eventId; 24 | 25 | @NotNull 26 | @Size(min = 1, max = 32) 27 | private String eventDescription; 28 | 29 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "CET") 30 | @NotNull 31 | private Date startDate; 32 | 33 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "CET") 34 | private Date endDate; 35 | 36 | @JsonFormat(pattern = "yyyy-MM-dd", timezone = "CET") 37 | protected Date insertDate; 38 | } 39 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotNull; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | public class UserDTO { 15 | @NotNull 16 | private String username; 17 | 18 | @NotNull 19 | private boolean deleted; 20 | } 21 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/dto/UserProfileDTO.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotNull; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | public class UserProfileDTO { 15 | 16 | private String userId; 17 | 18 | @NotNull 19 | private String username; 20 | 21 | @NotNull 22 | private String password; 23 | 24 | @NotNull 25 | private String passwordConfirmation; 26 | } 27 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/dto/UserRoleDTO.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotNull; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | @Builder 14 | public class UserRoleDTO { 15 | 16 | @NotNull 17 | private String roleId; 18 | 19 | @NotNull 20 | private String roleName; 21 | } 22 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/entities/Event.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.entities; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import ngSpring.demo.domain.AbstractBaseEntity; 8 | import ngSpring.demo.validation.ValidateDateRange; 9 | import org.hibernate.annotations.GenericGenerator; 10 | 11 | import javax.persistence.Entity; 12 | import javax.persistence.GeneratedValue; 13 | import javax.persistence.Id; 14 | import javax.validation.constraints.NotNull; 15 | import javax.validation.constraints.Size; 16 | import java.util.Date; 17 | 18 | @Entity 19 | @Builder 20 | @Data 21 | @EqualsAndHashCode(callSuper = false) 22 | @AllArgsConstructor 23 | @ValidateDateRange(start = "startDate", end = "endDate", message = "{validation.date.range_error}", equal = true) 24 | public class Event extends AbstractBaseEntity { 25 | 26 | @GeneratedValue(generator = "uuid") 27 | @GenericGenerator(name = "uuid", strategy = "uuid.hex") 28 | @Id 29 | private String eventId; 30 | 31 | @NotNull 32 | @Size(min = 1, max = 50) 33 | private String eventDescription; 34 | 35 | @NotNull 36 | private Date startDate; 37 | 38 | private Date endDate; 39 | 40 | public Event() { 41 | super(); 42 | } 43 | 44 | public Event(String eventDescription, Date startDate, Date endDate) { 45 | super(); 46 | this.eventDescription = eventDescription; 47 | this.startDate = startDate; 48 | this.endDate = endDate; 49 | } 50 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/domain/entities/User.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.entities; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.ToString; 6 | import ngSpring.demo.domain.AbstractBaseEntity; 7 | import org.hibernate.annotations.GenericGenerator; 8 | import org.springframework.security.core.GrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | 11 | import javax.persistence.Column; 12 | import javax.persistence.Entity; 13 | import javax.persistence.GeneratedValue; 14 | import javax.persistence.Id; 15 | import javax.validation.constraints.NotNull; 16 | import java.util.Collection; 17 | import java.util.HashSet; 18 | import java.util.Set; 19 | 20 | @Entity 21 | @Builder 22 | @ToString 23 | @AllArgsConstructor 24 | @SuppressWarnings("serial") 25 | public class User extends AbstractBaseEntity implements UserDetails { 26 | 27 | @GeneratedValue(generator = "uuid") 28 | @GenericGenerator(name = "uuid", strategy = "uuid.hex") 29 | @Id 30 | private String userId; 31 | 32 | @Column(name = "user_name") 33 | @NotNull 34 | private String username; 35 | 36 | @NotNull 37 | private String password; 38 | 39 | private boolean enabled; 40 | 41 | public User() { 42 | super(); 43 | } 44 | 45 | public User(String userName, String password) { 46 | super(); 47 | this.username = userName; 48 | this.password = password; 49 | } 50 | 51 | public String getUserId() { 52 | return userId; 53 | } 54 | 55 | public void setUserId(String userId) { 56 | this.userId = userId; 57 | } 58 | 59 | @Override 60 | public String getUsername() { 61 | return username; 62 | } 63 | 64 | public void setUsername(String username) { 65 | this.username = username; 66 | } 67 | 68 | @Override 69 | public String getPassword() { 70 | return password; 71 | } 72 | 73 | public void setPassword(String password) { 74 | this.password = password; 75 | } 76 | 77 | public void setEnabled(boolean enabled) { 78 | this.enabled = enabled; 79 | } 80 | 81 | @Override 82 | public boolean isEnabled() { 83 | return enabled; 84 | } 85 | 86 | @Override 87 | public Collection getAuthorities() { 88 | Set authorities = new HashSet(); 89 | return authorities; 90 | } 91 | 92 | @Override 93 | public boolean isAccountNonExpired() { 94 | return true; 95 | } 96 | 97 | @Override 98 | public boolean isAccountNonLocked() { 99 | return true; 100 | } 101 | 102 | @Override 103 | public boolean isCredentialsNonExpired() { 104 | return true; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/errorhandling/GlobalControllerExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.errorhandling; 2 | 3 | import ngSpring.demo.exceptions.EntityNotFoundException; 4 | import ngSpring.demo.exceptions.ValidationException; 5 | import ngSpring.demo.util.Message; 6 | import org.apache.log4j.Logger; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.security.access.AccessDeniedException; 10 | import org.springframework.validation.BindingResult; 11 | import org.springframework.validation.FieldError; 12 | import org.springframework.web.bind.MethodArgumentNotValidException; 13 | import org.springframework.web.bind.annotation.ControllerAdvice; 14 | import org.springframework.web.bind.annotation.ExceptionHandler; 15 | import org.springframework.web.bind.annotation.ResponseBody; 16 | 17 | import javax.persistence.RollbackException; 18 | import javax.validation.ConstraintViolation; 19 | import javax.validation.ConstraintViolationException; 20 | import javax.validation.Path; 21 | import java.util.ArrayList; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | 25 | @ControllerAdvice 26 | public class GlobalControllerExceptionHandler { 27 | 28 | private static final Logger LOG = Logger 29 | .getLogger(GlobalControllerExceptionHandler.class); 30 | 31 | // SECURITY ACCESS HANDLING 32 | 33 | @ExceptionHandler(AccessDeniedException.class) 34 | @ResponseBody 35 | public ResponseEntity handleSecurityErrors(AccessDeniedException ex) { 36 | LOG.debug("Got access error", ex); 37 | // fallback to server error 38 | return new ResponseEntity(new Message(ex.getMessage()), HttpStatus.FORBIDDEN); 39 | } 40 | 41 | // GENERAL ERROR HANDLING 42 | 43 | @ExceptionHandler(Exception.class) 44 | @ResponseBody 45 | public ResponseEntity handleErrors(Exception ex) { 46 | if (ex.getCause() instanceof RollbackException 47 | && ex.getCause().getCause() instanceof ConstraintViolationException) { 48 | ConstraintViolationException constraintViolationException = (ConstraintViolationException) ex.getCause().getCause(); 49 | return new ResponseEntity>(getValidationErrorResponse(constraintViolationException), HttpStatus.BAD_REQUEST); 50 | } else { 51 | LOG.error("Got unknown error", ex); 52 | // fallback to server error 53 | return new ResponseEntity(new Message(ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); 54 | } 55 | } 56 | // VALIDATION ERROR HANDLING 57 | 58 | @ExceptionHandler(MethodArgumentNotValidException.class) 59 | @ResponseBody 60 | public ResponseEntity handleValidationError(MethodArgumentNotValidException ex) { 61 | final List validationErrors = new ArrayList<>(); 62 | BindingResult result = ex.getBindingResult(); 63 | List fieldErrors = result.getFieldErrors(); 64 | for (FieldError fieldError : fieldErrors) { 65 | validationErrors.add(ValidationMessage.builder() 66 | .entity(fieldError.getField()) 67 | .messageTemplate(fieldError.getDefaultMessage()) 68 | .build()); 69 | } 70 | return new ResponseEntity>(validationErrors, HttpStatus.BAD_REQUEST); 71 | } 72 | 73 | @ExceptionHandler(ValidationException.class) 74 | @ResponseBody 75 | public ResponseEntity handleBadRequestException(ValidationException ex) { 76 | LOG.error("Got validation errors", ex); 77 | if (ex.getValidationMessages() == null || ex.getValidationMessages().isEmpty()) { 78 | return new ResponseEntity(new Message(ex.getMessage()), HttpStatus.BAD_REQUEST); 79 | } else { 80 | return new ResponseEntity>(ex.getValidationMessages(), HttpStatus.BAD_REQUEST); 81 | } 82 | } 83 | 84 | @ExceptionHandler(EntityNotFoundException.class) 85 | @ResponseBody 86 | public ResponseEntity handleEntityNotFoundException( 87 | EntityNotFoundException ex) { 88 | LOG.error("Could not find entity with id " + ex.getId(), ex); 89 | return new ResponseEntity(new Message(ex.getMessage()), HttpStatus.NOT_FOUND); 90 | } 91 | 92 | private List getValidationErrorResponse(ConstraintViolationException constraintViolationException) { 93 | final List validationErrors = new ArrayList<>(); 94 | LOG.error("Got validation errors", constraintViolationException); 95 | for (ConstraintViolation violationSet : constraintViolationException.getConstraintViolations()) { 96 | List propertyList = new ArrayList<>(); 97 | Iterator propertyIterator = violationSet 98 | .getPropertyPath().iterator(); 99 | while (propertyIterator.hasNext()) { 100 | propertyList.add(propertyIterator.next().getName()); 101 | } 102 | // add violations errors in response 103 | validationErrors.add(ValidationMessage.builder() 104 | .entity(violationSet.getRootBeanClass().getName()) 105 | // remove { and } 106 | .messageTemplate(violationSet.getMessageTemplate().replaceAll("^[{]|[}]$", "")) 107 | .propertyList(propertyList).build()); 108 | } 109 | return validationErrors; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/errorhandling/ValidationMessage.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.errorhandling; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | 8 | import java.io.Serializable; 9 | import java.util.List; 10 | 11 | 12 | @Builder 13 | @AllArgsConstructor 14 | @Getter 15 | @ToString 16 | @SuppressWarnings("serial") 17 | public class ValidationMessage implements Serializable { 18 | 19 | private String entity; 20 | 21 | private String messageTemplate; 22 | 23 | private List propertyList; 24 | } 25 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/exceptions/BusinessException.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.exceptions; 2 | 3 | 4 | /** 5 | * Common superclass for business exception in the application 6 | * 7 | * @author hypery2k 8 | */ 9 | @SuppressWarnings("serial") 10 | public class BusinessException extends Exception { 11 | 12 | public BusinessException(String msg) { 13 | super(msg); 14 | } 15 | 16 | public BusinessException(Exception exception) { 17 | super(exception); 18 | } 19 | 20 | public BusinessException(String msg, Exception exception) { 21 | super(msg, exception); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/exceptions/EntityNotFoundException.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.exceptions; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | 7 | // TODO add generic information 8 | @ResponseStatus(value = HttpStatus.NOT_FOUND) 9 | @SuppressWarnings("serial") 10 | public class EntityNotFoundException extends BusinessException { 11 | 12 | private String id; 13 | 14 | public EntityNotFoundException(String entityId) { 15 | super("could not find entity with id '" + entityId + "'."); 16 | this.id = entityId; 17 | } 18 | 19 | public EntityNotFoundException() { 20 | super("could not find entity"); 21 | } 22 | 23 | public String getId() { 24 | return id; 25 | } 26 | 27 | public EntityNotFoundException setId(String id) { 28 | this.id = id; 29 | return this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/exceptions/ValidationException.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.exceptions; 2 | 3 | import ngSpring.demo.errorhandling.ValidationMessage; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Indicate validation errors which are not handled via bean validation, 9 | * e.g. during file parsing 10 | * 11 | * @author hypery2k 12 | */ 13 | @SuppressWarnings("serial") 14 | public class ValidationException extends BusinessException { 15 | 16 | private List validationMessages; 17 | 18 | public ValidationException(String message) { 19 | super(message); 20 | } 21 | 22 | public ValidationException(String message, List validationMessages) { 23 | super(message); 24 | this.validationMessages = validationMessages; 25 | } 26 | 27 | public List getValidationMessages() { 28 | return validationMessages; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/mvc/MvcConfig.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.mvc; 2 | 3 | 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 7 | 8 | 9 | //tag::thymeleaf-config[] 10 | @Configuration 11 | public class MvcConfig extends WebMvcConfigurerAdapter { 12 | 13 | @Override 14 | public void addViewControllers(ViewControllerRegistry registry) { 15 | registry.addViewController("/login").setViewName("login"); 16 | } 17 | 18 | } 19 | //end::thymeleaf-config[] -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/repositories/EventRepository.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.repositories; 2 | 3 | import ngSpring.demo.domain.entities.Event; 4 | import org.springframework.data.domain.Sort; 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | public interface EventRepository extends CrudRepository { 8 | 9 | Event findByEventIdAndDeleted(String eventId, boolean deleted, Sort sort); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.repositories; 2 | 3 | import ngSpring.demo.domain.entities.User; 4 | import org.springframework.data.jpa.repository.Query; 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.data.repository.query.Param; 7 | 8 | public interface UserRepository extends CrudRepository { 9 | 10 | @Query(nativeQuery = true, value = "select u.user_name from user u where u.customer_id = :customerId and exists (select * from user_roles where user_id = u.user_id and role_id = 2)") 11 | String findControllerUserNameByCustomerId( 12 | @Param("customerId") String customerId); 13 | 14 | 15 | @Query(nativeQuery = true, value = "select u.user_name from user u, user_branches ub where u.user_id = ub.user_id and ub.branch_id = :branchId") 16 | String findPlanerUserNameByBranchId(@Param("branchId") String branchId); 17 | 18 | User findByUsernameAndDeletedFalse(String username); 19 | 20 | User findByUserIdAndDeletedFalse(String userId); 21 | } 22 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/security/AngularSpringAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.security; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.userdetails.UserDetailsService; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component("authenticationProvider") 12 | public class AngularSpringAuthenticationProvider extends DaoAuthenticationProvider { 13 | 14 | @Autowired 15 | public void setUserDetailsService(UserDetailsService userDetailsService) { 16 | super.setUserDetailsService(userDetailsService); 17 | super.setPasswordEncoder(passwordEncoder()); 18 | } 19 | 20 | @Override 21 | public Authentication authenticate(Authentication authentication) { 22 | return super.authenticate(authentication); 23 | } 24 | 25 | public PasswordEncoder passwordEncoder() { 26 | return new BCryptPasswordEncoder(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/security/AppSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.security; 2 | 3 | import org.springframework.boot.autoconfigure.security.SecurityProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.annotation.Order; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity; 10 | 11 | /** 12 | * Enable security authentication for web app 13 | * 14 | * @author hypery2k 15 | */ 16 | //tag::thymeleaf-config[] 17 | @Configuration 18 | @EnableWebMvcSecurity 19 | @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 20 | public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter { 21 | //end::thymeleaf-config[] 22 | 23 | // public ressources which are not secured 24 | private static final String[] UNSECURED_RESOURCE_LIST = 25 | new String[]{"/docs/*", "/styles/*", "/fonts/*", "/scripts/*", "/images/*", "/index.html", "/views/login.html"}; 26 | 27 | @Override 28 | public void configure(WebSecurity web) throws Exception { 29 | web.ignoring().antMatchers(UNSECURED_RESOURCE_LIST); 30 | } 31 | 32 | //tag::thymeleaf-config[] 33 | @Override 34 | protected void configure(HttpSecurity http) throws Exception { 35 | http.authorizeRequests().antMatchers(UNSECURED_RESOURCE_LIST).authenticated(); 36 | http.csrf().disable(); 37 | // use form authentication for web app 38 | http.authorizeRequests().and().formLogin().loginPage("/login"); 39 | // enable HTTP basic authentication for REST calls 40 | http.authorizeRequests().antMatchers("/api/**").authenticated().and().httpBasic(); 41 | //end::thymeleaf-config[] 42 | } 43 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/security/AuthenticationSecurity.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.security; 2 | 3 | 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Qualifier; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.Ordered; 8 | import org.springframework.core.annotation.Order; 9 | import org.springframework.security.authentication.AuthenticationProvider; 10 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 11 | import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter; 12 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 13 | import org.springframework.security.core.userdetails.UserDetailsService; 14 | 15 | /** 16 | * Configures global authentication provider, e.g. for password encryption and user details service 17 | * 18 | * @author hypery2k 19 | */ 20 | @Order(Ordered.HIGHEST_PRECEDENCE) 21 | @Configuration 22 | @EnableGlobalMethodSecurity(prePostEnabled = true) 23 | class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter { 24 | 25 | @Autowired 26 | @Qualifier("authenticationProvider") 27 | AuthenticationProvider authenticationProvider; 28 | 29 | @Autowired 30 | private UserDetailsService userDetailsService; 31 | 32 | @Override 33 | public void init(AuthenticationManagerBuilder auth) throws Exception { 34 | auth.userDetailsService(userDetailsService); 35 | auth.authenticationProvider(authenticationProvider); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/services/impl/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.services.impl; 2 | 3 | import ngSpring.demo.domain.entities.User; 4 | import ngSpring.demo.repositories.UserRepository; 5 | import org.apache.log4j.Logger; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 10 | import org.springframework.stereotype.Service; 11 | 12 | @Service("userDetailsService") 13 | public class UserDetailsServiceImpl implements UserDetailsService { 14 | 15 | private static final Logger LOG = Logger 16 | .getLogger(UserDetailsServiceImpl.class); 17 | 18 | @Autowired 19 | UserRepository userRepository; 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) 23 | throws UsernameNotFoundException { 24 | User user = userRepository.findByUsernameAndDeletedFalse(username); 25 | if (user != null) { 26 | LOG.debug("Found user with name " + username); 27 | return user; 28 | } else { 29 | LOG.error("Cannot find user with name " + username + " in the database."); 30 | return new User(username, ""); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/transformer/GenericTransformer.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.transformer; 2 | 3 | import ngSpring.demo.exceptions.EntityNotFoundException; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Commont DTO and Domain transformer methods used by any transformer 10 | * 11 | * @author hypery2k 12 | */ 13 | public abstract class GenericTransformer implements Transformer { 14 | 15 | @Override 16 | public List transformToEntities(List dtoList) throws EntityNotFoundException { 17 | List entityList = new ArrayList(); 18 | for (DTO dto : dtoList) { 19 | entityList.add(transformToEntity(dto)); 20 | } 21 | return entityList; 22 | } 23 | 24 | @Override 25 | public List transformToDTOs(List entityList) { 26 | List dtoList = new ArrayList(); 27 | for (Entity entity : entityList) { 28 | dtoList.add(transformToDTO(entity)); 29 | } 30 | return dtoList; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.transformer; 2 | 3 | import ngSpring.demo.exceptions.EntityNotFoundException; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Commont DTO and Domain transformer API 9 | * 10 | * @author hypery2k 11 | */ 12 | public interface Transformer { 13 | 14 | Entity transformToEntity(DTO dto) throws EntityNotFoundException; 15 | 16 | DTO transformToDTO(Entity entity); 17 | 18 | List transformToEntities(List dtoList) throws EntityNotFoundException; 19 | 20 | List transformToDTOs(List entityList); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/transformer/impl/EventTransformer.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.transformer.impl; 2 | 3 | import ngSpring.demo.domain.dto.EventDTO; 4 | import ngSpring.demo.domain.entities.Event; 5 | import ngSpring.demo.exceptions.EntityNotFoundException; 6 | import ngSpring.demo.transformer.GenericTransformer; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EventTransformer extends GenericTransformer { 11 | 12 | @Override 13 | public EventDTO transformToDTO(Event entity) { 14 | if (entity != null) { 15 | return EventDTO.builder() 16 | .eventId(entity.getEventId()) 17 | .eventDescription(entity.getEventDescription()) 18 | .startDate(entity.getStartDate()) 19 | .endDate(entity.getEndDate()) 20 | .insertDate(entity.getInsertDate()) 21 | .build(); 22 | } 23 | return null; 24 | } 25 | 26 | @Override 27 | public Event transformToEntity(EventDTO dto) throws EntityNotFoundException { 28 | if (dto != null) { 29 | Event event = Event.builder() 30 | .eventId(dto.getEventId()) 31 | .eventDescription(dto.getEventDescription()) 32 | .startDate(dto.getStartDate()) 33 | .endDate(dto.getEndDate()) 34 | .build(); 35 | event.setInsertDate(dto.getInsertDate()); 36 | return event; 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/transformer/impl/UserProfileTransformer.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.transformer.impl; 2 | 3 | 4 | import ngSpring.demo.domain.dto.UserProfileDTO; 5 | import ngSpring.demo.domain.entities.User; 6 | import ngSpring.demo.repositories.UserRepository; 7 | import ngSpring.demo.transformer.GenericTransformer; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | public class UserProfileTransformer extends GenericTransformer { 15 | 16 | @Autowired 17 | private UserRepository userRepository; 18 | 19 | @Override 20 | public User transformToEntity(UserProfileDTO dto) { 21 | PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 22 | return User.builder() 23 | .userId(dto.getUserId()) 24 | .username(dto.getUsername()) 25 | .password(dto.getPassword() != null ? passwordEncoder.encode(dto.getPassword()) : this.userRepository.findByUserIdAndDeletedFalse(dto.getUserId()).getPassword()) 26 | .build(); 27 | } 28 | 29 | @Override 30 | public UserProfileDTO transformToDTO(User user) { 31 | return UserProfileDTO.builder() 32 | .userId(user.getUserId()) 33 | .username(user.getUsername()) 34 | .build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/transformer/impl/UserTransformer.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.transformer.impl; 2 | 3 | 4 | import ngSpring.demo.domain.dto.UserDTO; 5 | import ngSpring.demo.domain.entities.User; 6 | import ngSpring.demo.exceptions.EntityNotFoundException; 7 | import ngSpring.demo.repositories.UserRepository; 8 | import ngSpring.demo.transformer.GenericTransformer; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @Component 16 | public class UserTransformer extends GenericTransformer { 17 | 18 | @Autowired 19 | private UserRepository userRepository; 20 | 21 | @Override 22 | public UserDTO transformToDTO(User entity) { 23 | if (entity != null) { 24 | List permissions = new ArrayList<>(); 25 | return UserDTO.builder() 26 | .username(entity.getUsername()) 27 | .deleted(entity.isDeleted()) 28 | .build(); 29 | } 30 | return null; 31 | } 32 | 33 | @Override 34 | public User transformToEntity(UserDTO dto) throws EntityNotFoundException { 35 | if (dto != null) { 36 | User user = userRepository.findByUsernameAndDeletedFalse(dto.getUsername()); 37 | if (user == null) { 38 | throw new EntityNotFoundException(dto.getUsername()); 39 | } else { 40 | return user; 41 | } 42 | } 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/util/Message.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.util; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | 8 | import java.io.Serializable; 9 | 10 | @Builder 11 | @AllArgsConstructor 12 | @Getter 13 | @ToString 14 | @SuppressWarnings("serial") 15 | public class Message implements Serializable { 16 | 17 | private String message; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/validation/AbstractBeanValidator.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.validation; 2 | 3 | 4 | import org.springframework.util.StringUtils; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import java.lang.annotation.Annotation; 8 | 9 | /** 10 | * Common superclass for bean validators to share some common functionality for reflection etc 11 | * 12 | * @author hypery2k 13 | */ 14 | public abstract class AbstractBeanValidator implements ConstraintValidator { 15 | 16 | /** 17 | * @param pAttribute to get the method for 18 | * @return the getter method name for the given attribute 19 | */ 20 | protected String getGetterDeclaration(final String pAttribute) { 21 | if (StringUtils.hasLength(pAttribute)) { 22 | final StringBuilder builder = new StringBuilder("get"); 23 | builder.append(Character.toUpperCase(pAttribute.charAt(0))); 24 | builder.append(pAttribute.substring(1)); 25 | return builder.toString(); 26 | } else { 27 | return ""; 28 | } 29 | } 30 | 31 | /** 32 | * @param pAttribute to get the method for 33 | * @return the is method name for the given boolean attribute 34 | */ 35 | protected String getIsDeclaration(final String pAttribute) { 36 | if (StringUtils.hasLength(pAttribute)) { 37 | final StringBuilder builder = new StringBuilder("is"); 38 | builder.append(Character.toUpperCase(pAttribute.charAt(0))); 39 | builder.append(pAttribute.substring(1)); 40 | return builder.toString(); 41 | } else { 42 | return ""; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/validation/DateRangeValidator.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.validation; 2 | 3 | import org.apache.log4j.Logger; 4 | 5 | import javax.validation.ConstraintValidatorContext; 6 | import java.lang.reflect.Method; 7 | import java.util.Date; 8 | 9 | /** 10 | * Validator for ensure that a start date is before a end date 11 | * 12 | * @author hypery2k 13 | */ 14 | public class DateRangeValidator extends AbstractBeanValidator { 15 | 16 | /** 17 | * Write logging messages for this class. 18 | */ 19 | private static final Logger LOG = Logger.getLogger(DateRangeValidator.class); 20 | 21 | private String start; 22 | 23 | private String end; 24 | 25 | private boolean equal; 26 | 27 | /** 28 | * @see javax.validation.ConstraintValidator#initialize(java.lang.annotation.Annotation) 29 | */ 30 | @Override 31 | public void initialize(final ValidateDateRange constraintAnnotation) { 32 | this.start = constraintAnnotation.start(); 33 | this.end = constraintAnnotation.end(); 34 | this.equal = constraintAnnotation.equal(); 35 | } 36 | 37 | /** 38 | * @see javax.validation.ConstraintValidator#isValid(java.lang.Object, javax.validation.ConstraintValidatorContext) 39 | */ 40 | @SuppressWarnings({"rawtypes", "unchecked"}) 41 | @Override 42 | public boolean isValid(final Object value, final ConstraintValidatorContext context) { 43 | boolean isValid = false; 44 | final Class clazz = value.getClass(); 45 | 46 | try { 47 | Date startDate = null; 48 | final Method startMethod = clazz.getMethod(this.getGetterDeclaration(this.start), new Class[0]); 49 | if (startMethod != null) { 50 | startDate = (Date) startMethod.invoke(value, null); 51 | } 52 | 53 | Date endDate = null; 54 | final Method endMethod = clazz.getMethod(this.getGetterDeclaration(this.end), new Class[0]); 55 | if (startMethod != null) { 56 | endDate = (Date) endMethod.invoke(value, null); 57 | } 58 | 59 | if (endDate != null && startDate != null) { 60 | if (this.equal) { 61 | isValid = endDate.compareTo(startDate) >= 0; 62 | } else { 63 | isValid = endDate.after(startDate); 64 | } 65 | } else { 66 | isValid = true; 67 | } 68 | } catch (final Exception e) { 69 | LOG.error("Error occurred during validating date range.", e); 70 | } 71 | if (!isValid) { 72 | final String template = context.getDefaultConstraintMessageTemplate(); 73 | context.disableDefaultConstraintViolation(); 74 | context.buildConstraintViolationWithTemplate(template) 75 | .addPropertyNode(this.start) 76 | .addPropertyNode(this.end) 77 | .addBeanNode() 78 | .addConstraintViolation(); 79 | } 80 | return isValid; 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/validation/TimeRangeValidator.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.validation; 2 | 3 | import org.apache.log4j.Logger; 4 | 5 | import javax.validation.ConstraintValidatorContext; 6 | import java.lang.reflect.Method; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | 10 | /** 11 | * This bean validator checks the given morning and evening time stamps if they are valid (start 12 | * must before end for 13 | * each entry) and if both are set if the morning time is before evening time. 14 | *

15 | * Note that it's possible that two pairs ( morning) can be left empty or set to 00:00 16 | * 17 | * @author hypery2k 18 | */ 19 | public class TimeRangeValidator extends AbstractBeanValidator { 20 | 21 | public static final String PLACEHOLDER_EMTPY_TIME = "00:00"; 22 | 23 | private String start; 24 | 25 | private String end; 26 | 27 | /** 28 | * Write logging messages for this class. 29 | */ 30 | private static final Logger LOG = Logger.getLogger(TimeRangeValidator.class); 31 | 32 | /** 33 | * @see javax.validation.ConstraintValidator#initialize(java.lang.annotation.Annotation) 34 | */ 35 | @Override 36 | public void initialize(final ValidateTimeRange constraintAnnotation) { 37 | this.start = constraintAnnotation.start(); 38 | this.end = constraintAnnotation.end(); 39 | } 40 | 41 | /** 42 | * @see javax.validation.ConstraintValidator#isValid(java.lang.Object, javax.validation.ConstraintValidatorContext) 43 | */ 44 | @SuppressWarnings({"rawtypes", "unchecked"}) 45 | @Override 46 | public boolean isValid(final Object value, final ConstraintValidatorContext context) { 47 | boolean isValid = false; 48 | final Class clazz = value.getClass(); 49 | try { 50 | final SimpleDateFormat format = new SimpleDateFormat("HH:mm"); 51 | final Method start = clazz.getMethod(this.getGetterDeclaration(this.start), new Class[0]); 52 | if (start != null) { 53 | Date startDate = null; 54 | final String propertValue = (String) start.invoke(value, null); 55 | if (propertValue != null && !propertValue.equals("") && !propertValue.equals(PLACEHOLDER_EMTPY_TIME)) { 56 | startDate = format.parse(propertValue); 57 | } 58 | if (startDate == null) { 59 | isValid = true; 60 | } else { 61 | final Method end = clazz.getMethod(this.getGetterDeclaration(this.end), new Class[0]); 62 | if (end != null) { 63 | final String propertValueEnd = (String) end.invoke(value, null); 64 | if (propertValueEnd != null && !propertValueEnd.equals("") && !propertValueEnd.equals(PLACEHOLDER_EMTPY_TIME)) { 65 | Date endDate = format.parse(propertValueEnd); 66 | // both are not null and end is after start 67 | isValid = endDate.after(startDate); 68 | } 69 | } 70 | } 71 | } 72 | } catch (final Exception e) { 73 | LOG.error("Error occurred during validating date range.", e); 74 | } 75 | if (!isValid) { 76 | final String template = context.getDefaultConstraintMessageTemplate(); 77 | context.disableDefaultConstraintViolation(); 78 | context.buildConstraintViolationWithTemplate(template) 79 | .addPropertyNode(this.start) 80 | .addPropertyNode(this.end) 81 | .addBeanNode() 82 | .addConstraintViolation(); 83 | } 84 | return isValid; 85 | } 86 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/validation/ValidateDateRange.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.validation; 2 | 3 | 4 | import javax.validation.Constraint; 5 | import javax.validation.Payload; 6 | import java.lang.annotation.*; 7 | 8 | /** 9 | * Simply checks if the given start date is before the end date 10 | * 11 | * @author hypery2k 12 | */ 13 | @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Constraint(validatedBy = {DateRangeValidator.class}) 16 | @Documented 17 | public @interface ValidateDateRange { 18 | 19 | String message() default "{validation.date.range_error}"; 20 | 21 | /** 22 | * start date 23 | * 24 | * @return 25 | */ 26 | String start(); 27 | 28 | /** 29 | * end date 30 | * 31 | * @return 32 | */ 33 | String end(); 34 | 35 | /** 36 | * use this property to let the start and date be equal 37 | * 38 | * @return 39 | */ 40 | boolean equal() default false; 41 | 42 | Class[] payload() default {}; 43 | 44 | Class[] groups() default {}; 45 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/java/ngSpring/demo/validation/ValidateTimeRange.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.validation; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.*; 6 | 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | /** 10 | * Simply checks if the given start time is before the end time and evening time is later the 11 | * morning time 12 | * 13 | * @author hypery2k 14 | */ 15 | @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Constraint(validatedBy = {TimeRangeValidator.class}) 18 | @Documented 19 | public @interface ValidateTimeRange { 20 | 21 | String message() default "{validation.time.range_error}"; 22 | 23 | String start(); 24 | 25 | String end(); 26 | 27 | Class[] payload() default {}; 28 | 29 | Class[] groups() default {}; 30 | 31 | /** 32 | * Defines several {@code @ValidateTimeRange} annotations on the same element. 33 | */ 34 | @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) 35 | @Retention(RUNTIME) 36 | @Documented 37 | public @interface List { 38 | ValidateTimeRange[] value(); 39 | } 40 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/resources/ValidationMessages_de.properties: -------------------------------------------------------------------------------- 1 | 2 | validation.date.range_error = Das Startdatum liegt nicht vor dem Enddatum. -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/resources/application-heroku.properties: -------------------------------------------------------------------------------- 1 | # general 2 | spring.application.name=angular-spring-demo 3 | server.port=9080 4 | 5 | # http encoding 6 | spring.http.encoding.charset=UTF-8 7 | spring.http.encoding.enabled=true 8 | spring.http.encoding.force=true 9 | 10 | # database: mysql 11 | spring.datasource.driverClassName=com.mysql.jdbc.Driver 12 | spring.datasource.url=jdbc:mysql://localhost/NGSPRING 13 | spring.datasource.username=ngspring 14 | spring.datasource.password=password 15 | spring.jpa.hibernate.ddl-auto=update 16 | hibernate.dialect=mysql 17 | 18 | #flyway properties 19 | flyway.url=jdbc:mysql://localhost/NGSPRING 20 | flyway.user=ngspring 21 | flyway.password=password 22 | flyway.enabled=true 23 | 24 | # Show or not log for each sql query 25 | spring.jpa.show-sql = false 26 | 27 | org.springframework.security.level=DEBUG 28 | 29 | # monitoring 30 | management.context-path=/actuator 31 | endpoints.enabled=true 32 | 33 | # pretty-print output 34 | spring.jackson.serialization.INDENT_OUTPUT=true 35 | spring.jackson.serialization.write-dates-as-timestamps:false 36 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/resources/application-test.properties: -------------------------------------------------------------------------------- 1 | # database for test: H2 2 | spring.datasource.driverClassName=org.h2.Driver 3 | spring.datasource.url=jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE;MODE=MySQL 4 | spring.datasource.username=sa 5 | spring.datasource.password= 6 | 7 | flyway.enabled=false 8 | 9 | spring.jpa.hibernate.ddl-auto=create-drop 10 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # general 2 | spring.application.name=angular-spring-demo 3 | server.port=9080 4 | 5 | # http encoding 6 | spring.http.encoding.charset=UTF-8 7 | spring.http.encoding.enabled=true 8 | spring.http.encoding.force=true 9 | 10 | # database: mysql 11 | spring.datasource.driverClassName=com.mysql.jdbc.Driver 12 | spring.datasource.url=jdbc:mysql://33.33.33.10:3306/NGSPRING?useUnicode=true&characterEncoding=utf8 13 | spring.datasource.username=ngspring 14 | spring.datasource.password=password 15 | spring.jpa.hibernate.ddl-auto=update 16 | hibernate.dialect=mysql 17 | 18 | #flyway properties 19 | flyway.url=jdbc:mysql://33.33.33.10:3306/NGSPRING 20 | flyway.user=ngspring 21 | flyway.password=password 22 | flyway.enabled=true 23 | 24 | # Show or not log for each sql query 25 | spring.jpa.show-sql = false 26 | 27 | org.springframework.security.level=DEBUG 28 | 29 | # monitoring 30 | management.context-path=/actuator 31 | endpoints.enabled=true 32 | 33 | # pretty-print output 34 | spring.jackson.serialization.INDENT_OUTPUT=true 35 | spring.jackson.serialization.write-dates-as-timestamps:false 36 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/resources/db/migration/V1_0_01__Initial_db_version.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE event ( 2 | event_id VARCHAR(50) NOT NULL, 3 | event_description VARCHAR(100), 4 | start_date DATE, 5 | end_date DATE, 6 | insert_date DATE, 7 | deleted BIT(1) DEFAULT 0, 8 | PRIMARY KEY(event_id) 9 | ); 10 | 11 | CREATE TABLE user ( 12 | user_id varchar(255) NOT NULL, 13 | enabled bit(1) NOT NULL, 14 | password varchar(255) NOT NULL, 15 | user_name varchar(255) NOT NULL, 16 | insert_date datetime DEFAULT NULL, 17 | deleted bit(1) NOT NULL, 18 | PRIMARY KEY (user_id) 19 | ); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | eventCMS - Login 16 | 17 | 20 | 21 | 78 | 79 | 80 | 81 | 82 |

83 | 84 |

eventCMS

85 | 86 | 95 | 96 | 100 | 101 | 105 | 106 |
107 | 108 | 109 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "jasmine": true, 22 | "globals": { 23 | "angular": false, 24 | "browser": false, 25 | "inject": false 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/mocks/mock.app.js: -------------------------------------------------------------------------------- 1 | /*global mockApp: true*/ 2 | var mockApp = angular.module('ngSpringBootAppMock', ['ngSpringBootApp']); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/mocks/mock.srv.auth.js: -------------------------------------------------------------------------------- 1 | mockApp.factory('AuthServicePositiveMock', function () { 2 | 'use strict'; 3 | return { 4 | login: function (credentials, callback, errorCallback) { 5 | callback(credentials); 6 | }, 7 | logout: function (callback, errorCallback) { 8 | callback(); 9 | } 10 | }; 11 | } 12 | ); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/mocks/mock.srv.event.js: -------------------------------------------------------------------------------- 1 | mockApp.factory('EventServicePositiveMock', function () { 2 | 'use strict'; 3 | return { 4 | listAllEvents: function (callback, errorCallback) { 5 | callback(); 6 | }, 7 | getEvent: function (eventId, callback, errorCallback) { 8 | callback({eventCategory: 'eventCategory'}); 9 | }, 10 | saveEvent: function (event, callback, errorCallback) { 11 | callback(); 12 | }, 13 | deleteEvent: function (event, callback, errorCallback) { 14 | callback(); 15 | } 16 | }; 17 | } 18 | ); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/mocks/mock.srv.redirect.js: -------------------------------------------------------------------------------- 1 | mockApp.factory('RedirectServicePositiveMock', function () { 2 | 'use strict'; 3 | return { 4 | redirect: function (path) { 5 | 6 | } 7 | }; 8 | } 9 | ); 10 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/mocks/mock.srv.user.js: -------------------------------------------------------------------------------- 1 | mockApp.factory('UserServicePositiveMock', function (Domain) { 2 | 'use strict'; 3 | 4 | return { 5 | saveUser: function (user, callback, errorCallback) { 6 | callback(); 7 | }, 8 | getControllerByCustomer: function (customerId, callback, errorCallback){ 9 | callback(); 10 | }, 11 | getPlanerByBranch: function(branchId, callback, errorCallback){ 12 | callback(Domain.UserProfile.build()); 13 | }, 14 | deleteUser: function(userId, callback, errorCallback){ 15 | callback(); 16 | } 17 | }; 18 | } 19 | ); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/controllers/ctrl.eventSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: EventCtrl', function () { 4 | // load the controller's module 5 | 6 | beforeEach(function () { 7 | // load app module 8 | module('ngSpringBootApp'); 9 | // load app mock module 10 | module('ngSpringBootAppMock'); 11 | }); 12 | 13 | var ctrl, 14 | scope, 15 | mockedFeedbackUIService, 16 | mockedEventService, 17 | mockedAuthService; 18 | 19 | // Initialize the controller and a mock scope 20 | beforeEach(inject(function ($controller, $rootScope, $translate, EventServicePositiveMock, AuthServicePositiveMock, feedbackUI) { 21 | scope = $rootScope.$new(); 22 | mockedFeedbackUIService = feedbackUI; 23 | mockedEventService = EventServicePositiveMock; 24 | mockedAuthService = AuthServicePositiveMock; 25 | // init mocks 26 | ctrl = $controller('EventCtrl', { 27 | $scope: scope, 28 | $translate: $translate, 29 | feedbackUI: feedbackUI, 30 | // use mock module 31 | EventService: mockedEventService, 32 | AuthenticationService: mockedAuthService 33 | }); 34 | })); 35 | 36 | it('should get event', function () { 37 | spyOn(mockedEventService, 'getEvent'); 38 | scope.getEvent('1'); 39 | scope.$apply(); 40 | expect(mockedEventService.getEvent).toHaveBeenCalled(); 41 | }); 42 | 43 | it('should not allow transfer invalid values', function () { 44 | spyOn(mockedEventService, 'saveEvent'); 45 | scope.saveEvent({$valid: false}); 46 | scope.$apply(); 47 | expect(mockedEventService.saveEvent).not.toHaveBeenCalled(); 48 | }); 49 | 50 | it('should not allow transfer valid values', function () { 51 | spyOn(mockedEventService, 'saveEvent'); 52 | scope.saveEvent({$valid: true}); 53 | scope.$apply(); 54 | expect(mockedEventService.saveEvent).toHaveBeenCalled(); 55 | }); 56 | 57 | it('should delete event', function () { 58 | spyOn(mockedEventService, 'deleteEvent'); 59 | spyOn(mockedEventService, 'getEvent').and.callThrough(); 60 | scope.deleteEvent(); 61 | scope.$apply(); 62 | expect(mockedEventService.deleteEvent).toHaveBeenCalled(); 63 | }); 64 | 65 | it('should list all events for customer during init', function () { 66 | spyOn(mockedEventService, 'listAllEvents'); 67 | scope.reset(); 68 | expect(mockedEventService.listAllEvents).toHaveBeenCalled(); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/controllers/ctrl.mainSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MainCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('ngSpringBootApp')); 7 | 8 | var MainCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | MainCtrl = $controller('MainCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/controllers/ctrl.navSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: LoginCtrl', function () { 4 | 5 | beforeEach(function () { 6 | // load app module 7 | module('ngSpringBootApp'); 8 | // load app mock module 9 | module('ngSpringBootAppMock'); 10 | }); 11 | 12 | var controller, 13 | mockedFeedbackUIService, 14 | mockedLoginService, 15 | rootScope, 16 | scope; 17 | 18 | // Initialize the controller and a mock scope 19 | beforeEach(inject(function ($controller, $rootScope, $translate, AuthServicePositiveMock, feedbackUI) { 20 | rootScope = $rootScope; 21 | scope = $rootScope.$new(); 22 | mockedFeedbackUIService = feedbackUI; 23 | mockedLoginService = AuthServicePositiveMock; 24 | // init mocks 25 | controller = $controller('NavCtrl', { 26 | $scope: scope, 27 | $translate: $translate, 28 | feedbackUI: mockedFeedbackUIService, 29 | // use mock module 30 | AuthenticationService: mockedLoginService 31 | }); 32 | spyOn(mockedFeedbackUIService, 'appendErrorMsg'); 33 | spyOn(mockedLoginService, 'login').and.callThrough(); 34 | spyOn(mockedLoginService, 'logout').and.callThrough(); 35 | })); 36 | 37 | it('should logout successfully', function () { 38 | rootScope.user = { 39 | username: 'abc', 40 | password: 'pass' 41 | }; 42 | scope.logout(); 43 | scope.$apply(); 44 | expect(mockedLoginService.logout).toHaveBeenCalled(); 45 | expect(rootScope.user).not.toBeDefined(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/services/srv.authSpec.js: -------------------------------------------------------------------------------- 1 | describe('Service: authenticationService', function () { 2 | 'use strict'; 3 | 4 | 5 | // load the controller's module 6 | beforeEach(module('ngSpringBootAppMock')); 7 | 8 | var service, 9 | redirectService, 10 | rootScope, 11 | httpBackend; 12 | 13 | // Initialize the service 14 | beforeEach(inject(function (_$rootScope_, 15 | _$httpBackend_, 16 | RedirectService, 17 | AuthenticationService) { 18 | rootScope = _$rootScope_; 19 | redirectService = RedirectService; 20 | service = AuthenticationService; 21 | httpBackend = _$httpBackend_; 22 | spyOn(redirectService, 'redirect').and.callFake(function () { 23 | }); 24 | })); 25 | 26 | describe('error handling', function () { 27 | 28 | it('should not login with spring errors', function (done) { 29 | httpBackend.expectPOST('./login').respond(400); 30 | service.login({username: 'abc', password: '123'}, function () { 31 | fail(); 32 | }, function (user) { 33 | expect(user.isValid()).not.toBeTruthy(); 34 | done(); 35 | }); 36 | httpBackend.flush(); 37 | }); 38 | 39 | it('should login with user errors', function (done) { 40 | httpBackend.expectPOST('./login').respond(200); 41 | httpBackend.expectGET('./api/login/user').respond(400, { 42 | username: 'abcUser', 43 | userRoles: ['role1', 'roles2'] 44 | }); 45 | service.login({username: 'abc', password: '123'}, function () { 46 | fail(); 47 | }, function (user) { 48 | expect(user.isValid()).not.toBeTruthy(); 49 | done(); 50 | }); 51 | httpBackend.flush(); 52 | }); 53 | 54 | it('should logout with errors', function (done) { 55 | httpBackend.expectPOST('./logout').respond(400); 56 | service.logout(function () { 57 | fail(); 58 | }, function (user) { 59 | expect(user.isValid()).not.toBeTruthy(); 60 | done(); 61 | } 62 | ); 63 | httpBackend.flush(); 64 | }); 65 | }); 66 | 67 | describe('normal handling', function () { 68 | 69 | it('should login successfully', function (done) { 70 | httpBackend.expectPOST('./login').respond(200); 71 | httpBackend.expectGET('./api/login/user').respond(200, { 72 | username: 'abcUser', 73 | userRoles: ['role1', 'roles2'] 74 | }); 75 | service.login({username: 'abc', password: '123'}, function (user) { 76 | expect(user.isValid()).toBeTruthy(); 77 | expect(user.username).toBe('abcUser'); 78 | done(); 79 | }); 80 | httpBackend.flush(); 81 | }); 82 | 83 | it('should revalidate existing user session', function (done) { 84 | httpBackend.expectGET('./api/login/user').respond(200, { 85 | username: 'abcUser', 86 | userRoles: ['role1', 'roles2'] 87 | }); 88 | service.checkUser(function (user) { 89 | expect(user.isValid()).toBeTruthy(); 90 | done(); 91 | }, function () { 92 | fail(); 93 | }); 94 | httpBackend.flush(); 95 | }); 96 | 97 | it('should logout successfully', function (done) { 98 | httpBackend.expectPOST('./logout').respond(200); 99 | service.logout(function (user) { 100 | expect(user.isValid()).not.toBeTruthy(); 101 | done(); 102 | }); 103 | httpBackend.flush(); 104 | }); 105 | 106 | it('should invalidate non existing user session', function (done) { 107 | httpBackend.expectGET('./api/login/user').respond(400); 108 | service.checkUser(function () { 109 | fail(); 110 | }, function (user) { 111 | expect(user.isValid()).not.toBeTruthy(); 112 | done(); 113 | }); 114 | httpBackend.flush(); 115 | }); 116 | 117 | it('should handle invalid user details', function (done) { 118 | httpBackend.expectGET('./api/login/user').respond(200, { 119 | user: 'abcUser' 120 | }); 121 | service.checkUser(function (user) { 122 | expect(user.isValid()).not.toBeTruthy(); 123 | done(); 124 | }, function () { 125 | fail(); 126 | }); 127 | httpBackend.flush(); 128 | }); 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/services/srv.domainEventSpec.js: -------------------------------------------------------------------------------- 1 | describe('Service: Domain', function () { 2 | 'use strict'; 3 | 4 | // load the controller's module 5 | beforeEach(module('ngSpringBootApp')); 6 | 7 | var service; 8 | 9 | // Initialize the service 10 | beforeEach(inject(function (Domain) { 11 | service = Domain; 12 | })); 13 | 14 | describe('Event', function () { 15 | 16 | it('should convert dates to strings to DTO', function () { 17 | var eventDTO = service.Event.build({ 18 | 'eventDescription': 'ss', 19 | 'startDate': new Date(2015, 7, 13, 0, 0, 0, 0), 20 | 'endDate': new Date(2014, 1, 11, 0, 0, 0, 0) 21 | }).convertToDTO(); 22 | expect(eventDTO.startDate.getTime()).toBe(new Date(2015, 7, 13, 12, 0, 0, 0).getTime()); 23 | expect(eventDTO.endDate.getTime()).toBe(new Date(2014, 1, 11, 12, 0, 0, 0).getTime()); 24 | }); 25 | 26 | // TODO more tests 27 | 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/services/srv.domainValidationSpec.js: -------------------------------------------------------------------------------- 1 | describe('Service: Domain', function () { 2 | 'use strict'; 3 | 4 | // load the controller's module 5 | beforeEach(module('ngSpringBootApp')); 6 | 7 | var service; 8 | 9 | // Initialize the service 10 | beforeEach(inject(function (Domain) { 11 | service = Domain; 12 | })); 13 | 14 | describe('user validation and security tests', function () { 15 | 16 | it('should validate user', function () { 17 | var validUser = service.User.build('user', '1', ['role1']); 18 | expect(validUser.isValid()).toBeTruthy(); 19 | var invalidUser1 = service.User.build(); 20 | expect(invalidUser1.isValid()).not.toBeTruthy(); 21 | var invalidUser2 = service.User.build('anonymousUser'); 22 | expect(invalidUser2.isValid()).not.toBeTruthy(); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/services/srv.errorHandlingSpec.js: -------------------------------------------------------------------------------- 1 | describe('Service: ErrorHandling', function () { 2 | 'use strict'; 3 | 4 | // load the controller's module 5 | beforeEach(module('ngSpringBootApp')); 6 | 7 | var service, rootScope; 8 | 9 | // Initialize the service 10 | beforeEach(inject(function ($rootScope, ErrorHandlingService) { 11 | rootScope = $rootScope; 12 | service = ErrorHandlingService; 13 | })); 14 | 15 | it('should build validation messages for the http code 400', function (done) { 16 | service.resolve({ 17 | error: { 18 | data: [{'messageTemplate': 'validation.date.range_error'}] 19 | }, 20 | status: 500 21 | }, function (msg) { 22 | if (msg) { 23 | done(); 24 | } 25 | }); 26 | rootScope.$apply(); 27 | }); 28 | 29 | it('should build default error messages for the http code 403', function (done) { 30 | service.resolve({error: {}, status: 403}, function (msg) { 31 | if (msg) { 32 | done(); 33 | } 34 | }); 35 | rootScope.$apply(); 36 | }); 37 | 38 | it('should build default error messages for the http code 404', function (done) { 39 | service.resolve({error: {}, status: 404}, function (msg) { 40 | if (msg) { 41 | done(); 42 | } 43 | }); 44 | rootScope.$apply(); 45 | }); 46 | 47 | it('should build default error messages for the http code 405', function (done) { 48 | service.resolve({error: {}, status: 405}, function (msg) { 49 | if (msg) { 50 | done(); 51 | } 52 | }); 53 | rootScope.$apply(); 54 | }); 55 | 56 | it('should build default error messages for the http code 409', function (done) { 57 | service.resolve({error: {}, status: 409}, function (msg) { 58 | if (msg) { 59 | done(); 60 | } 61 | }); 62 | rootScope.$apply(); 63 | }); 64 | 65 | it('should build default error messages for the http code 500', function (done) { 66 | service.resolve({error: {}, status: 500}, function (msg) { 67 | if (msg) { 68 | done(); 69 | } 70 | }); 71 | rootScope.$apply(); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/frontend/spec/services/srv.eventSpec.js: -------------------------------------------------------------------------------- 1 | describe('Service: EventService', function () { 2 | 'use strict'; 3 | 4 | // load the controller's module 5 | beforeEach(module('ngSpringBootApp')); 6 | 7 | var service, 8 | domain, 9 | scope, 10 | httpBackend; 11 | 12 | // Initialize the service 13 | beforeEach(inject(function (EventService, $rootScope, $httpBackend, Domain) { 14 | scope = $rootScope.$new(); 15 | service = EventService; 16 | httpBackend = $httpBackend; 17 | domain = Domain; 18 | })); 19 | 20 | describe('error handling', function () { 21 | it('should not call normal handler', function (done) { 22 | httpBackend.expectGET('/api/events').respond(400); 23 | service.listAllEvents(function () { 24 | fail(); 25 | }, function () { 26 | done(); 27 | }); 28 | httpBackend.flush(); 29 | }); 30 | }); 31 | 32 | describe('normal handling', function () { 33 | 34 | describe('read', function () { 35 | 36 | it('should list events', function (done) { 37 | var events; 38 | httpBackend.expectGET('/api/events').respond(200, [{ 39 | 'insertDate': '2015-07-07', 40 | 'deleted': false, 41 | 'eventId': '144', 42 | 'eventDescription': 'Beschreibung', 43 | 'startDate': '2015-07-17', 44 | 'endDate': '2015-07-17' 45 | }, { 46 | 'insertDate': '2015-07-07', 47 | 'deleted': false, 48 | 'eventId': '145', 49 | 'eventDescription': 'Beschreibung', 50 | 'startDate': '2015-07-28', 51 | 'endDate': '2015-07-30' 52 | }]); 53 | service.listAllEvents(function (response) { 54 | events = response; 55 | expect(events.length).toBe(2); 56 | expect(events[0].eventId).toBe('144'); 57 | expect(events[1].eventId).toBe('145'); 58 | done(); 59 | }); 60 | httpBackend.flush(); 61 | }); 62 | 63 | it('get event by eventId for customer', function (done) { 64 | var event; 65 | httpBackend.expectGET('/api/events/1').respond(200, { 66 | 'insertDate': '2015-07-07', 67 | 'deleted': false, 68 | 'eventId': '145', 69 | 'eventDescription': 'Beschreibung', 70 | 'startDate': '2015-07-28', 71 | 'endDate': '2015-07-30' 72 | }); 73 | service.getEvent('1', function (response) { 74 | event = response; 75 | expect(event.eventId).toBe('145'); 76 | done(); 77 | }); 78 | httpBackend.flush(); 79 | }); 80 | }); 81 | 82 | describe('write', function () { 83 | 84 | it('should create event', function (done) { 85 | httpBackend.expectPOST('/api/events').respond(200); 86 | service.saveEvent(domain.Event.build({ 87 | insertDate: new Date(), 88 | eventDescription: 'event', 89 | startDate: new Date(), 90 | endDate: new Date() 91 | }), function () { 92 | done(); 93 | }); 94 | httpBackend.flush(); 95 | }); 96 | 97 | it('should update event', function (done) { 98 | httpBackend.expectPUT('/api/events/1').respond(200); 99 | service.saveEvent(domain.Event.build({ 100 | insertDate: new Date(), 101 | eventId: 1, 102 | eventDescription: 'event', 103 | startDate: new Date(), 104 | endDate: new Date() 105 | }), function () { 106 | done(); 107 | }); 108 | httpBackend.flush(); 109 | }); 110 | 111 | }); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/AngularSpringApplicationTest.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.ActiveProfiles; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | import org.springframework.test.context.web.WebAppConfiguration; 9 | 10 | @RunWith(SpringJUnit4ClassRunner.class) 11 | @ActiveProfiles("test") 12 | @SpringApplicationConfiguration(classes = AngularSpringApplication.class) 13 | @WebAppConfiguration 14 | public class AngularSpringApplicationTest { 15 | 16 | @Test 17 | public void contextLoads() { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/domain/EventDomainTest.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain; 2 | 3 | import ngSpring.demo.domain.entities.Event; 4 | import ngSpring.demo.domain.fixtures.EventFixture; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.notNullValue; 14 | 15 | 16 | /** 17 | * @author hypery2k 18 | */ 19 | @RunWith(Parameterized.class) 20 | public class EventDomainTest { 21 | 22 | private Event eventToTest; 23 | 24 | public EventDomainTest(Event event) { 25 | this.eventToTest = event; 26 | } 27 | 28 | @Parameterized.Parameters 29 | public static Collection data() { 30 | Event invalidEventWithInvalidBranch = EventFixture.buildInvalidEventDataWithoutBranch(); 31 | Event invalidEventWithValidBranch = EventFixture.buildInvalidEventDataWithoutBranch(); 32 | // add them as parameters 33 | final Object[][] data = new Object[][]{ 34 | {EventFixture.buildValidEventData()}, 35 | {invalidEventWithInvalidBranch}, 36 | {invalidEventWithValidBranch}, 37 | {EventFixture.buildInvalidEventData()}, 38 | }; 39 | return Arrays.asList(data); 40 | 41 | } 42 | 43 | @Test 44 | public void shouldHandleToString() { 45 | assertThat(this.eventToTest.toString(), notNullValue()); 46 | } 47 | 48 | @Test 49 | public void shouldHandleHashCode() { 50 | assertThat(this.eventToTest.hashCode(), notNullValue()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/domain/UserDomainTest.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain; 2 | 3 | import ngSpring.demo.domain.entities.User; 4 | import ngSpring.demo.domain.fixtures.UserFixture; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.notNullValue; 14 | 15 | /** 16 | * @author hypery2k 17 | */ 18 | @RunWith(Parameterized.class) 19 | public class UserDomainTest { 20 | 21 | private User userToTest; 22 | 23 | public UserDomainTest(User user) { 24 | this.userToTest = user; 25 | } 26 | 27 | @Parameterized.Parameters 28 | public static Collection data() { 29 | User userWithName = UserFixture.invalidData(); 30 | userWithName.setUsername("test"); 31 | // add them as parameters 32 | final Object[][] data = new Object[][]{ 33 | {UserFixture.validData()}, 34 | {userWithName}, 35 | {UserFixture.invalidData()} 36 | }; 37 | return Arrays.asList(data); 38 | 39 | } 40 | 41 | @Test 42 | public void shouldHandleToString() { 43 | assertThat(this.userToTest.toString(), notNullValue()); 44 | } 45 | 46 | @Test 47 | public void shouldHandleHashCode() { 48 | assertThat(this.userToTest.hashCode(), notNullValue()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/domain/fixtures/EventFixture.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.fixtures; 2 | 3 | import ngSpring.demo.domain.entities.Event; 4 | import org.apache.commons.lang3.time.DateUtils; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * @author hypery2k 10 | */ 11 | public class EventFixture { 12 | 13 | 14 | public static Event buildValidEventData() { 15 | return Event.builder() 16 | .eventDescription("Test Event") 17 | .startDate(new Date()) 18 | .endDate(DateUtils.addDays(new Date(), 2)) 19 | .build(); 20 | } 21 | 22 | public static Event buildInvalidEventDataWithoutBranch() { 23 | return Event.builder() 24 | .eventId(null) 25 | .eventDescription(null) 26 | .startDate(new Date()) 27 | .endDate(DateUtils.addDays(new Date(), -2)) 28 | .build(); 29 | } 30 | 31 | public static Event buildInvalidEventDataWithoutBranchId() { 32 | return Event.builder() 33 | .eventId(null) 34 | .eventDescription(null) 35 | .startDate(new Date()) 36 | .endDate(DateUtils.addDays(new Date(), -2)) 37 | .build(); 38 | } 39 | 40 | public static Event buildInvalidEventData() { 41 | return Event.builder() 42 | .eventId(null) 43 | .eventDescription(null) 44 | .startDate(new Date()) 45 | .endDate(DateUtils.addDays(new Date(), -2)) 46 | .build(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/domain/fixtures/UserFixture.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.fixtures; 2 | 3 | import ngSpring.demo.domain.entities.User; 4 | 5 | public class UserFixture { 6 | public static User validData() { 7 | return User.builder() 8 | .enabled(true) 9 | .username("user") 10 | .password("password") 11 | .build(); 12 | } 13 | 14 | public static User invalidData() { 15 | return User.builder().build(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/domain/validation/EventValidationTest.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.validation; 2 | 3 | import ngSpring.demo.domain.entities.Event; 4 | import ngSpring.demo.domain.fixtures.EventFixture; 5 | import ngSpring.demo.util.ValidationTestBase; 6 | import org.junit.Test; 7 | 8 | import javax.validation.ConstraintViolation; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.containsInAnyOrder; 14 | import static org.hamcrest.Matchers.equalTo; 15 | import static org.hamcrest.core.Is.is; 16 | 17 | 18 | /** 19 | * @author hypery2k 20 | */ 21 | public class EventValidationTest extends ValidationTestBase { 22 | 23 | @Test 24 | public void shouldAcceptValidEntity() { 25 | //given 26 | Event event = EventFixture.buildValidEventData(); 27 | //when 28 | Set> constraintViolations = validator.validate(event); 29 | //then 30 | List messages = getValidationMessages(constraintViolations); 31 | assertThat(messages.size(), is(equalTo(0))); 32 | } 33 | 34 | @Test 35 | public void shouldValidateRequiredFields() { 36 | //given 37 | Event eventWithValidationErrors = EventFixture.buildInvalidEventData(); 38 | //when 39 | Set> constraintViolations = validator.validate(eventWithValidationErrors); 40 | //then 41 | List messages = getValidationMessages(constraintViolations); 42 | assertThat(constraintViolations.size(), is(equalTo(2))); 43 | assertThat(messages, containsInAnyOrder( 44 | "{validation.date.range_error}", 45 | "{javax.validation.constraints.NotNull.message}")); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/domain/validation/UserValidationTest.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.domain.validation; 2 | 3 | import ngSpring.demo.domain.entities.User; 4 | import ngSpring.demo.domain.fixtures.UserFixture; 5 | import ngSpring.demo.util.ValidationTestBase; 6 | import org.junit.Test; 7 | 8 | import javax.validation.ConstraintViolation; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.containsInAnyOrder; 14 | import static org.hamcrest.Matchers.equalTo; 15 | import static org.hamcrest.core.Is.is; 16 | 17 | public class UserValidationTest extends ValidationTestBase { 18 | @Test 19 | public void shouldAcceptValidEntity() { 20 | // given 21 | User user = UserFixture.validData(); 22 | // when 23 | Set> constraintViolations = validator 24 | .validate(user); 25 | // then 26 | List messages = getValidationMessages(constraintViolations); 27 | assertThat(messages.size(), is(equalTo(0))); 28 | } 29 | 30 | @Test 31 | public void shouldValidateRequiredFields() { 32 | // given 33 | User user = UserFixture.invalidData(); 34 | // when 35 | Set> constraintViolations = validator 36 | .validate(user); 37 | // then 38 | List messages = getValidationMessages(constraintViolations); 39 | assertThat(constraintViolations.size(), is(equalTo(2))); 40 | assertThat(messages, containsInAnyOrder( 41 | "{javax.validation.constraints.NotNull.message}", 42 | "{javax.validation.constraints.NotNull.message}")); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/errorhandling/ControllerErrorHandlingTestBase.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.errorhandling; 2 | 3 | import ngSpring.demo.util.MockedMvcTestBase; 4 | import org.junit.Test; 5 | 6 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 7 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 8 | 9 | public class ControllerErrorHandlingTestBase extends MockedMvcTestBase { 10 | 11 | @Test 12 | public void shouldHandleInvalidURLWith404() throws Exception { 13 | // given 14 | String uri = "/really/unkown/"; 15 | // when and then 16 | this.mockMvc 17 | .perform(get(uri)) 18 | .andExpect(status().isNotFound()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/integration/rest/EventControllerIT.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.integration.rest; 2 | 3 | import com.jayway.restassured.RestAssured; 4 | import com.jayway.restassured.http.ContentType; 5 | import ngSpring.demo.domain.entities.Event; 6 | import ngSpring.demo.util.RestITBase; 7 | import org.apache.commons.httpclient.HttpStatus; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import java.text.ParseException; 12 | import java.text.SimpleDateFormat; 13 | import java.util.Arrays; 14 | import java.util.Date; 15 | 16 | public class EventControllerIT extends RestITBase { 17 | 18 | private Date startDate; 19 | 20 | private Date endDate; 21 | 22 | private Event sampleEvent1; 23 | 24 | private Event sampleEvent2; 25 | 26 | private Event sampleEvent3; 27 | 28 | @Before 29 | public void setUp() throws ParseException { 30 | super.setUp(); 31 | 32 | startDate = new SimpleDateFormat("yyyy-MM-dd").parse("2015-06-01"); 33 | endDate = new SimpleDateFormat("yyyy-MM-dd").parse("2015-06-02"); 34 | sampleEvent1 = new Event("Event1", startDate, endDate); 35 | sampleEvent2 = new Event("Event2", startDate, endDate); 36 | sampleEvent3 = new Event("Event3", startDate, endDate); 37 | getEventRepository().save(Arrays.asList(sampleEvent1, sampleEvent2, sampleEvent3)); 38 | } 39 | 40 | // Negative test cases 41 | 42 | @Test 43 | public void shouldNotPutEventSinceStartDateIsAfterEndDate() { 44 | String json = new JSONBuilder() 45 | .add("eventId", sampleEvent1.getEventId()) 46 | .add("eventDescription", "MeinAngpasstesEvent") 47 | .add("startDate", "2015-06-15").add("endDate", "2015-06-11") 48 | .build(); 49 | loginWithCorrectCredentials() 50 | .and() 51 | .body(json) 52 | .and() 53 | .contentType(ContentType.JSON) 54 | .expect() 55 | .statusCode(HttpStatus.SC_BAD_REQUEST) 56 | .when() 57 | .put("/api/events/{eventId}", sampleEvent1.getEventId()); 58 | } 59 | 60 | @Test 61 | public void shouldNotPostEventSinceStartDateIsAfterEndDate() { 62 | String json = new JSONBuilder() 63 | .add("eventDescription", "MeinAngpasstesEvent") 64 | .add("startDate", "2015-06-15").add("endDate", "2015-06-11") 65 | .build(); 66 | loginWithCorrectCredentials() 67 | .and() 68 | .body(json) 69 | .and() 70 | .contentType(ContentType.JSON) 71 | .expect() 72 | .statusCode(HttpStatus.SC_BAD_REQUEST) 73 | .when() 74 | .post("/api/events"); 75 | } 76 | 77 | @Test 78 | public void shouldFailWithWrongAuth() { 79 | try { 80 | loginWithIncorrectCredentials() 81 | .expect() 82 | .statusCode(HttpStatus.SC_UNAUTHORIZED) 83 | .when() 84 | .get("/api/events/{eventId}", sampleEvent1.getEventId()); 85 | } finally { 86 | RestAssured.reset(); 87 | } 88 | } 89 | 90 | @Test 91 | public void shouldFailWithNoAuth() throws Exception { 92 | try { 93 | loginWithEmptyCredentials() 94 | .expect() 95 | .statusCode(HttpStatus.SC_MOVED_TEMPORARILY) 96 | .when() 97 | .get("/api/events/{eventId}", sampleEvent1.getEventId()); 98 | } finally { 99 | RestAssured.reset(); 100 | } 101 | } 102 | 103 | @Test 104 | public void shouldFetchEvent() { 105 | loginWithCorrectCredentials() 106 | .expect() 107 | .statusCode(HttpStatus.SC_OK) 108 | .when() 109 | .get("/api/events/{eventId}", sampleEvent1.getEventId()); 110 | } 111 | 112 | @Test 113 | public void shouldNotFindEvent() { 114 | loginWithCorrectCredentials() 115 | .expect() 116 | .statusCode(HttpStatus.SC_NOT_FOUND) 117 | .when() 118 | .get("/api/events/{eventId}", "42"); 119 | } 120 | 121 | // positive test cases 122 | 123 | @Test 124 | public void shouldFetchAll() { 125 | loginWithCorrectCredentials() 126 | .expect() 127 | .statusCode(HttpStatus.SC_OK) 128 | .when() 129 | .get("/api/events"); 130 | } 131 | 132 | @Test 133 | public void shouldPostEvent() { 134 | String json = new JSONBuilder() 135 | .add("eventDescription", "MeinAngpasstesEvent") 136 | .add("startDate", "2015-06-01").add("endDate", "2015-06-11") 137 | .build(); 138 | loginWithCorrectCredentials() 139 | .and() 140 | .body(json) 141 | .and() 142 | .contentType(ContentType.JSON) 143 | .expect() 144 | .statusCode(HttpStatus.SC_OK) 145 | .when() 146 | .post("/api/events"); 147 | } 148 | 149 | @Test 150 | public void shouldPutEvent() { 151 | String json = new JSONBuilder() 152 | .add("eventId", sampleEvent1.getEventId()) 153 | .add("eventDescription", "MeinAngpasstesEvent") 154 | .add("startDate", "2015-06-01").add("endDate", "2015-06-11") 155 | .build(); 156 | loginWithCorrectCredentials() 157 | .and() 158 | .body(json) 159 | .and() 160 | .contentType(ContentType.JSON) 161 | .expect() 162 | .statusCode(HttpStatus.SC_OK) 163 | .when() 164 | .put("/api/events/{eventId}", sampleEvent1.getEventId()); 165 | } 166 | 167 | @Test 168 | public void shouldDeleteEvent() { 169 | loginWithCorrectCredentials() 170 | .expect() 171 | .statusCode(HttpStatus.SC_OK) 172 | .when() 173 | .delete("/api/events/{eventId}", sampleEvent1.getEventId()); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/integration/ui/LoginSerenityIT.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.integration.ui; 2 | 3 | import net.serenitybdd.junit.runners.SerenityParameterizedRunner; 4 | import net.thucydides.core.annotations.*; 5 | import net.thucydides.junit.annotations.UseTestDataFrom; 6 | import ngSpring.demo.integration.ui.steps.LoginSteps; 7 | import ngSpring.demo.integration.ui.util.AbstractSerenityITTestBase; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | /** 12 | * @author mreinhardt 13 | */ 14 | @WithTags({ 15 | @WithTag(type = "epic", name = "User management") 16 | }) 17 | @Narrative(text = "Login") 18 | @RunWith(SerenityParameterizedRunner.class) 19 | @UseTestDataFrom(value = "testdata/login.csv") 20 | public class LoginSerenityIT extends AbstractSerenityITTestBase { 21 | 22 | @Steps 23 | public LoginSteps loginSteps; 24 | 25 | // test data 26 | 27 | private String username; 28 | 29 | private String password; 30 | 31 | private boolean fail; 32 | 33 | @WithTags({ 34 | @WithTag(type = "feature", name = "Login"), 35 | @WithTag(type = "feature", name = "User"), 36 | @WithTag(type = "testtype", name = "postdeploy") 37 | }) 38 | @Test 39 | @Issues(value = {"#9"}) 40 | public void loginShouldWork() { 41 | loginSteps.performLogin(this.username, this.password); 42 | if (fail) { 43 | loginSteps.userShouldSeeAnErrorMessage(); 44 | } else { 45 | loginSteps.userShouldSeeNoErrorMessage(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/integration/ui/pages/CmsPage.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.integration.ui.pages; 2 | 3 | import net.serenitybdd.core.annotations.findby.FindBy; 4 | import net.serenitybdd.core.pages.PageObject; 5 | import net.thucydides.core.webdriver.exceptions.ElementShouldBeInvisibleException; 6 | import org.openqa.selenium.WebDriver; 7 | import org.openqa.selenium.WebElement; 8 | 9 | /** 10 | * Common page, e.g. for messages ... 11 | * 12 | * @author mreinhardt 13 | */ 14 | public abstract class CmsPage extends PageObject { 15 | 16 | public CmsPage(WebDriver driver) { 17 | super(driver); 18 | } 19 | 20 | @FindBy(xpath = "//*[contains(@class, 'alert-danger')]/button[contains(@class, 'close')]") 21 | private WebElement closeErrorMessageButton; 22 | 23 | @FindBy(xpath = "//*[contains(@class, 'alert-info')]/button[contains(@class, 'close')]") 24 | private WebElement closeInfoMessageButton; 25 | 26 | @FindBy(xpath = "//div[contains(@class,'block-ui-message-container')]//div[contains(@class,'block-ui-message')]") 27 | protected WebElement loadingIndicator; 28 | 29 | public boolean errorMessageIsVisible() { 30 | waitForLoader(); 31 | return containsElements(".alert-danger"); 32 | } 33 | 34 | public boolean infoMessageIsVisible() { 35 | waitForLoader(); 36 | return containsElements(".alert-info"); 37 | } 38 | 39 | public boolean onlyOneErrorMessageIsVisible() { 40 | waitForLoader(); 41 | return findAll(".alert-danger").size() == 1; 42 | } 43 | 44 | public boolean onlyOneInfoMessageIsVisible() { 45 | waitForLoader(); 46 | return findAll(".alert-info").size() == 1; 47 | } 48 | 49 | public void closeInfoMessageButton() { 50 | waitForLoader(); 51 | element(closeInfoMessageButton).click(); 52 | } 53 | 54 | public void closeErrorMessageButton() { 55 | waitForLoader(); 56 | element(closeErrorMessageButton).click(); 57 | } 58 | 59 | public void click(WebElement webElement) { 60 | waitForLoader(); 61 | element(webElement).waitUntilEnabled(); 62 | element(webElement).click(); 63 | waitForLoader(); 64 | } 65 | 66 | public void enterText(WebElement webElement, String text) { 67 | waitForLoader(); 68 | element(webElement).waitUntilEnabled(); 69 | element(webElement).clear(); 70 | element(webElement).sendKeys(text); 71 | } 72 | 73 | 74 | public void select(WebElement webElement, String text) { 75 | waitForLoader(); 76 | element(webElement).waitUntilEnabled(); 77 | element(webElement).selectByVisibleText(text); 78 | waitForLoader(); 79 | } 80 | 81 | public void waitForLoader() { 82 | 83 | try { 84 | element(loadingIndicator).waitUntilNotVisible(); 85 | } catch (ElementShouldBeInvisibleException ignored) { 86 | // we ignore here the element not visible execption 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/integration/ui/pages/LoginPage.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.integration.ui.pages; 2 | 3 | import net.serenitybdd.core.annotations.findby.FindBy; 4 | import net.thucydides.core.annotations.DefaultUrl; 5 | import org.openqa.selenium.WebDriver; 6 | import org.openqa.selenium.WebElement; 7 | 8 | /** 9 | * @author mreinhardt 10 | */ 11 | 12 | @DefaultUrl("http://localhost:9000/login") 13 | public class LoginPage extends CmsPage { 14 | 15 | public LoginPage(WebDriver driver) { 16 | super(driver); 17 | } 18 | 19 | @FindBy(id = "username") 20 | private WebElement userInput; 21 | 22 | @FindBy(id = "password") 23 | private WebElement passwordInput; 24 | 25 | @FindBy(id = "login") 26 | private WebElement loginButton; 27 | 28 | 29 | public void inputUserName(String emailAddress) { 30 | enterText(userInput, emailAddress); 31 | } 32 | 33 | public void inputPassword(String password) { 34 | enterText(passwordInput, password); 35 | } 36 | 37 | public void clickOnLogin() { 38 | click(loginButton); 39 | } 40 | 41 | public boolean loginFormVisible() { 42 | return element(userInput).isCurrentlyVisible() && element(passwordInput).isCurrentlyVisible(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/integration/ui/steps/LoginSteps.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.integration.ui.steps; 2 | 3 | import net.thucydides.core.annotations.Step; 4 | import net.thucydides.core.annotations.StepGroup; 5 | import net.thucydides.core.pages.Pages; 6 | import net.thucydides.core.steps.ScenarioSteps; 7 | import ngSpring.demo.integration.ui.pages.LoginPage; 8 | 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | 11 | /** 12 | * @author mreinhardt 13 | */ 14 | public class LoginSteps extends ScenarioSteps { 15 | 16 | private static final long serialVersionUID = -566491664850250304L; 17 | 18 | public LoginSteps(Pages pages) { 19 | super(pages); 20 | } 21 | 22 | // PAGES 23 | 24 | public LoginPage loginPage() { 25 | return getPages().currentPageAt(LoginPage.class); 26 | } 27 | 28 | // STEPS 29 | 30 | @Step 31 | public void inputUsername(String user) { 32 | loginPage().inputUserName(user); 33 | } 34 | 35 | @Step 36 | public void inputPassword(String pass) { 37 | loginPage().inputPassword(pass); 38 | } 39 | 40 | @Step 41 | public void clickOnLogin() { 42 | loginPage().clickOnLogin(); 43 | } 44 | 45 | @StepGroup 46 | public void performLogin(String email, String pass) { 47 | inputUsername(email); 48 | inputPassword(pass); 49 | clickOnLogin(); 50 | } 51 | 52 | @Step 53 | public void userShouldSeeLogin() { 54 | assertThat("Should be on login page ", loginPage().loginFormVisible()); 55 | } 56 | 57 | 58 | @Step 59 | public void userShouldSeeAnErrorMessage() { 60 | assertThat("There should be an error message ", loginPage().errorMessageIsVisible()); 61 | assertThat("There should be only one info message ", loginPage().onlyOneErrorMessageIsVisible()); 62 | } 63 | 64 | @Step 65 | public void userShouldSeeNoErrorMessage() { 66 | assertThat("There should be no error message ", !loginPage().errorMessageIsVisible()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/integration/ui/util/AbstractSerenityITTestBase.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.integration.ui.util; 2 | 3 | import net.serenitybdd.junit.runners.SerenityRunner; 4 | import net.thucydides.core.annotations.Managed; 5 | import net.thucydides.core.annotations.ManagedPages; 6 | import net.thucydides.core.pages.Pages; 7 | import net.thucydides.junit.annotations.Concurrent; 8 | import org.junit.runner.RunWith; 9 | import org.openqa.selenium.WebDriver; 10 | 11 | @Concurrent 12 | @RunWith(SerenityRunner.class) 13 | public abstract class AbstractSerenityITTestBase { 14 | 15 | @Managed 16 | public WebDriver webdriver; 17 | 18 | @ManagedPages 19 | public Pages pages; 20 | } 21 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/transformer/EventTransformerTest.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.transformer; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import ngSpring.demo.domain.dto.EventDTO; 5 | import ngSpring.demo.domain.entities.Event; 6 | import ngSpring.demo.domain.fixtures.EventFixture; 7 | import ngSpring.demo.transformer.impl.EventTransformer; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.junit.runners.Parameterized; 12 | import org.mockito.InjectMocks; 13 | import org.mockito.MockitoAnnotations; 14 | 15 | import java.util.Arrays; 16 | import java.util.Collection; 17 | 18 | import static org.hamcrest.MatcherAssert.assertThat; 19 | import static org.hamcrest.Matchers.equalTo; 20 | 21 | @RunWith(Parameterized.class) 22 | @RequiredArgsConstructor 23 | public class EventTransformerTest { 24 | 25 | @InjectMocks 26 | private EventTransformer transformer; 27 | 28 | // test data 29 | 30 | private final Event entity; 31 | 32 | private final boolean shouldFail; 33 | 34 | 35 | @Parameterized.Parameters 36 | public static Collection data() { 37 | // add them as parameters 38 | final Object[][] data = new Object[][]{ 39 | // @formatter:off 40 | {EventFixture.buildValidEventData(), false}, 41 | {EventFixture.buildInvalidEventData(), true}, 42 | {EventFixture.buildInvalidEventDataWithoutBranchId(), true}, 43 | {EventFixture.buildInvalidEventDataWithoutBranch(), true}, 44 | // @formatter:on 45 | }; 46 | return Arrays.asList(data); 47 | 48 | } 49 | 50 | @Before 51 | public void setup() { 52 | MockitoAnnotations.initMocks(this); 53 | } 54 | 55 | @Test 56 | public void shouldTransformCorrectly() throws Exception { 57 | try { 58 | EventDTO dto = transformer.transformToDTO(entity); 59 | Event entityTransformed = transformer.transformToEntity(dto); 60 | // ensure that transformation worked 61 | assertThat(this.entity, equalTo(entityTransformed)); 62 | } catch (Exception e) { 63 | if (!this.shouldFail) { 64 | throw e; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/transformer/UserTransformerTest.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.transformer; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import ngSpring.demo.domain.dto.UserDTO; 5 | import ngSpring.demo.domain.entities.User; 6 | import ngSpring.demo.domain.fixtures.UserFixture; 7 | import ngSpring.demo.repositories.UserRepository; 8 | import ngSpring.demo.transformer.impl.UserTransformer; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.junit.runners.Parameterized; 13 | import org.mockito.InjectMocks; 14 | import org.mockito.Mock; 15 | import org.mockito.MockitoAnnotations; 16 | 17 | import java.util.Arrays; 18 | import java.util.Collection; 19 | 20 | import static org.hamcrest.MatcherAssert.assertThat; 21 | import static org.hamcrest.Matchers.equalTo; 22 | import static org.mockito.Matchers.any; 23 | import static org.mockito.Mockito.when; 24 | 25 | @RunWith(Parameterized.class) 26 | @RequiredArgsConstructor 27 | public class UserTransformerTest { 28 | 29 | @InjectMocks 30 | private UserTransformer transformer; 31 | 32 | @Mock 33 | private UserRepository userRepository; 34 | 35 | // test data 36 | 37 | private final User entity; 38 | 39 | private final boolean shouldFail; 40 | 41 | 42 | @Parameterized.Parameters 43 | public static Collection data() { 44 | // add them as parameters 45 | final Object[][] data = new Object[][]{ 46 | // @formatter:off 47 | {UserFixture.validData(), false}, 48 | {UserFixture.invalidData(), true}, 49 | // @formatter:on 50 | }; 51 | return Arrays.asList(data); 52 | 53 | } 54 | 55 | @Before 56 | public void setup() { 57 | MockitoAnnotations.initMocks(this); 58 | when(userRepository.findByUsernameAndDeletedFalse(any(String.class))).thenReturn(entity); 59 | } 60 | 61 | @Test 62 | public void shouldTransformCorrectly() throws Exception { 63 | try { 64 | UserDTO dto = transformer.transformToDTO(entity); 65 | User entityTransformed = transformer.transformToEntity(dto); 66 | // ensure that transformation worked 67 | assertThat(this.entity, equalTo(entityTransformed)); 68 | } catch (Exception e) { 69 | if (!this.shouldFail) { 70 | throw e; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/util/MockedMvcTestBase.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.util; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.SerializationFeature; 7 | import ngSpring.demo.AngularSpringApplication; 8 | import ngSpring.demo.domain.entities.Event; 9 | import ngSpring.demo.repositories.EventRepository; 10 | import org.junit.Before; 11 | import org.junit.runner.RunWith; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.SpringApplicationConfiguration; 14 | import org.springframework.http.MediaType; 15 | import org.springframework.test.context.ActiveProfiles; 16 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 17 | import org.springframework.test.context.web.WebAppConfiguration; 18 | import org.springframework.test.web.servlet.MockMvc; 19 | import org.springframework.web.context.WebApplicationContext; 20 | 21 | import java.nio.charset.Charset; 22 | import java.text.DateFormat; 23 | import java.text.ParseException; 24 | import java.text.SimpleDateFormat; 25 | import java.util.ArrayList; 26 | import java.util.Date; 27 | import java.util.List; 28 | 29 | import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 30 | 31 | @RunWith(SpringJUnit4ClassRunner.class) 32 | @ActiveProfiles("test") 33 | @SpringApplicationConfiguration(classes = AngularSpringApplication.class) 34 | @WebAppConfiguration 35 | public abstract class MockedMvcTestBase { 36 | 37 | @Autowired 38 | protected WebApplicationContext webApplicationContext; 39 | 40 | @Autowired 41 | protected EventRepository eventRepository; 42 | 43 | protected MockMvc mockMvc; 44 | 45 | protected DateFormat dfmt = new SimpleDateFormat("yyyy-MM-dd"); 46 | 47 | protected MediaType contentType = new MediaType( 48 | MediaType.APPLICATION_JSON.getType(), 49 | MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); 50 | 51 | @Before 52 | public void setup() throws Exception { 53 | this.mockMvc = webAppContextSetup(webApplicationContext).build(); 54 | this.eventRepository.deleteAll(); 55 | } 56 | 57 | protected List loadEvents() throws ParseException { 58 | List eventList = new ArrayList(); 59 | eventList.add(createEvent("Viele neue Preise", dfmt.parse("2015-06-01"), null)); 60 | eventList.add(createEvent("voll geile Mucke!", dfmt.parse("2014-01-03"), dfmt.parse("2015-02-01"))); 61 | eventList.add(createEvent("Gedöns", dfmt.parse("2015-01-01"), dfmt.parse("2015-11-01"))); 62 | return eventList; 63 | } 64 | 65 | // ENTITY HELPERS 66 | 67 | protected Event createEvent(String eventDescription, Date startDate, Date endDate) { 68 | return eventRepository.save(new Event(eventDescription, startDate, endDate)); 69 | } 70 | 71 | // HELPER 72 | 73 | protected String createJSON(Object object) throws JsonProcessingException { 74 | ObjectMapper mapper = new ObjectMapper(); 75 | SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMM yyyy"); 76 | mapper.configure(SerializationFeature.INDENT_OUTPUT, true); 77 | mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); 78 | mapper.setDateFormat(outputFormat); 79 | mapper.setSerializationInclusion(Include.NON_EMPTY); 80 | return mapper.writeValueAsString(object); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/util/RestITBase.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.util; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.jayway.restassured.RestAssured; 5 | import com.jayway.restassured.specification.RequestSpecification; 6 | import ngSpring.demo.AngularSpringApplication; 7 | import ngSpring.demo.domain.entities.User; 8 | import ngSpring.demo.repositories.EventRepository; 9 | import ngSpring.demo.repositories.UserRepository; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.boot.test.IntegrationTest; 16 | import org.springframework.boot.test.SpringApplicationConfiguration; 17 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 18 | import org.springframework.test.context.ActiveProfiles; 19 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 20 | import org.springframework.test.context.web.WebAppConfiguration; 21 | 22 | import java.text.ParseException; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | import static com.jayway.restassured.RestAssured.given; 27 | 28 | @RunWith(SpringJUnit4ClassRunner.class) 29 | @ActiveProfiles("test") 30 | @SpringApplicationConfiguration(classes = AngularSpringApplication.class) 31 | @WebAppConfiguration 32 | @IntegrationTest("server.port:0") 33 | public abstract class RestITBase { 34 | 35 | @Autowired 36 | private EventRepository eventRepository; 37 | 38 | @Autowired 39 | private UserRepository userRepository; 40 | 41 | @Value("${local.server.port}") 42 | protected int port; 43 | 44 | @Before 45 | public void setUp() throws ParseException { 46 | RestAssured.port = port; 47 | userRepository.save(User.builder() 48 | .enabled(true) 49 | .username("user") 50 | .password(new BCryptPasswordEncoder().encode("password")) 51 | .build()); 52 | } 53 | 54 | @After 55 | public void clean() { 56 | try { 57 | this.userRepository.deleteAll(); 58 | this.eventRepository.deleteAll(); 59 | } catch (Exception ignored) { 60 | } 61 | } 62 | 63 | protected UserRepository getUserRepository() { 64 | return userRepository; 65 | } 66 | 67 | public EventRepository getEventRepository() { 68 | return eventRepository; 69 | } 70 | 71 | protected int getPort() { 72 | return port; 73 | } 74 | 75 | protected RequestSpecification login(String user, String password) { 76 | return given().auth().preemptive().basic(user, password).redirects() 77 | .follow(false); 78 | } 79 | 80 | protected String toJSON(Object entity) { 81 | try { 82 | ObjectMapper mapper = new ObjectMapper(); 83 | return mapper.writeValueAsString(entity); 84 | } catch (Exception e) { 85 | return ""; 86 | } 87 | } 88 | 89 | protected String toJSON(Map map) { 90 | try { 91 | ObjectMapper mapper = new ObjectMapper(); 92 | return mapper.writeValueAsString(map); 93 | } catch (Exception ignored) { 94 | return ""; 95 | } 96 | } 97 | 98 | // HELPERS 99 | protected RequestSpecification loginWithCorrectCredentials() { 100 | return login("user", "password"); 101 | } 102 | 103 | protected RequestSpecification loginWithIncorrectCredentials() { 104 | return login("user", "blub"); 105 | } 106 | 107 | protected RequestSpecification loginWithEmptyCredentials() { 108 | return given().auth().none().redirects().follow(false); 109 | } 110 | 111 | public class JSONBuilder { 112 | 113 | private Map properties = new HashMap(); 114 | 115 | public JSONBuilder add(String key, String value) { 116 | this.properties.put(key, value); 117 | return this; 118 | } 119 | 120 | public String build() { 121 | return toJSON(this.properties); 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/java/ngSpring/demo/util/ValidationTestBase.java: -------------------------------------------------------------------------------- 1 | package ngSpring.demo.util; 2 | 3 | import org.junit.BeforeClass; 4 | 5 | import javax.validation.ConstraintViolation; 6 | import javax.validation.Validation; 7 | import javax.validation.Validator; 8 | import javax.validation.ValidatorFactory; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | /** 14 | * Common base to run bean validation unit tests 15 | * 16 | * @author hypery2k 17 | */ 18 | public class ValidationTestBase { 19 | 20 | /** 21 | * the bean validator instance 22 | */ 23 | protected static Validator validator; 24 | 25 | /** 26 | * set up the correct validation 27 | */ 28 | @BeforeClass 29 | public static void setUp() { 30 | final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 31 | validator = factory.getValidator(); 32 | } 33 | 34 | // HELPER METHODS 35 | protected List getValidationMessages(Set> constraintViolations) { 36 | List messages = new ArrayList<>(); 37 | for (ConstraintViolation violation : constraintViolations) { 38 | messages.add(violation.getMessageTemplate()); 39 | } 40 | return messages; 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/resources/setup/test.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO event(event_id,event_description,start_date,end_date,insert_date,deleted) 2 | VALUES("1", "(Responsive) UI Testing mit Galen", "2015-08-18", NULL, "2015-07-01", false); 3 | 4 | INSERT INTO event(event_id,event_description,start_date,end_date,insert_date,deleted) 5 | VALUES("2", "Ein großes Event für Groß und Klein, damit auch jeder was davon hat!", "2015-08-01", "2015-08-21", "2015-07-01", false); 6 | 7 | INSERT INTO event(event_id,event_description,start_date,end_date,insert_date,deleted) 8 | VALUES("3", "Clean Code Session", "2015-08-03", "2015-08-05", "2015-07-01", false); 9 | 10 | INSERT INTO event(event_id,event_description,start_date,end_date,insert_date,deleted) 11 | VALUES("4", "Spieleabend", "2015-08-01", NULL, "2015-07-01", false); 12 | 13 | INSERT INTO user(user_id,user_name,password,insert_date,enabled,deleted) 14 | VALUES ("1", "user", "$2a$10$o2C6NPSNsq45fV.qArHXiep0OGb4YNCODGQNFpKWQ7TX7jZuiCKYq", "2015-07-01", true, false); -------------------------------------------------------------------------------- /angular-spring-boot-webapp/src/test/resources/testdata/login.csv: -------------------------------------------------------------------------------- 1 | username,password,fail 2 | user,password,false 3 | user2,password,true -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | die() { 4 | [[ $# -gt 1 ]] && { 5 | exit_status=$1 6 | shift 7 | } 8 | local -i frame=0; local info= 9 | while info=$(caller $frame) 10 | do 11 | local -a f=( $info ) 12 | [[ $frame -gt 0 ]] && { 13 | printf >&2 "ERROR in \"%s\" %s:%s\n" "${f[1]}" "${f[2]}" "${f[0]}" 14 | } 15 | (( frame++ )) || :; #ignore increment errors (i.e., errexit is set) 16 | done 17 | 18 | printf >&2 "ERROR: $*\n" 19 | 20 | exit ${exit_status:-1} 21 | } 22 | 23 | #trap 'die $? "*** bootstrap failed. ***"' ERR 24 | 25 | set -o nounset -o pipefail 26 | 27 | sudo apt-get update 28 | sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password root' 29 | sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password root' 30 | sudo apt-get install -y vim curl python-software-properties 31 | sudo apt-get update 32 | sudo apt-get -y install mysql-server 33 | sed -i "s/^bind-address/#bind-address/" /etc/mysql/my.cnf 34 | 35 | ######## 36 | # MySQL 37 | ######## 38 | 39 | #MYSQL CONFIG 40 | ##set mysql user 41 | mysql -u root -proot -e "GRANT ALL PRIVILEGES ON *.* TO 'ngspring'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION; FLUSH PRIVILEGES;" 42 | 43 | ##create mysql database 44 | mysql -u ngspring -ppassword -e "CREATE DATABASE NGSPRING" 45 | 46 | ##restart 47 | sudo /etc/init.d/mysql restart 48 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | ngspring-demo 6 | angular-spring-boot-sample 7 | 0.2.0-SNAPSHOT 8 | 9 | pom 10 | 11 | 12 | angular-spring-boot-webapp 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypery2k/angular-spring-boot-sample/02073c15169a5344722878efc781aed7dd75a327/sample.png --------------------------------------------------------------------------------