diff --git a/jhipster/.editorconfig b/jhipster/jhipster-microservice/car-app/.editorconfig
similarity index 100%
rename from jhipster/.editorconfig
rename to jhipster/jhipster-microservice/car-app/.editorconfig
diff --git a/jhipster/.gitattributes b/jhipster/jhipster-microservice/car-app/.gitattributes
similarity index 100%
rename from jhipster/.gitattributes
rename to jhipster/jhipster-microservice/car-app/.gitattributes
diff --git a/jhipster/jhipster-microservice/car-app/.gitignore b/jhipster/jhipster-microservice/car-app/.gitignore
new file mode 100644
index 0000000000..74b29e2042
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/.gitignore
@@ -0,0 +1,141 @@
+######################
+# Project Specific
+######################
+/target/www/**
+/src/test/javascript/coverage/
+/src/test/javascript/PhantomJS*/
+
+######################
+# Node
+######################
+/node/
+node_tmp/
+node_modules/
+npm-debug.log.*
+
+######################
+# SASS
+######################
+.sass-cache/
+
+######################
+# Eclipse
+######################
+*.pydevproject
+.project
+.metadata
+tmp/
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+.factorypath
+/src/main/resources/rebel.xml
+
+# External tool builders
+.externalToolBuilders/**
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+######################
+# Intellij
+######################
+.idea/
+*.iml
+*.iws
+*.ipr
+*.ids
+*.orig
+
+######################
+# Visual Studio Code
+######################
+.vscode/
+
+######################
+# Maven
+######################
+/log/
+/target/
+
+######################
+# Gradle
+######################
+.gradle/
+/build/
+
+######################
+# Package Files
+######################
+*.jar
+*.war
+*.ear
+*.db
+
+######################
+# Windows
+######################
+# Windows image file caches
+Thumbs.db
+
+# Folder config file
+Desktop.ini
+
+######################
+# Mac OSX
+######################
+.DS_Store
+.svn
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+######################
+# Directories
+######################
+/bin/
+/deploy/
+
+######################
+# Logs
+######################
+*.log
+
+######################
+# Others
+######################
+*.class
+*.*~
+*~
+.merge_file*
+
+######################
+# Gradle Wrapper
+######################
+!gradle/wrapper/gradle-wrapper.jar
+
+######################
+# Maven Wrapper
+######################
+!.mvn/wrapper/maven-wrapper.jar
+
+######################
+# ESLint
+######################
+.eslintcache
diff --git a/jhipster/jhipster-microservice/car-app/.jhipster/Car.json b/jhipster/jhipster-microservice/car-app/.jhipster/Car.json
new file mode 100644
index 0000000000..ceaef3cd63
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/.jhipster/Car.json
@@ -0,0 +1,25 @@
+{
+ "fluentMethods": true,
+ "relationships": [],
+ "fields": [
+ {
+ "fieldName": "make",
+ "fieldType": "String"
+ },
+ {
+ "fieldName": "brand",
+ "fieldType": "String"
+ },
+ {
+ "fieldName": "price",
+ "fieldType": "Double"
+ }
+ ],
+ "changelogDate": "20170503041524",
+ "dto": "no",
+ "service": "no",
+ "entityTableName": "car",
+ "pagination": "infinite-scroll",
+ "microserviceName": "carapp",
+ "searchEngine": false
+}
diff --git a/jhipster/.mvn/wrapper/maven-wrapper.jar b/jhipster/jhipster-microservice/car-app/.mvn/wrapper/maven-wrapper.jar
similarity index 100%
rename from jhipster/.mvn/wrapper/maven-wrapper.jar
rename to jhipster/jhipster-microservice/car-app/.mvn/wrapper/maven-wrapper.jar
diff --git a/jhipster/.mvn/wrapper/maven-wrapper.properties b/jhipster/jhipster-microservice/car-app/.mvn/wrapper/maven-wrapper.properties
similarity index 100%
rename from jhipster/.mvn/wrapper/maven-wrapper.properties
rename to jhipster/jhipster-microservice/car-app/.mvn/wrapper/maven-wrapper.properties
diff --git a/jhipster/jhipster-microservice/car-app/.yo-rc.json b/jhipster/jhipster-microservice/car-app/.yo-rc.json
new file mode 100644
index 0000000000..896ab47839
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/.yo-rc.json
@@ -0,0 +1,29 @@
+{
+ "generator-jhipster": {
+ "jhipsterVersion": "4.0.8",
+ "baseName": "carapp",
+ "packageName": "com.car.app",
+ "packageFolder": "com/car/app",
+ "serverPort": "8081",
+ "authenticationType": "jwt",
+ "hibernateCache": "hazelcast",
+ "clusteredHttpSession": false,
+ "websocket": false,
+ "databaseType": "sql",
+ "devDatabaseType": "h2Disk",
+ "prodDatabaseType": "mysql",
+ "searchEngine": false,
+ "messageBroker": false,
+ "serviceDiscoveryType": "eureka",
+ "buildTool": "maven",
+ "enableSocialSignIn": false,
+ "jwtSecretKey": "0ebd193e0552c4ac548217d353abbde0761a1997",
+ "enableTranslation": false,
+ "applicationType": "microservice",
+ "testFrameworks": [],
+ "jhiPrefix": "jhi",
+ "skipClient": true,
+ "skipUserManagement": true,
+ "clientPackageManager": "yarn"
+ }
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/car-app/README.md b/jhipster/jhipster-microservice/car-app/README.md
new file mode 100644
index 0000000000..7dcbb23bb1
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/README.md
@@ -0,0 +1,75 @@
+# carapp
+This application was generated using JHipster 4.0.8, you can find documentation and help at [https://jhipster.github.io/documentation-archive/v4.0.8](https://jhipster.github.io/documentation-archive/v4.0.8).
+
+This is a "microservice" application intended to be part of a microservice architecture, please refer to the [Doing microservices with JHipster][] page of the documentation for more information.
+
+This application is configured for Service Discovery and Configuration with the JHipster-Registry. On launch, it will refuse to start if it is not able to connect to the JHipster-Registry at [http://localhost:8761](http://localhost:8761). For more information, read our documentation on [Service Discovery and Configuration with the JHipster-Registry][].
+
+## Development
+
+To start your application in the dev profile, simply run:
+
+ ./mvnw
+
+
+For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][].
+
+
+## Building for production
+
+To optimize the carapp application for production, run:
+
+ ./mvnw -Pprod clean package
+
+To ensure everything worked, run:
+
+ java -jar target/*.war
+
+
+Refer to [Using JHipster in production][] for more details.
+
+## Testing
+
+To launch your application's tests, run:
+
+ ./mvnw clean test
+
+For more information, refer to the [Running tests page][].
+
+## Using Docker to simplify development (optional)
+
+You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services.
+For example, to start a mysql database in a docker container, run:
+
+ docker-compose -f src/main/docker/mysql.yml up -d
+
+To stop it and remove the container, run:
+
+ docker-compose -f src/main/docker/mysql.yml down
+
+You can also fully dockerize your application and all the services that it depends on.
+To achieve this, first build a docker image of your app by running:
+
+ ./mvnw package -Pprod docker:build
+
+Then run:
+
+ docker-compose -f src/main/docker/app.yml up -d
+
+For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`yo jhipster:docker-compose`), which is able to generate docker configurations for one or several JHipster applications.
+
+## Continuous Integration (optional)
+
+To configure CI for your project, run the ci-cd sub-generator (`yo jhipster:ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information.
+
+[JHipster Homepage and latest documentation]: https://jhipster.github.io
+[JHipster 4.0.8 archive]: https://jhipster.github.io/documentation-archive/v4.0.8
+[Doing microservices with JHipster]: https://jhipster.github.io/documentation-archive/v4.0.8/microservices-architecture/
+[Using JHipster in development]: https://jhipster.github.io/documentation-archive/v4.0.8/development/
+[Service Discovery and Configuration with the JHipster-Registry]: https://jhipster.github.io/documentation-archive/v4.0.8/microservices-architecture/#jhipster-registry
+[Using Docker and Docker-Compose]: https://jhipster.github.io/documentation-archive/v4.0.8/docker-compose
+[Using JHipster in production]: https://jhipster.github.io/documentation-archive/v4.0.8/production/
+[Running tests page]: https://jhipster.github.io/documentation-archive/v4.0.8/running-tests/
+[Setting up Continuous Integration]: https://jhipster.github.io/documentation-archive/v4.0.8/setting-up-ci/
+
+
diff --git a/jhipster/mvnw b/jhipster/jhipster-microservice/car-app/mvnw
similarity index 100%
rename from jhipster/mvnw
rename to jhipster/jhipster-microservice/car-app/mvnw
diff --git a/jhipster/mvnw.cmd b/jhipster/jhipster-microservice/car-app/mvnw.cmd
similarity index 100%
rename from jhipster/mvnw.cmd
rename to jhipster/jhipster-microservice/car-app/mvnw.cmd
diff --git a/jhipster/jhipster-microservice/car-app/package.json b/jhipster/jhipster-microservice/car-app/package.json
new file mode 100644
index 0000000000..ac56dd89a7
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/package.json
@@ -0,0 +1,5 @@
+{
+ "devDependencies": {
+ "generator-jhipster": "4.0.8"
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/pom.xml b/jhipster/jhipster-microservice/car-app/pom.xml
new file mode 100644
index 0000000000..bf26818f66
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/pom.xml
@@ -0,0 +1,928 @@
+
+
+ 4.0.0
+
+
+ spring-boot-starter-parent
+ org.springframework.boot
+ 1.5.2.RELEASE
+
+
+
+ com.car.app
+ carapp
+ 0.0.1-SNAPSHOT
+ war
+ Carapp
+
+
+ ${maven.version}
+
+
+
+ -Djava.security.egd=file:/dev/./urandom -Xmx256m
+ 3.6.2
+ 2.0.0
+ 2.5
+ 3.5
+ 0.4.13
+ 1.2
+ 5.2.8.Final
+ 2.6.0
+ 0.7.9
+ 1.8
+ 3.21.0-GA
+ 1.0.0
+ 1.1.0
+ 0.7.0
+ 3.6
+ 2.0.0
+ 4.8
+ jdt_apt
+ 1.1.0.Final
+ 3.6.0
+ 1.4.1
+ 3.0.1
+ yyyyMMddHHmmss
+ ${java.version}
+ ${java.version}
+ 3.0.0
+ 3.1.3
+ v6.10.0
+
+
+
+
+ ${project.build.directory}/test-results
+ 0.0.20
+ false
+ 3.2.2
+ 2.12.1
+ 3.2
+
+ src/main/webapp/content/**/*.*, src/main/webapp/bower_components/**/*.*, src/main/webapp/i18n/*.js, target/www/**/*.*
+
+ S3437,UndocumentedApi,BoldAndItalicTagsCheck
+
+
+ src/main/webapp/app/**/*.*
+ Web:BoldAndItalicTagsCheck
+
+ src/main/java/**/*
+ squid:S3437
+
+ src/main/java/**/*
+ squid:UndocumentedApi
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco-it.exec
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+ jacoco
+
+ ${project.testresult.directory}/karma
+
+ ${project.testresult.directory}/coverage/report-lcov/lcov.info
+
+ ${project.testresult.directory}/coverage/report-lcov/lcov.info
+
+ ${project.basedir}/src/main/
+ ${project.testresult.directory}/surefire-reports
+ ${project.basedir}/src/test/
+
+ 2.5.0
+
+ Camden.SR5
+ 2.6.1
+ 1.4.10.Final
+ 1.1.0.Final
+ v0.21.3
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-hibernate5
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-hppc
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-json-org
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ com.h2database
+ h2
+
+
+ com.hazelcast
+ hazelcast
+
+
+ com.hazelcast
+ hazelcast-hibernate52
+ ${hazelcast-hibernate52.version}
+
+
+ com.hazelcast
+ hazelcast-spring
+
+
+ com.jayway.jsonpath
+ json-path
+ test
+
+
+
+ com.mattbertolini
+ liquibase-slf4j
+ ${liquibase-slf4j.version}
+
+
+ com.ryantenney.metrics
+ metrics-spring
+ ${metrics-spring.version}
+
+
+ metrics-annotation
+ com.codahale.metrics
+
+
+ metrics-core
+ com.codahale.metrics
+
+
+ metrics-healthchecks
+ com.codahale.metrics
+
+
+
+
+ com.zaxxer
+ HikariCP
+
+
+ tools
+ com.sun
+
+
+
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+ io.dropwizard.metrics
+ metrics-annotation
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-core
+
+
+ io.dropwizard.metrics
+ metrics-json
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-jvm
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-servlet
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-servlets
+
+
+ io.github.jhipster
+ jhipster
+ ${jhipster.server.version}
+
+
+ io.jsonwebtoken
+ jjwt
+ ${jjwt.version}
+
+
+ io.springfox
+ springfox-bean-validators
+ ${springfox.version}
+
+
+ io.springfox
+ springfox-swagger2
+ ${springfox.version}
+
+
+ mapstruct
+ org.mapstruct
+
+
+
+
+ javax.cache
+ cache-api
+
+
+ mysql
+ mysql-connector-java
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ ${logstash-logback-encoder.version}
+
+
+ logback-core
+ ch.qos.logback
+
+
+ logback-classic
+ ch.qos.logback
+
+
+ logback-access
+ ch.qos.logback
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang.version}
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.awaitility
+ awaitility
+ ${awaitility.version}
+ test
+
+
+ org.hibernate
+ hibernate-envers
+
+
+ org.hibernate
+ hibernate-validator
+
+
+ org.liquibase
+ liquibase-core
+
+
+ jetty-servlet
+ org.eclipse.jetty
+
+
+
+
+ org.mapstruct
+ mapstruct-jdk8
+ ${mapstruct.version}
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework.boot
+ spring-boot-actuator
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-loader-tools
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.boot
+ spring-boot-starter-cloud-connectors
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ spring-boot-starter-tomcat
+ org.springframework.boot
+
+
+
+
+ org.springframework.boot
+ spring-boot-test
+ test
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+ org.springframework.cloud
+ spring-cloud-starter-feign
+
+
+ org.springframework.cloud
+ spring-cloud-starter-hystrix
+
+
+ org.springframework.cloud
+ spring-cloud-starter-ribbon
+
+
+
+ netty-transport-native-epoll
+ io.netty
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-spectator
+
+
+ org.springframework.retry
+ spring-retry
+
+
+
+ org.springframework.security
+ spring-security-data
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+
+ spring-boot:run
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-maven-plugin.version}
+
+ prepare-agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ com.github.ekryd.sortpom
+ sortpom-maven-plugin
+ ${sortpom-maven-plugin.version}
+
+
+ verify
+
+ sort
+
+
+
+
+ true
+ 4
+ groupId,artifactId
+ groupId,artifactId
+ true
+ false
+
+
+
+ com.spotify
+ docker-maven-plugin
+ ${docker-maven-plugin.version}
+
+ carapp
+ src/main/docker
+
+
+ /
+ ${project.build.directory}
+ ${project.build.finalName}.war
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-eclipse-plugin
+
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ ${maven-enforcer-plugin.version}
+
+
+ enforce-versions
+
+ enforce
+
+
+
+
+
+
+ You are running an older version of Maven. JHipster requires at least Maven ${maven.version}
+ [${maven.version},)
+
+
+ You are running an older version of Java. JHipster requires at least JDK ${java.version}
+ [${java.version}.0,)
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven-resources-plugin.version}
+
+
+ default-resources
+ validate
+
+ copy-resources
+
+
+ target/classes
+ false
+
+ #
+
+
+
+ src/main/resources/
+ true
+
+ **/*.xml
+ **/*.yml
+
+
+
+ src/main/resources/
+ false
+
+ **/*.xml
+ **/*.yml
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ alphabetical
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-maven-plugin.version}
+
+
+ pre-unit-tests
+
+ prepare-agent
+
+
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+
+
+
+
+ post-unit-test
+ test
+
+ report
+
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+ ${project.testresult.directory}/coverage/jacoco
+
+
+
+
+
+ org.liquibase
+ liquibase-maven-plugin
+ ${liquibase.version}
+
+
+ javax.validation
+ validation-api
+ ${validation-api.version}
+
+
+ org.javassist
+ javassist
+ ${javassist.version}
+
+
+ org.liquibase.ext
+ liquibase-hibernate5
+ ${liquibase-hibernate5.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ ${project.parent.version}
+
+
+
+ src/main/resources/config/liquibase/master.xml
+ src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml
+ org.h2.Driver
+ jdbc:h2:file:./target/h2db/db/carapp
+
+ carapp
+
+ hibernate:spring:com.car.app.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ true
+ debug
+
+
+
+ org.sonarsource.scanner.maven
+ sonar-maven-plugin
+ ${sonar-maven-plugin.version}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ true
+
+
+
+
+
+
+
+
+ no-liquibase
+
+ ,no-liquibase
+
+
+
+ swagger
+
+ ,swagger
+
+
+
+ dev
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+
+
+
+
+ DEBUG
+
+ dev${profile.no-liquibase}
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+
+
+ prod
+
+
+
+ maven-clean-plugin
+
+
+
+ target/www/
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ build-info
+
+
+
+
+ true
+
+
+
+
+
+
+ INFO
+
+ prod${profile.swagger}${profile.no-liquibase}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+
+
+
+ cc
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+ ${scala-maven-plugin.version}
+
+
+ compile
+ compile
+
+ add-source
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ add-source
+ testCompile
+
+
+
+
+ incremental
+ true
+ ${scala.version}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+ none
+
+
+ default-testCompile
+ none
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+ src/main/webapp/
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ true
+ true
+
+
+
+
+
+
+
+ DEBUG
+
+ dev,swagger
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+
+
+
+ graphite
+
+
+ io.dropwizard.metrics
+ metrics-graphite
+
+
+
+
+
+ prometheus
+
+
+ io.prometheus
+ simpleclient
+ ${prometheus-simpleclient.version}
+
+
+ io.prometheus
+ simpleclient_dropwizard
+ ${prometheus-simpleclient.version}
+
+
+ io.prometheus
+ simpleclient_servlet
+ ${prometheus-simpleclient.version}
+
+
+
+
+
+ IDE
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/Dockerfile b/jhipster/jhipster-microservice/car-app/src/main/docker/Dockerfile
new file mode 100644
index 0000000000..037e197be6
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/Dockerfile
@@ -0,0 +1,13 @@
+FROM openjdk:8-jre-alpine
+
+ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
+ JHIPSTER_SLEEP=0
+
+# add directly the war
+ADD *.war /app.war
+
+VOLUME /tmp
+EXPOSE 8081 5701/udp
+CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \
+ sleep ${JHIPSTER_SLEEP} && \
+ java -Djava.security.egd=file:/dev/./urandom -jar /app.war
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/app.yml b/jhipster/jhipster-microservice/car-app/src/main/docker/app.yml
new file mode 100644
index 0000000000..0904a7335d
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/app.yml
@@ -0,0 +1,19 @@
+version: '2'
+services:
+ carapp-app:
+ image: carapp
+ environment:
+ - SPRING_PROFILES_ACTIVE=prod,swagger
+ - SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/config
+ - SPRING_DATASOURCE_URL=jdbc:mysql://carapp-mysql:3306/carapp?useUnicode=true&characterEncoding=utf8&useSSL=false
+ - JHIPSTER_SLEEP=10 # gives time for the database to boot before the application
+ carapp-mysql:
+ extends:
+ file: mysql.yml
+ service: carapp-mysql
+ jhipster-registry:
+ extends:
+ file: jhipster-registry.yml
+ service: jhipster-registry
+ environment:
+ - SPRING_CLOUD_CONFIG_SERVER_NATIVE_SEARCH_LOCATIONS=file:./central-config/docker-config/
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/README.md b/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/README.md
new file mode 100644
index 0000000000..022a152863
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/README.md
@@ -0,0 +1,7 @@
+# Central configuration sources details
+
+The JHipster-Registry will use the following directories as its configuration source :
+- localhost-config : when running the registry in docker with the jhipster-registry.yml docker-compose file
+- docker-config : when running the registry and the app both in docker with the app.yml docker-compose file
+
+For more info, refer to http://jhipster.github.io/microservices-architecture/#registry_app_configuration
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/docker-config/application.yml b/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/docker-config/application.yml
new file mode 100644
index 0000000000..f11d367241
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/docker-config/application.yml
@@ -0,0 +1,15 @@
+# Common configuration shared between all applications
+configserver:
+ name: Docker JHipster Registry
+ status: Connected to the JHipster Registry running in Docker
+
+jhipster:
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@jhipster-registry:8761/eureka/
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/localhost-config/application.yml b/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/localhost-config/application.yml
new file mode 100644
index 0000000000..052a6d0535
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/central-server-config/localhost-config/application.yml
@@ -0,0 +1,15 @@
+# Common configuration shared between all applications
+configserver:
+ name: Docker JHipster Registry
+ status: Connected to the JHipster Registry running in Docker
+
+jhipster:
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/jhipster-registry.yml b/jhipster/jhipster-microservice/car-app/src/main/docker/jhipster-registry.yml
new file mode 100644
index 0000000000..58feb685d4
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/jhipster-registry.yml
@@ -0,0 +1,18 @@
+version: '2'
+services:
+ jhipster-registry:
+ image: jhipster/jhipster-registry:v2.5.8
+ volumes:
+ - ./central-server-config:/central-config
+ # When run with the "dev" Spring profile, the JHipster Registry will
+ # read the config from the local filesystem (central-server-config directory)
+ # When run with the "prod" Spring profile, it will read the config from a git repository
+ # See http://jhipster.github.io/microservices-architecture/#registry_app_configuration
+ environment:
+ - SPRING_PROFILES_ACTIVE=dev
+ - SECURITY_USER_PASSWORD=admin
+ - SPRING_CLOUD_CONFIG_SERVER_NATIVE_SEARCH_LOCATIONS=file:./central-config/localhost-config/
+ # - GIT_URI=https://github.com/jhipster/jhipster-registry/
+ # - GIT_SEARCH_PATHS=central-config
+ ports:
+ - 8761:8761
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/mysql.yml b/jhipster/jhipster-microservice/car-app/src/main/docker/mysql.yml
new file mode 100644
index 0000000000..151facca36
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/mysql.yml
@@ -0,0 +1,13 @@
+version: '2'
+services:
+ carapp-mysql:
+ image: mysql:5.7.13
+ # volumes:
+ # - ~/volumes/jhipster/carapp/mysql/:/var/lib/mysql/
+ environment:
+ - MYSQL_USER=root
+ - MYSQL_ALLOW_EMPTY_PASSWORD=yes
+ - MYSQL_DATABASE=carapp
+ ports:
+ - 3306:3306
+ command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8
diff --git a/jhipster/jhipster-microservice/car-app/src/main/docker/sonar.yml b/jhipster/jhipster-microservice/car-app/src/main/docker/sonar.yml
new file mode 100644
index 0000000000..353e034f40
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/docker/sonar.yml
@@ -0,0 +1,7 @@
+version: '2'
+services:
+ carapp-sonar:
+ image: sonarqube:6.2-alpine
+ ports:
+ - 9000:9000
+ - 9092:9092
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/ApplicationWebXml.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/ApplicationWebXml.java
new file mode 100644
index 0000000000..edd9098f8a
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/ApplicationWebXml.java
@@ -0,0 +1,21 @@
+package com.car.app;
+
+import com.car.app.config.DefaultProfileUtil;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.support.SpringBootServletInitializer;
+
+/**
+ * This is a helper Java class that provides an alternative to creating a web.xml.
+ * This will be invoked only when the application is deployed to a servlet container like Tomcat, JBoss etc.
+ */
+public class ApplicationWebXml extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ /**
+ * set a default to use when no profile is configured.
+ */
+ DefaultProfileUtil.addDefaultProfile(application.application());
+ return application.sources(CarappApp.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/CarappApp.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/CarappApp.java
new file mode 100644
index 0000000000..b85a50b156
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/CarappApp.java
@@ -0,0 +1,91 @@
+package com.car.app;
+
+import com.car.app.config.ApplicationProperties;
+import com.car.app.config.DefaultProfileUtil;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.actuate.autoconfigure.*;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.env.Environment;
+
+import javax.annotation.PostConstruct;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collection;
+
+@ComponentScan
+@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
+@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
+@EnableDiscoveryClient
+public class CarappApp {
+
+ private static final Logger log = LoggerFactory.getLogger(CarappApp.class);
+
+ private final Environment env;
+
+ public CarappApp(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Initializes carapp.
+ *
+ * Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
+ *
+ * You can find more information on how profiles work with JHipster on http://jhipster.github.io/profiles/.
+ */
+ @PostConstruct
+ public void initApplication() {
+ Collection activeProfiles = Arrays.asList(env.getActiveProfiles());
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
+ log.error("You have misconfigured your application! It should not run " +
+ "with both the 'dev' and 'prod' profiles at the same time.");
+ }
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
+ log.error("You have misconfigured your application! It should not" +
+ "run with both the 'dev' and 'cloud' profiles at the same time.");
+ }
+ }
+
+ /**
+ * Main method, used to run the application.
+ *
+ * @param args the command line arguments
+ * @throws UnknownHostException if the local host name could not be resolved into an address
+ */
+ public static void main(String[] args) throws UnknownHostException {
+ SpringApplication app = new SpringApplication(CarappApp.class);
+ DefaultProfileUtil.addDefaultProfile(app);
+ Environment env = app.run(args).getEnvironment();
+ String protocol = "http";
+ if (env.getProperty("server.ssl.key-store") != null) {
+ protocol = "https";
+ }
+ log.info("\n----------------------------------------------------------\n\t" +
+ "Application '{}' is running! Access URLs:\n\t" +
+ "Local: \t\t{}://localhost:{}\n\t" +
+ "External: \t{}://{}:{}\n\t" +
+ "Profile(s): \t{}\n----------------------------------------------------------",
+ env.getProperty("spring.application.name"),
+ protocol,
+ env.getProperty("server.port"),
+ protocol,
+ InetAddress.getLocalHost().getHostAddress(),
+ env.getProperty("server.port"),
+ env.getActiveProfiles());
+
+ String configServerStatus = env.getProperty("configserver.status");
+ log.info("\n----------------------------------------------------------\n\t" +
+ "Config Server: \t{}\n----------------------------------------------------------",
+ configServerStatus == null ? "Not found or not setup for this application" : configServerStatus);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/aop/logging/LoggingAspect.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/aop/logging/LoggingAspect.java
new file mode 100644
index 0000000000..7171abbcd2
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/aop/logging/LoggingAspect.java
@@ -0,0 +1,79 @@
+package com.car.app.aop.logging;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+
+import java.util.Arrays;
+
+/**
+ * Aspect for logging execution of service and repository Spring components.
+ *
+ * By default, it only runs with the "dev" profile.
+ */
+@Aspect
+public class LoggingAspect {
+
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ private final Environment env;
+
+ public LoggingAspect(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Pointcut that matches all repositories, services and Web REST endpoints.
+ */
+ @Pointcut("within(com.car.app.repository..*) || within(com.car.app.service..*) || within(com.car.app.web.rest..*)")
+ public void loggingPointcut() {
+ // Method is empty as this is just a Pointcut, the implementations are in the advices.
+ }
+
+ /**
+ * Advice that logs methods throwing exceptions.
+ */
+ @AfterThrowing(pointcut = "loggingPointcut()", throwing = "e")
+ public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e);
+
+ } else {
+ log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL");
+ }
+ }
+
+ /**
+ * Advice that logs when a method is entered and exited.
+ */
+ @Around("loggingPointcut()")
+ public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ if (log.isDebugEnabled()) {
+ log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
+ }
+ try {
+ Object result = joinPoint.proceed();
+ if (log.isDebugEnabled()) {
+ log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), result);
+ }
+ return result;
+ } catch (IllegalArgumentException e) {
+ log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
+ joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
+
+ throw e;
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/ApplicationProperties.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/ApplicationProperties.java
new file mode 100644
index 0000000000..1f69dd1529
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/ApplicationProperties.java
@@ -0,0 +1,15 @@
+package com.car.app.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Properties specific to JHipster.
+ *
+ *
+ * Properties are configured in the application.yml file.
+ *
+ */
+@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
+public class ApplicationProperties {
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/AsyncConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/AsyncConfiguration.java
new file mode 100644
index 0000000000..67cb2ad402
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/AsyncConfiguration.java
@@ -0,0 +1,46 @@
+package com.car.app.config;
+
+import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
+import io.github.jhipster.config.JHipsterProperties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.*;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+@Configuration
+@EnableAsync
+@EnableScheduling
+public class AsyncConfiguration implements AsyncConfigurer {
+
+ private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public AsyncConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Override
+ @Bean(name = "taskExecutor")
+ public Executor getAsyncExecutor() {
+ log.debug("Creating Async Task Executor");
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
+ executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
+ executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
+ executor.setThreadNamePrefix("carapp-Executor-");
+ return new ExceptionHandlingAsyncTaskExecutor(executor);
+ }
+
+ @Override
+ public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+ return new SimpleAsyncUncaughtExceptionHandler();
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/CacheConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/CacheConfiguration.java
new file mode 100644
index 0000000000..0fdfff40f8
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/CacheConfiguration.java
@@ -0,0 +1,134 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.Hazelcast;
+import com.hazelcast.config.MapConfig;
+import com.hazelcast.config.EvictionPolicy;
+import com.hazelcast.config.MaxSizeConfig;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.context.annotation.*;
+import org.springframework.core.env.Environment;
+
+import javax.annotation.PreDestroy;
+
+@Configuration
+@EnableCaching
+@AutoConfigureAfter(value = { MetricsConfiguration.class })
+@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class })
+public class CacheConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);
+
+ private final Environment env;
+
+ private final DiscoveryClient discoveryClient;
+
+ private final ServerProperties serverProperties;
+
+ public CacheConfiguration(Environment env, DiscoveryClient discoveryClient, ServerProperties serverProperties) {
+ this.env = env;
+ this.discoveryClient = discoveryClient;
+ this.serverProperties = serverProperties;
+ }
+
+ @PreDestroy
+ public void destroy() {
+ log.info("Closing Cache Manager");
+ Hazelcast.shutdownAll();
+ }
+
+ @Bean
+ public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
+ log.debug("Starting HazelcastCacheManager");
+ CacheManager cacheManager = new com.hazelcast.spring.cache.HazelcastCacheManager(hazelcastInstance);
+ return cacheManager;
+ }
+
+ @Bean
+ public HazelcastInstance hazelcastInstance(JHipsterProperties jHipsterProperties) {
+ log.debug("Configuring Hazelcast");
+ Config config = new Config();
+ config.setInstanceName("carapp");
+ // The serviceId is by default the application's name, see Spring Boot's eureka.instance.appname property
+ String serviceId = discoveryClient.getLocalServiceInstance().getServiceId();
+ log.debug("Configuring Hazelcast clustering for instanceId: {}", serviceId);
+
+ // In development, everything goes through 127.0.0.1, with a different port
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ log.debug("Application is running with the \"dev\" profile, Hazelcast " +
+ "cluster will only work with localhost instances");
+
+ System.setProperty("hazelcast.local.localAddress", "127.0.0.1");
+ config.getNetworkConfig().setPort(serverProperties.getPort() + 5701);
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
+ for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
+ String clusterMember = "127.0.0.1:" + (instance.getPort() + 5701);
+ log.debug("Adding Hazelcast (dev) cluster member " + clusterMember);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
+ }
+ } else { // Production configuration, one host per instance all using port 5701
+ config.getNetworkConfig().setPort(5701);
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
+ for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
+ String clusterMember = instance.getHost() + ":5701";
+ log.debug("Adding Hazelcast (prod) cluster member " + clusterMember);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
+ }
+ }
+ config.getMapConfigs().put("default", initializeDefaultMapConfig());
+ config.getMapConfigs().put("com.car.app.domain.*", initializeDomainMapConfig(jHipsterProperties));
+ return Hazelcast.newHazelcastInstance(config);
+ }
+
+ private MapConfig initializeDefaultMapConfig() {
+ MapConfig mapConfig = new MapConfig();
+
+ /*
+ Number of backups. If 1 is set as the backup-count for example,
+ then all entries of the map will be copied to another JVM for
+ fail-safety. Valid numbers are 0 (no backup), 1, 2, 3.
+ */
+ mapConfig.setBackupCount(0);
+
+ /*
+ Valid values are:
+ NONE (no eviction),
+ LRU (Least Recently Used),
+ LFU (Least Frequently Used).
+ NONE is the default.
+ */
+ mapConfig.setEvictionPolicy(EvictionPolicy.LRU);
+
+ /*
+ Maximum size of the map. When max size is reached,
+ map is evicted based on the policy defined.
+ Any integer between 0 and Integer.MAX_VALUE. 0 means
+ Integer.MAX_VALUE. Default is 0.
+ */
+ mapConfig.setMaxSizeConfig(new MaxSizeConfig(0, MaxSizeConfig.MaxSizePolicy.USED_HEAP_SIZE));
+
+ return mapConfig;
+ }
+
+ private MapConfig initializeDomainMapConfig(JHipsterProperties jHipsterProperties) {
+ MapConfig mapConfig = new MapConfig();
+ mapConfig.setTimeToLiveSeconds(jHipsterProperties.getCache().getHazelcast().getTimeToLiveSeconds());
+ return mapConfig;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/CloudDatabaseConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/CloudDatabaseConfiguration.java
new file mode 100644
index 0000000000..2ca29491c9
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/CloudDatabaseConfiguration.java
@@ -0,0 +1,24 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.CacheManager;
+import org.springframework.cloud.config.java.AbstractCloudConfig;
+import org.springframework.context.annotation.*;
+
+import javax.sql.DataSource;
+
+@Configuration
+@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
+public class CloudDatabaseConfiguration extends AbstractCloudConfig {
+
+ private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
+
+ @Bean
+ public DataSource dataSource(CacheManager cacheManager) {
+ log.info("Configuring JDBC datasource from a cloud provider");
+ return connectionFactory().dataSource();
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/Constants.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/Constants.java
new file mode 100644
index 0000000000..260a3bbce8
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/Constants.java
@@ -0,0 +1,16 @@
+package com.car.app.config;
+
+/**
+ * Application constants.
+ */
+public final class Constants {
+
+ //Regex for acceptable logins
+ public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
+
+ public static final String SYSTEM_ACCOUNT = "system";
+ public static final String ANONYMOUS_USER = "anonymoususer";
+
+ private Constants() {
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DatabaseConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DatabaseConfiguration.java
new file mode 100644
index 0000000000..35a8ee3f48
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DatabaseConfiguration.java
@@ -0,0 +1,75 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;
+
+import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
+import liquibase.integration.spring.SpringLiquibase;
+import org.h2.tools.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+
+@Configuration
+@EnableJpaRepositories("com.car.app.repository")
+@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
+@EnableTransactionManagement
+public class DatabaseConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
+
+ private final Environment env;
+
+ public DatabaseConfiguration(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Open the TCP port for the H2 database, so it is available remotely.
+ *
+ * @return the H2 database TCP server
+ * @throws SQLException if the server failed to start
+ */
+ @Bean(initMethod = "start", destroyMethod = "stop")
+ @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
+ public Server h2TCPServer() throws SQLException {
+ return Server.createTcpServer("-tcp","-tcpAllowOthers");
+ }
+
+ @Bean
+ public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor,
+ DataSource dataSource, LiquibaseProperties liquibaseProperties) {
+
+ // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
+ SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env);
+ liquibase.setDataSource(dataSource);
+ liquibase.setChangeLog("classpath:config/liquibase/master.xml");
+ liquibase.setContexts(liquibaseProperties.getContexts());
+ liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
+ liquibase.setDropFirst(liquibaseProperties.isDropFirst());
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) {
+ liquibase.setShouldRun(false);
+ } else {
+ liquibase.setShouldRun(liquibaseProperties.isEnabled());
+ log.debug("Configuring Liquibase");
+ }
+ return liquibase;
+ }
+
+ @Bean
+ public Hibernate5Module hibernate5Module() {
+ return new Hibernate5Module();
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DateTimeFormatConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DateTimeFormatConfiguration.java
new file mode 100644
index 0000000000..67fff05c84
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DateTimeFormatConfiguration.java
@@ -0,0 +1,17 @@
+package com.car.app.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter {
+
+ @Override
+ public void addFormatters(FormatterRegistry registry) {
+ DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
+ registrar.setUseIsoFormat(true);
+ registrar.registerFormatters(registry);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DefaultProfileUtil.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DefaultProfileUtil.java
new file mode 100644
index 0000000000..640458f9db
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/DefaultProfileUtil.java
@@ -0,0 +1,48 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.core.env.Environment;
+
+import java.util.*;
+
+/**
+ * Utility class to load a Spring profile to be used as default
+ * when there is no spring.profiles.active set in the environment or as command line argument.
+ * If the value is not available in application.yml then dev profile will be used as default.
+ */
+public final class DefaultProfileUtil {
+
+ private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default";
+
+ private DefaultProfileUtil() {
+ }
+
+ /**
+ * Set a default to use when no profile is configured.
+ *
+ * @param app the Spring application
+ */
+ public static void addDefaultProfile(SpringApplication app) {
+ Map defProperties = new HashMap<>();
+ /*
+ * The default profile to use when no other profiles are defined
+ * This cannot be set in the application.yml file.
+ * See https://github.com/spring-projects/spring-boot/issues/1219
+ */
+ defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT);
+ app.setDefaultProperties(defProperties);
+ }
+
+ /**
+ * Get the profiles that are applied else get default profiles.
+ */
+ public static String[] getActiveProfiles(Environment env) {
+ String[] profiles = env.getActiveProfiles();
+ if (profiles.length == 0) {
+ return env.getDefaultProfiles();
+ }
+ return profiles;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LocaleConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LocaleConfiguration.java
new file mode 100644
index 0000000000..92adfb7f66
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LocaleConfiguration.java
@@ -0,0 +1,35 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.locale.AngularCookieLocaleResolver;
+
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+
+@Configuration
+public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware {
+
+ @Override
+ public void setEnvironment(Environment environment) {
+ // unused
+ }
+
+ @Bean(name = "localeResolver")
+ public LocaleResolver localeResolver() {
+ AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver();
+ cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY");
+ return cookieLocaleResolver;
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
+ localeChangeInterceptor.setParamName("language");
+ registry.addInterceptor(localeChangeInterceptor);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LoggingAspectConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LoggingAspectConfiguration.java
new file mode 100644
index 0000000000..e7370c954f
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LoggingAspectConfiguration.java
@@ -0,0 +1,19 @@
+package com.car.app.config;
+
+import com.car.app.aop.logging.LoggingAspect;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.springframework.context.annotation.*;
+import org.springframework.core.env.Environment;
+
+@Configuration
+@EnableAspectJAutoProxy
+public class LoggingAspectConfiguration {
+
+ @Bean
+ @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
+ public LoggingAspect loggingAspect(Environment env) {
+ return new LoggingAspect(env);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LoggingConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LoggingConfiguration.java
new file mode 100644
index 0000000000..318323ae7c
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/LoggingConfiguration.java
@@ -0,0 +1,113 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import ch.qos.logback.classic.AsyncAppender;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.LoggerContextListener;
+import ch.qos.logback.core.spi.ContextAwareBase;
+import net.logstash.logback.appender.LogstashSocketAppender;
+import net.logstash.logback.stacktrace.ShortenedThrowableConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class LoggingConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
+
+ private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+
+ @Value("${spring.application.name}")
+ private String appName;
+
+ @Value("${server.port}")
+ private String serverPort;
+
+ @Value("${eureka.instance.instanceId}")
+ private String instanceId;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public LoggingConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
+ addLogstashAppender(context);
+
+ // Add context listener
+ LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener();
+ loggerContextListener.setContext(context);
+ context.addListener(loggerContextListener);
+ }
+ }
+
+ public void addLogstashAppender(LoggerContext context) {
+ log.info("Initializing Logstash logging");
+
+ LogstashSocketAppender logstashAppender = new LogstashSocketAppender();
+ logstashAppender.setName("LOGSTASH");
+ logstashAppender.setContext(context);
+ String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"," +
+ "\"instance_id\":\"" + instanceId + "\"}";
+
+ // Set the Logstash appender config from JHipster properties
+ logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost());
+ logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort());
+ logstashAppender.setCustomFields(customFields);
+
+ // Limit the maximum length of the forwarded stacktrace so that it won't exceed the 8KB UDP limit of logstash
+ ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter();
+ throwableConverter.setMaxLength(7500);
+ throwableConverter.setRootCauseFirst(true);
+ logstashAppender.setThrowableConverter(throwableConverter);
+
+ logstashAppender.start();
+
+ // Wrap the appender in an Async appender for performance
+ AsyncAppender asyncLogstashAppender = new AsyncAppender();
+ asyncLogstashAppender.setContext(context);
+ asyncLogstashAppender.setName("ASYNC_LOGSTASH");
+ asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
+ asyncLogstashAppender.addAppender(logstashAppender);
+ asyncLogstashAppender.start();
+
+ context.getLogger("ROOT").addAppender(asyncLogstashAppender);
+ }
+
+ /**
+ * Logback configuration is achieved by configuration file and API.
+ * When configuration file change is detected, the configuration is reset.
+ * This listener ensures that the programmatic configuration is also re-applied after reset.
+ */
+ class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener {
+
+ @Override
+ public boolean isResetResistant() {
+ return true;
+ }
+
+ @Override
+ public void onStart(LoggerContext context) {
+ addLogstashAppender(context);
+ }
+
+ @Override
+ public void onReset(LoggerContext context) {
+ addLogstashAppender(context);
+ }
+
+ @Override
+ public void onStop(LoggerContext context) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) {
+ // Nothing to do.
+ }
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/MetricsConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/MetricsConfiguration.java
new file mode 100644
index 0000000000..27b617a190
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/MetricsConfiguration.java
@@ -0,0 +1,113 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.JHipsterProperties;
+import io.github.jhipster.config.metrics.SpectatorLogMetricWriter;
+
+import com.netflix.spectator.api.Registry;
+import org.springframework.boot.actuate.autoconfigure.ExportMetricReader;
+import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter;
+import org.springframework.boot.actuate.metrics.writer.MetricWriter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.netflix.metrics.spectator.SpectatorMetricReader;
+
+import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Slf4jReporter;
+import com.codahale.metrics.health.HealthCheckRegistry;
+import com.codahale.metrics.jvm.*;
+import com.ryantenney.metrics.spring.config.annotation.EnableMetrics;
+import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter;
+import com.zaxxer.hikari.HikariDataSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.*;
+
+import javax.annotation.PostConstruct;
+import java.lang.management.ManagementFactory;
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+@EnableMetrics(proxyTargetClass = true)
+public class MetricsConfiguration extends MetricsConfigurerAdapter {
+
+ private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory";
+ private static final String PROP_METRIC_REG_JVM_GARBAGE = "jvm.garbage";
+ private static final String PROP_METRIC_REG_JVM_THREADS = "jvm.threads";
+ private static final String PROP_METRIC_REG_JVM_FILES = "jvm.files";
+ private static final String PROP_METRIC_REG_JVM_BUFFERS = "jvm.buffers";
+ private final Logger log = LoggerFactory.getLogger(MetricsConfiguration.class);
+
+ private MetricRegistry metricRegistry = new MetricRegistry();
+
+ private HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();
+
+ private final JHipsterProperties jHipsterProperties;
+
+ private HikariDataSource hikariDataSource;
+
+ public MetricsConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Autowired(required = false)
+ public void setHikariDataSource(HikariDataSource hikariDataSource) {
+ this.hikariDataSource = hikariDataSource;
+ }
+
+ @Override
+ @Bean
+ public MetricRegistry getMetricRegistry() {
+ return metricRegistry;
+ }
+
+ @Override
+ @Bean
+ public HealthCheckRegistry getHealthCheckRegistry() {
+ return healthCheckRegistry;
+ }
+
+ @PostConstruct
+ public void init() {
+ log.debug("Registering JVM gauges");
+ metricRegistry.register(PROP_METRIC_REG_JVM_MEMORY, new MemoryUsageGaugeSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_GARBAGE, new GarbageCollectorMetricSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_THREADS, new ThreadStatesGaugeSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_FILES, new FileDescriptorRatioGauge());
+ metricRegistry.register(PROP_METRIC_REG_JVM_BUFFERS, new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer()));
+ if (hikariDataSource != null) {
+ log.debug("Monitoring the datasource");
+ hikariDataSource.setMetricRegistry(metricRegistry);
+ }
+ if (jHipsterProperties.getMetrics().getJmx().isEnabled()) {
+ log.debug("Initializing Metrics JMX reporting");
+ JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build();
+ jmxReporter.start();
+ }
+ if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
+ log.info("Initializing Metrics Log reporting");
+ final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
+ .outputTo(LoggerFactory.getLogger("metrics"))
+ .convertRatesTo(TimeUnit.SECONDS)
+ .convertDurationsTo(TimeUnit.MILLISECONDS)
+ .build();
+ reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS);
+ }
+ }
+
+ /* Spectator metrics log reporting */
+ @Bean
+ @ConditionalOnProperty("jhipster.logging.spectator-metrics.enabled")
+ @ExportMetricReader
+ public SpectatorMetricReader SpectatorMetricReader(Registry registry) {
+ log.info("Initializing Spectator Metrics Log reporting");
+ return new SpectatorMetricReader(registry);
+ }
+
+ @Bean
+ @ConditionalOnProperty("jhipster.logging.spectator-metrics.enabled")
+ @ExportMetricWriter
+ MetricWriter metricWriter() {
+ return new SpectatorLogMetricWriter();
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/MicroserviceSecurityConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/MicroserviceSecurityConfiguration.java
new file mode 100644
index 0000000000..3e7b125ad6
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/MicroserviceSecurityConfiguration.java
@@ -0,0 +1,71 @@
+package com.car.app.config;
+
+import com.car.app.security.AuthoritiesConstants;
+import com.car.app.security.jwt.JWTConfigurer;
+import com.car.app.security.jwt.TokenProvider;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+public class MicroserviceSecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ private final TokenProvider tokenProvider;
+
+ public MicroserviceSecurityConfiguration(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web.ignoring()
+ .antMatchers(HttpMethod.OPTIONS, "/**")
+ .antMatchers("/app/**/*.{js,html}")
+ .antMatchers("/bower_components/**")
+ .antMatchers("/i18n/**")
+ .antMatchers("/content/**")
+ .antMatchers("/swagger-ui/index.html")
+ .antMatchers("/test/**")
+ .antMatchers("/h2-console/**");
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .csrf()
+ .disable()
+ .headers()
+ .frameOptions()
+ .disable()
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .authorizeRequests()
+ .antMatchers("/api/**").authenticated()
+ .antMatchers("/management/health").permitAll()
+ .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
+ .antMatchers("/swagger-resources/configuration/ui").permitAll()
+ .and()
+ .apply(securityConfigurerAdapter());
+ }
+
+ private JWTConfigurer securityConfigurerAdapter() {
+ return new JWTConfigurer(tokenProvider);
+ }
+
+ @Bean
+ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
+ return new SecurityEvaluationContextExtension();
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/ThymeleafConfiguration.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/ThymeleafConfiguration.java
new file mode 100644
index 0000000000..d84ca403d2
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/ThymeleafConfiguration.java
@@ -0,0 +1,26 @@
+package com.car.app.config;
+
+import org.apache.commons.lang3.CharEncoding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.*;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+
+@Configuration
+public class ThymeleafConfiguration {
+
+ @SuppressWarnings("unused")
+ private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class);
+
+ @Bean
+ @Description("Thymeleaf template resolver serving HTML 5 emails")
+ public ClassLoaderTemplateResolver emailTemplateResolver() {
+ ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
+ emailTemplateResolver.setPrefix("mails/");
+ emailTemplateResolver.setSuffix(".html");
+ emailTemplateResolver.setTemplateMode("HTML5");
+ emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
+ emailTemplateResolver.setOrder(1);
+ return emailTemplateResolver;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/WebConfigurer.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/WebConfigurer.java
new file mode 100644
index 0000000000..c66579e940
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/WebConfigurer.java
@@ -0,0 +1,144 @@
+package com.car.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.servlet.InstrumentedFilter;
+import com.codahale.metrics.servlets.MetricsServlet;
+import com.hazelcast.core.HazelcastInstance;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.embedded.*;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
+import io.undertow.UndertowOptions;
+import org.springframework.boot.web.servlet.ServletContextInitializer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.util.*;
+import javax.servlet.*;
+
+/**
+ * Configuration of web application with Servlet 3.0 APIs.
+ */
+@Configuration
+public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
+
+ private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
+
+ private final Environment env;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ private final HazelcastInstance hazelcastInstance;
+
+ private MetricRegistry metricRegistry;
+
+ public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties, HazelcastInstance hazelcastInstance) {
+
+ this.env = env;
+ this.jHipsterProperties = jHipsterProperties;
+ this.hazelcastInstance = hazelcastInstance;
+ }
+
+ @Override
+ public void onStartup(ServletContext servletContext) throws ServletException {
+ if (env.getActiveProfiles().length != 0) {
+ log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
+ }
+ EnumSet disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
+ initMetrics(servletContext, disps);
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ initH2Console(servletContext);
+ }
+ log.info("Web application fully configured");
+ }
+
+ /**
+ * Customize the Servlet engine: Mime types, the document root, the cache.
+ */
+ @Override
+ public void customize(ConfigurableEmbeddedServletContainer container) {
+ MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
+ // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
+ mappings.add("html", "text/html;charset=utf-8");
+ // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
+ mappings.add("json", "text/html;charset=utf-8");
+ container.setMimeMappings(mappings);
+
+ /*
+ * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
+ * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
+ * See the JHipsterProperties class and your application-*.yml configuration files
+ * for more information.
+ */
+ if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
+ container instanceof UndertowEmbeddedServletContainerFactory) {
+
+ ((UndertowEmbeddedServletContainerFactory) container)
+ .addBuilderCustomizers(builder ->
+ builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
+ }
+ }
+
+ /**
+ * Initializes Metrics.
+ */
+ private void initMetrics(ServletContext servletContext, EnumSet disps) {
+ log.debug("Initializing Metrics registries");
+ servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
+ metricRegistry);
+ servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
+ metricRegistry);
+
+ log.debug("Registering Metrics Filter");
+ FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
+ new InstrumentedFilter());
+
+ metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
+ metricsFilter.setAsyncSupported(true);
+
+ log.debug("Registering Metrics Servlet");
+ ServletRegistration.Dynamic metricsAdminServlet =
+ servletContext.addServlet("metricsServlet", new MetricsServlet());
+
+ metricsAdminServlet.addMapping("/management/metrics/*");
+ metricsAdminServlet.setAsyncSupported(true);
+ metricsAdminServlet.setLoadOnStartup(2);
+ }
+
+ @Bean
+ public CorsFilter corsFilter() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = jHipsterProperties.getCors();
+ if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
+ log.debug("Registering CORS filter");
+ source.registerCorsConfiguration("/api/**", config);
+ source.registerCorsConfiguration("/v2/api-docs", config);
+ }
+ return new CorsFilter(source);
+ }
+
+ /**
+ * Initializes H2 console.
+ */
+ private void initH2Console(ServletContext servletContext) {
+ log.debug("Initialize H2 console");
+ ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
+ h2ConsoleServlet.addMapping("/h2-console/*");
+ h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
+ h2ConsoleServlet.setLoadOnStartup(1);
+ }
+
+ @Autowired(required = false)
+ public void setMetricRegistry(MetricRegistry metricRegistry) {
+ this.metricRegistry = metricRegistry;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/audit/AuditEventConverter.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/audit/AuditEventConverter.java
new file mode 100644
index 0000000000..93680c0ef1
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/audit/AuditEventConverter.java
@@ -0,0 +1,91 @@
+package com.car.app.config.audit;
+
+import com.car.app.domain.PersistentAuditEvent;
+
+import org.springframework.boot.actuate.audit.AuditEvent;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.*;
+
+@Component
+public class AuditEventConverter {
+
+ /**
+ * Convert a list of PersistentAuditEvent to a list of AuditEvent
+ *
+ * @param persistentAuditEvents the list to convert
+ * @return the converted list.
+ */
+ public List convertToAuditEvent(Iterable persistentAuditEvents) {
+ if (persistentAuditEvents == null) {
+ return Collections.emptyList();
+ }
+ List auditEvents = new ArrayList<>();
+ for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) {
+ auditEvents.add(convertToAuditEvent(persistentAuditEvent));
+ }
+ return auditEvents;
+ }
+
+ /**
+ * Convert a PersistentAuditEvent to an AuditEvent
+ *
+ * @param persistentAuditEvent the event to convert
+ * @return the converted list.
+ */
+ public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) {
+ Instant instant = persistentAuditEvent.getAuditEventDate().atZone(ZoneId.systemDefault()).toInstant();
+ return new AuditEvent(Date.from(instant), persistentAuditEvent.getPrincipal(),
+ persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData()));
+ }
+
+ /**
+ * Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface
+ *
+ * @param data the data to convert
+ * @return a map of String, Object
+ */
+ public Map convertDataToObjects(Map data) {
+ Map results = new HashMap<>();
+
+ if (data != null) {
+ for (Map.Entry entry : data.entrySet()) {
+ results.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Internal conversion. This method will allow to save additional data.
+ * By default, it will save the object as string
+ *
+ * @param data the data to convert
+ * @return a map of String, String
+ */
+ public Map convertDataToStrings(Map data) {
+ Map results = new HashMap<>();
+
+ if (data != null) {
+ for (Map.Entry entry : data.entrySet()) {
+ Object object = entry.getValue();
+
+ // Extract the data that will be saved.
+ if (object instanceof WebAuthenticationDetails) {
+ WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) object;
+ results.put("remoteAddress", authenticationDetails.getRemoteAddress());
+ results.put("sessionId", authenticationDetails.getSessionId());
+ } else if (object != null) {
+ results.put(entry.getKey(), object.toString());
+ } else {
+ results.put(entry.getKey(), "null");
+ }
+ }
+ }
+
+ return results;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/audit/package-info.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/audit/package-info.java
new file mode 100644
index 0000000000..0d1f9d950e
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/audit/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Audit specific code.
+ */
+package com.car.app.config.audit;
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/package-info.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/package-info.java
new file mode 100644
index 0000000000..1d4b771794
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/config/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring Framework configuration files.
+ */
+package com.car.app.config;
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/AbstractAuditingEntity.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/AbstractAuditingEntity.java
new file mode 100644
index 0000000000..ee265f27ae
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/AbstractAuditingEntity.java
@@ -0,0 +1,80 @@
+package com.car.app.domain;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.hibernate.envers.Audited;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+import java.time.ZonedDateTime;
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+
+/**
+ * Base abstract class for entities which will hold definitions for created, last modified by and created,
+ * last modified by date.
+ */
+@MappedSuperclass
+@Audited
+@EntityListeners(AuditingEntityListener.class)
+public abstract class AbstractAuditingEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @CreatedBy
+ @Column(name = "created_by", nullable = false, length = 50, updatable = false)
+ @JsonIgnore
+ private String createdBy;
+
+ @CreatedDate
+ @Column(name = "created_date", nullable = false)
+ @JsonIgnore
+ private ZonedDateTime createdDate = ZonedDateTime.now();
+
+ @LastModifiedBy
+ @Column(name = "last_modified_by", length = 50)
+ @JsonIgnore
+ private String lastModifiedBy;
+
+ @LastModifiedDate
+ @Column(name = "last_modified_date")
+ @JsonIgnore
+ private ZonedDateTime lastModifiedDate = ZonedDateTime.now();
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public ZonedDateTime getCreatedDate() {
+ return createdDate;
+ }
+
+ public void setCreatedDate(ZonedDateTime createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ public String getLastModifiedBy() {
+ return lastModifiedBy;
+ }
+
+ public void setLastModifiedBy(String lastModifiedBy) {
+ this.lastModifiedBy = lastModifiedBy;
+ }
+
+ public ZonedDateTime getLastModifiedDate() {
+ return lastModifiedDate;
+ }
+
+ public void setLastModifiedDate(ZonedDateTime lastModifiedDate) {
+ this.lastModifiedDate = lastModifiedDate;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/Car.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/Car.java
new file mode 100644
index 0000000000..dad21815d3
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/Car.java
@@ -0,0 +1,109 @@
+package com.car.app.domain;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * A Car.
+ */
+@Entity
+@Table(name = "car")
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+public class Car implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "make")
+ private String make;
+
+ @Column(name = "brand")
+ private String brand;
+
+ @Column(name = "price")
+ private Double price;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getMake() {
+ return make;
+ }
+
+ public Car make(String make) {
+ this.make = make;
+ return this;
+ }
+
+ public void setMake(String make) {
+ this.make = make;
+ }
+
+ public String getBrand() {
+ return brand;
+ }
+
+ public Car brand(String brand) {
+ this.brand = brand;
+ return this;
+ }
+
+ public void setBrand(String brand) {
+ this.brand = brand;
+ }
+
+ public Double getPrice() {
+ return price;
+ }
+
+ public Car price(Double price) {
+ this.price = price;
+ return this;
+ }
+
+ public void setPrice(Double price) {
+ this.price = price;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Car car = (Car) o;
+ if (car.id == null || id == null) {
+ return false;
+ }
+ return Objects.equals(id, car.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(id);
+ }
+
+ @Override
+ public String toString() {
+ return "Car{" +
+ "id=" + id +
+ ", make='" + make + "'" +
+ ", brand='" + brand + "'" +
+ ", price='" + price + "'" +
+ '}';
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/PersistentAuditEvent.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/PersistentAuditEvent.java
new file mode 100644
index 0000000000..d3d8eb86dc
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/PersistentAuditEvent.java
@@ -0,0 +1,78 @@
+package com.car.app.domain;
+
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Persist AuditEvent managed by the Spring Boot actuator
+ * @see org.springframework.boot.actuate.audit.AuditEvent
+ */
+@Entity
+@Table(name = "jhi_persistent_audit_event")
+public class PersistentAuditEvent implements Serializable {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "event_id")
+ private Long id;
+
+ @NotNull
+ @Column(nullable = false)
+ private String principal;
+
+ @Column(name = "event_date")
+ private LocalDateTime auditEventDate;
+ @Column(name = "event_type")
+ private String auditEventType;
+
+ @ElementCollection
+ @MapKeyColumn(name = "name")
+ @Column(name = "value")
+ @CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id"))
+ private Map data = new HashMap<>();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getPrincipal() {
+ return principal;
+ }
+
+ public void setPrincipal(String principal) {
+ this.principal = principal;
+ }
+
+ public LocalDateTime getAuditEventDate() {
+ return auditEventDate;
+ }
+
+ public void setAuditEventDate(LocalDateTime auditEventDate) {
+ this.auditEventDate = auditEventDate;
+ }
+
+ public String getAuditEventType() {
+ return auditEventType;
+ }
+
+ public void setAuditEventType(String auditEventType) {
+ this.auditEventType = auditEventType;
+ }
+
+ public Map getData() {
+ return data;
+ }
+
+ public void setData(Map data) {
+ this.data = data;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/package-info.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/package-info.java
new file mode 100644
index 0000000000..99ddca6167
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/domain/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * JPA domain objects.
+ */
+package com.car.app.domain;
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/repository/CarRepository.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/repository/CarRepository.java
new file mode 100644
index 0000000000..6aea5a0be8
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/repository/CarRepository.java
@@ -0,0 +1,15 @@
+package com.car.app.repository;
+
+import com.car.app.domain.Car;
+
+import org.springframework.data.jpa.repository.*;
+
+import java.util.List;
+
+/**
+ * Spring Data JPA repository for the Car entity.
+ */
+@SuppressWarnings("unused")
+public interface CarRepository extends JpaRepository {
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/repository/package-info.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/repository/package-info.java
new file mode 100644
index 0000000000..55d8e8e5e5
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/repository/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring Data JPA repositories.
+ */
+package com.car.app.repository;
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/AuthoritiesConstants.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/AuthoritiesConstants.java
new file mode 100644
index 0000000000..ca36d02c5c
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/AuthoritiesConstants.java
@@ -0,0 +1,16 @@
+package com.car.app.security;
+
+/**
+ * Constants for Spring Security authorities.
+ */
+public final class AuthoritiesConstants {
+
+ public static final String ADMIN = "ROLE_ADMIN";
+
+ public static final String USER = "ROLE_USER";
+
+ public static final String ANONYMOUS = "ROLE_ANONYMOUS";
+
+ private AuthoritiesConstants() {
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/SecurityUtils.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/SecurityUtils.java
new file mode 100644
index 0000000000..dc399d4220
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/SecurityUtils.java
@@ -0,0 +1,68 @@
+package com.car.app.security;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * Utility class for Spring Security.
+ */
+public final class SecurityUtils {
+
+ private SecurityUtils() {
+ }
+
+ /**
+ * Get the login of the current user.
+ *
+ * @return the login of the current user
+ */
+ public static String getCurrentUserLogin() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ String userName = null;
+ if (authentication != null) {
+ if (authentication.getPrincipal() instanceof UserDetails) {
+ UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
+ userName = springSecurityUser.getUsername();
+ } else if (authentication.getPrincipal() instanceof String) {
+ userName = (String) authentication.getPrincipal();
+ }
+ }
+ return userName;
+ }
+
+ /**
+ * Check if a user is authenticated.
+ *
+ * @return true if the user is authenticated, false otherwise
+ */
+ public static boolean isAuthenticated() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ if (authentication != null) {
+ return authentication.getAuthorities().stream()
+ .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS));
+ }
+ return false;
+ }
+
+ /**
+ * If the current user has a specific authority (security role).
+ *
+ *
The name of this method comes from the isUserInRole() method in the Servlet API
+ *
+ * @param authority the authority to check
+ * @return true if the current user has the authority, false otherwise
+ */
+ public static boolean isCurrentUserInRole(String authority) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ if (authentication != null) {
+ return authentication.getAuthorities().stream()
+ .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
+ }
+ return false;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/SpringSecurityAuditorAware.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/SpringSecurityAuditorAware.java
new file mode 100644
index 0000000000..a99d9e5e88
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/SpringSecurityAuditorAware.java
@@ -0,0 +1,19 @@
+package com.car.app.security;
+
+import com.car.app.config.Constants;
+
+import org.springframework.data.domain.AuditorAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Implementation of AuditorAware based on Spring Security.
+ */
+@Component
+public class SpringSecurityAuditorAware implements AuditorAware {
+
+ @Override
+ public String getCurrentAuditor() {
+ String userName = SecurityUtils.getCurrentUserLogin();
+ return userName != null ? userName : Constants.SYSTEM_ACCOUNT;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/JWTConfigurer.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/JWTConfigurer.java
new file mode 100644
index 0000000000..957bae3f1e
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/JWTConfigurer.java
@@ -0,0 +1,23 @@
+package com.car.app.security.jwt;
+
+import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+public class JWTConfigurer extends SecurityConfigurerAdapter {
+
+ public static final String AUTHORIZATION_HEADER = "Authorization";
+
+ private TokenProvider tokenProvider;
+
+ public JWTConfigurer(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+ JWTFilter customFilter = new JWTFilter(tokenProvider);
+ http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/JWTFilter.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/JWTFilter.java
new file mode 100644
index 0000000000..2785714cc2
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/JWTFilter.java
@@ -0,0 +1,58 @@
+package com.car.app.security.jwt;
+
+import java.io.IOException;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.GenericFilterBean;
+
+import io.jsonwebtoken.ExpiredJwtException;
+
+/**
+ * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is
+ * found.
+ */
+public class JWTFilter extends GenericFilterBean {
+
+ private final Logger log = LoggerFactory.getLogger(JWTFilter.class);
+
+ private TokenProvider tokenProvider;
+
+ public JWTFilter(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+ try {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+ String jwt = resolveToken(httpServletRequest);
+ if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
+ Authentication authentication = this.tokenProvider.getAuthentication(jwt);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ } catch (ExpiredJwtException eje) {
+ log.info("Security exception for user {} - {}",
+ eje.getClaims().getSubject(), eje.getMessage());
+
+ log.trace("Security exception trace: {}", eje);
+ ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ private String resolveToken(HttpServletRequest request){
+ String bearerToken = request.getHeader(JWTConfigurer.AUTHORIZATION_HEADER);
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
+ return bearerToken.substring(7, bearerToken.length());
+ }
+ return null;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/TokenProvider.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/TokenProvider.java
new file mode 100644
index 0000000000..3908bfa8f3
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/security/jwt/TokenProvider.java
@@ -0,0 +1,109 @@
+package com.car.app.security.jwt;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import javax.annotation.PostConstruct;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.stereotype.Component;
+
+import io.jsonwebtoken.*;
+
+@Component
+public class TokenProvider {
+
+ private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
+
+ private static final String AUTHORITIES_KEY = "auth";
+
+ private String secretKey;
+
+ private long tokenValidityInMilliseconds;
+
+ private long tokenValidityInMillisecondsForRememberMe;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public TokenProvider(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @PostConstruct
+ public void init() {
+ this.secretKey =
+ jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret();
+
+ this.tokenValidityInMilliseconds =
+ 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds();
+ this.tokenValidityInMillisecondsForRememberMe =
+ 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe();
+ }
+
+ public String createToken(Authentication authentication, Boolean rememberMe) {
+ String authorities = authentication.getAuthorities().stream()
+ .map(GrantedAuthority::getAuthority)
+ .collect(Collectors.joining(","));
+
+ long now = (new Date()).getTime();
+ Date validity;
+ if (rememberMe) {
+ validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
+ } else {
+ validity = new Date(now + this.tokenValidityInMilliseconds);
+ }
+
+ return Jwts.builder()
+ .setSubject(authentication.getName())
+ .claim(AUTHORITIES_KEY, authorities)
+ .signWith(SignatureAlgorithm.HS512, secretKey)
+ .setExpiration(validity)
+ .compact();
+ }
+
+ public Authentication getAuthentication(String token) {
+ Claims claims = Jwts.parser()
+ .setSigningKey(secretKey)
+ .parseClaimsJws(token)
+ .getBody();
+
+ Collection extends GrantedAuthority> authorities =
+ Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
+ .map(SimpleGrantedAuthority::new)
+ .collect(Collectors.toList());
+
+ User principal = new User(claims.getSubject(), "", authorities);
+
+ return new UsernamePasswordAuthenticationToken(principal, "", authorities);
+ }
+
+ public boolean validateToken(String authToken) {
+ try {
+ Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
+ return true;
+ } catch (SignatureException e) {
+ log.info("Invalid JWT signature.");
+ log.trace("Invalid JWT signature trace: {}", e);
+ } catch (MalformedJwtException e) {
+ log.info("Invalid JWT token.");
+ log.trace("Invalid JWT token trace: {}", e);
+ } catch (ExpiredJwtException e) {
+ log.info("Expired JWT token.");
+ log.trace("Expired JWT token trace: {}", e);
+ } catch (UnsupportedJwtException e) {
+ log.info("Unsupported JWT token.");
+ log.trace("Unsupported JWT token trace: {}", e);
+ } catch (IllegalArgumentException e) {
+ log.info("JWT token compact of handler are invalid.");
+ log.trace("JWT token compact of handler are invalid trace: {}", e);
+ }
+ return false;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/service/package-info.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/service/package-info.java
new file mode 100644
index 0000000000..2d3df85add
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/service/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Service layer beans.
+ */
+package com.car.app.service;
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/CarResource.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/CarResource.java
new file mode 100644
index 0000000000..1ac6caf83c
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/CarResource.java
@@ -0,0 +1,128 @@
+package com.car.app.web.rest;
+
+import com.codahale.metrics.annotation.Timed;
+import com.car.app.domain.Car;
+
+import com.car.app.repository.CarRepository;
+import com.car.app.web.rest.util.HeaderUtil;
+import com.car.app.web.rest.util.PaginationUtil;
+import io.swagger.annotations.ApiParam;
+import io.github.jhipster.web.util.ResponseUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * REST controller for managing Car.
+ */
+@RestController
+@RequestMapping("/api")
+public class CarResource {
+
+ private final Logger log = LoggerFactory.getLogger(CarResource.class);
+
+ private static final String ENTITY_NAME = "car";
+
+ private final CarRepository carRepository;
+
+ public CarResource(CarRepository carRepository) {
+ this.carRepository = carRepository;
+ }
+
+ /**
+ * POST /cars : Create a new car.
+ *
+ * @param car the car to create
+ * @return the ResponseEntity with status 201 (Created) and with body the new car, or with status 400 (Bad Request) if the car has already an ID
+ * @throws URISyntaxException if the Location URI syntax is incorrect
+ */
+ @PostMapping("/cars")
+ @Timed
+ public ResponseEntity createCar(@RequestBody Car car) throws URISyntaxException {
+ log.debug("REST request to save Car : {}", car);
+ if (car.getId() != null) {
+ return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new car cannot already have an ID")).body(null);
+ }
+ Car result = carRepository.save(car);
+ return ResponseEntity.created(new URI("/api/cars/" + result.getId()))
+ .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
+ .body(result);
+ }
+
+ /**
+ * PUT /cars : Updates an existing car.
+ *
+ * @param car the car to update
+ * @return the ResponseEntity with status 200 (OK) and with body the updated car,
+ * or with status 400 (Bad Request) if the car is not valid,
+ * or with status 500 (Internal Server Error) if the car couldnt be updated
+ * @throws URISyntaxException if the Location URI syntax is incorrect
+ */
+ @PutMapping("/cars")
+ @Timed
+ public ResponseEntity updateCar(@RequestBody Car car) throws URISyntaxException {
+ log.debug("REST request to update Car : {}", car);
+ if (car.getId() == null) {
+ return createCar(car);
+ }
+ Car result = carRepository.save(car);
+ return ResponseEntity.ok()
+ .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, car.getId().toString()))
+ .body(result);
+ }
+
+ /**
+ * GET /cars : get all the cars.
+ *
+ * @param pageable the pagination information
+ * @return the ResponseEntity with status 200 (OK) and the list of cars in body
+ * @throws URISyntaxException if there is an error to generate the pagination HTTP headers
+ */
+ @GetMapping("/cars")
+ @Timed
+ public ResponseEntity> getAllCars(@ApiParam Pageable pageable) {
+ log.debug("REST request to get a page of Cars");
+ Page page = carRepository.findAll(pageable);
+ HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/cars");
+ return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
+ }
+
+ /**
+ * GET /cars/:id : get the "id" car.
+ *
+ * @param id the id of the car to retrieve
+ * @return the ResponseEntity with status 200 (OK) and with body the car, or with status 404 (Not Found)
+ */
+ @GetMapping("/cars/{id}")
+ @Timed
+ public ResponseEntity getCar(@PathVariable Long id) {
+ log.debug("REST request to get Car : {}", id);
+ Car car = carRepository.findOne(id);
+ return ResponseUtil.wrapOrNotFound(Optional.ofNullable(car));
+ }
+
+ /**
+ * DELETE /cars/:id : delete the "id" car.
+ *
+ * @param id the id of the car to delete
+ * @return the ResponseEntity with status 200 (OK)
+ */
+ @DeleteMapping("/cars/{id}")
+ @Timed
+ public ResponseEntity deleteCar(@PathVariable Long id) {
+ log.debug("REST request to delete Car : {}", id);
+ carRepository.delete(id);
+ return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/LogsResource.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/LogsResource.java
new file mode 100644
index 0000000000..b9054ac4f7
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/LogsResource.java
@@ -0,0 +1,39 @@
+package com.car.app.web.rest;
+
+import com.car.app.web.rest.vm.LoggerVM;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import com.codahale.metrics.annotation.Timed;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Controller for view and managing Log Level at runtime.
+ */
+@RestController
+@RequestMapping("/management")
+public class LogsResource {
+
+ @GetMapping("/logs")
+ @Timed
+ public List getList() {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ return context.getLoggerList()
+ .stream()
+ .map(LoggerVM::new)
+ .collect(Collectors.toList());
+ }
+
+ @PutMapping("/logs")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ @Timed
+ public void changeLevel(@RequestBody LoggerVM jsonLogger) {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel()));
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/ProfileInfoResource.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/ProfileInfoResource.java
new file mode 100644
index 0000000000..ad2262f458
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/ProfileInfoResource.java
@@ -0,0 +1,69 @@
+package com.car.app.web.rest;
+
+import com.car.app.config.DefaultProfileUtil;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import org.springframework.core.env.Environment;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Resource to return information about the currently running Spring profiles.
+ */
+@RestController
+@RequestMapping("/api")
+public class ProfileInfoResource {
+
+ private final Environment env;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public ProfileInfoResource(Environment env, JHipsterProperties jHipsterProperties) {
+ this.env = env;
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @GetMapping("/profile-info")
+ public ProfileInfoVM getActiveProfiles() {
+ String[] activeProfiles = DefaultProfileUtil.getActiveProfiles(env);
+ return new ProfileInfoVM(activeProfiles, getRibbonEnv(activeProfiles));
+ }
+
+ private String getRibbonEnv(String[] activeProfiles) {
+ String[] displayOnActiveProfiles = jHipsterProperties.getRibbon().getDisplayOnActiveProfiles();
+ if (displayOnActiveProfiles == null) {
+ return null;
+ }
+ List ribbonProfiles = new ArrayList<>(Arrays.asList(displayOnActiveProfiles));
+ List springBootProfiles = Arrays.asList(activeProfiles);
+ ribbonProfiles.retainAll(springBootProfiles);
+ if (!ribbonProfiles.isEmpty()) {
+ return ribbonProfiles.get(0);
+ }
+ return null;
+ }
+
+ class ProfileInfoVM {
+
+ private String[] activeProfiles;
+
+ private String ribbonEnv;
+
+ ProfileInfoVM(String[] activeProfiles, String ribbonEnv) {
+ this.activeProfiles = activeProfiles;
+ this.ribbonEnv = ribbonEnv;
+ }
+
+ public String[] getActiveProfiles() {
+ return activeProfiles;
+ }
+
+ public String getRibbonEnv() {
+ return ribbonEnv;
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/CustomParameterizedException.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/CustomParameterizedException.java
new file mode 100644
index 0000000000..0bf41f2fdb
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/CustomParameterizedException.java
@@ -0,0 +1,34 @@
+package com.car.app.web.rest.errors;
+
+/**
+ * Custom, parameterized exception, which can be translated on the client side.
+ * For example:
+ *
+ *
+ * throw new CustomParameterizedException("myCustomError", "hello", "world");
+ *
+ *
+ * Can be translated with:
+ *
+ *
+ * "error.myCustomError" : "The server says {{params[0]}} to {{params[1]}}"
+ *
+ */
+public class CustomParameterizedException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+ private final String[] params;
+
+ public CustomParameterizedException(String message, String... params) {
+ super(message);
+ this.message = message;
+ this.params = params;
+ }
+
+ public ParameterizedErrorVM getErrorVM() {
+ return new ParameterizedErrorVM(message, params);
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ErrorConstants.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ErrorConstants.java
new file mode 100644
index 0000000000..f6db48f223
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ErrorConstants.java
@@ -0,0 +1,14 @@
+package com.car.app.web.rest.errors;
+
+public final class ErrorConstants {
+
+ public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure";
+ public static final String ERR_ACCESS_DENIED = "error.accessDenied";
+ public static final String ERR_VALIDATION = "error.validation";
+ public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported";
+ public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError";
+
+ private ErrorConstants() {
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ErrorVM.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ErrorVM.java
new file mode 100644
index 0000000000..69d00c00d5
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ErrorVM.java
@@ -0,0 +1,52 @@
+package com.car.app.web.rest.errors;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * View Model for transferring error message with a list of field errors.
+ */
+public class ErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+ private final String description;
+
+ private List fieldErrors;
+
+ public ErrorVM(String message) {
+ this(message, null);
+ }
+
+ public ErrorVM(String message, String description) {
+ this.message = message;
+ this.description = description;
+ }
+
+ public ErrorVM(String message, String description, List fieldErrors) {
+ this.message = message;
+ this.description = description;
+ this.fieldErrors = fieldErrors;
+ }
+
+ public void add(String objectName, String field, String message) {
+ if (fieldErrors == null) {
+ fieldErrors = new ArrayList<>();
+ }
+ fieldErrors.add(new FieldErrorVM(objectName, field, message));
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public List getFieldErrors() {
+ return fieldErrors;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ExceptionTranslator.java
new file mode 100644
index 0000000000..6282b816e1
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ExceptionTranslator.java
@@ -0,0 +1,85 @@
+package com.car.app.web.rest.errors;
+
+import java.util.List;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.dao.ConcurrencyFailureException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.ResponseEntity.BodyBuilder;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * Controller advice to translate the server side exceptions to client-friendly json structures.
+ */
+@ControllerAdvice
+public class ExceptionTranslator {
+
+ @ExceptionHandler(ConcurrencyFailureException.class)
+ @ResponseStatus(HttpStatus.CONFLICT)
+ @ResponseBody
+ public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) {
+ return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE);
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ResponseBody
+ public ErrorVM processValidationError(MethodArgumentNotValidException ex) {
+ BindingResult result = ex.getBindingResult();
+ List fieldErrors = result.getFieldErrors();
+
+ return processFieldErrors(fieldErrors);
+ }
+
+ @ExceptionHandler(CustomParameterizedException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ResponseBody
+ public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) {
+ return ex.getErrorVM();
+ }
+
+ @ExceptionHandler(AccessDeniedException.class)
+ @ResponseStatus(HttpStatus.FORBIDDEN)
+ @ResponseBody
+ public ErrorVM processAccessDeniedException(AccessDeniedException e) {
+ return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage());
+ }
+
+ private ErrorVM processFieldErrors(List fieldErrors) {
+ ErrorVM dto = new ErrorVM(ErrorConstants.ERR_VALIDATION);
+
+ for (FieldError fieldError : fieldErrors) {
+ dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode());
+ }
+
+ return dto;
+ }
+
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+ @ResponseBody
+ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+ public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
+ return new ErrorVM(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage());
+ }
+
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity processRuntimeException(Exception ex) {
+ BodyBuilder builder;
+ ErrorVM errorVM;
+ ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
+ if (responseStatus != null) {
+ builder = ResponseEntity.status(responseStatus.value());
+ errorVM = new ErrorVM("error." + responseStatus.value().value(), responseStatus.reason());
+ } else {
+ builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
+ errorVM = new ErrorVM(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error");
+ }
+ return builder.body(errorVM);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/FieldErrorVM.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/FieldErrorVM.java
new file mode 100644
index 0000000000..cdb2fc07e5
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/FieldErrorVM.java
@@ -0,0 +1,33 @@
+package com.car.app.web.rest.errors;
+
+import java.io.Serializable;
+
+public class FieldErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String objectName;
+
+ private final String field;
+
+ private final String message;
+
+ public FieldErrorVM(String dto, String field, String message) {
+ this.objectName = dto;
+ this.field = field;
+ this.message = message;
+ }
+
+ public String getObjectName() {
+ return objectName;
+ }
+
+ public String getField() {
+ return field;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ParameterizedErrorVM.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ParameterizedErrorVM.java
new file mode 100644
index 0000000000..b0c40c9cbb
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/errors/ParameterizedErrorVM.java
@@ -0,0 +1,27 @@
+package com.car.app.web.rest.errors;
+
+import java.io.Serializable;
+
+/**
+ * View Model for sending a parameterized error message.
+ */
+public class ParameterizedErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private final String message;
+ private final String[] params;
+
+ public ParameterizedErrorVM(String message, String... params) {
+ this.message = message;
+ this.params = params;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String[] getParams() {
+ return params;
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/package-info.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/package-info.java
new file mode 100644
index 0000000000..fb696c0d89
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring MVC REST controllers.
+ */
+package com.car.app.web.rest;
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/util/HeaderUtil.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/util/HeaderUtil.java
new file mode 100644
index 0000000000..f6f9c1abb3
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/util/HeaderUtil.java
@@ -0,0 +1,43 @@
+package com.car.app.web.rest.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+
+/**
+ * Utility class for HTTP headers creation.
+ */
+public final class HeaderUtil {
+
+ private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class);
+
+ private HeaderUtil() {
+ }
+
+ public static HttpHeaders createAlert(String message, String param) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-carappApp-alert", message);
+ headers.add("X-carappApp-params", param);
+ return headers;
+ }
+
+ public static HttpHeaders createEntityCreationAlert(String entityName, String param) {
+ return createAlert("A new " + entityName + " is created with identifier " + param, param);
+ }
+
+ public static HttpHeaders createEntityUpdateAlert(String entityName, String param) {
+ return createAlert("A " + entityName + " is updated with identifier " + param, param);
+ }
+
+ public static HttpHeaders createEntityDeletionAlert(String entityName, String param) {
+ return createAlert("A " + entityName + " is deleted with identifier " + param, param);
+ }
+
+ public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) {
+ log.error("Entity creation failed, {}", defaultMessage);
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-carappApp-error", defaultMessage);
+ headers.add("X-carappApp-params", entityName);
+ return headers;
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/util/PaginationUtil.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/util/PaginationUtil.java
new file mode 100644
index 0000000000..9ba36f7a44
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/util/PaginationUtil.java
@@ -0,0 +1,47 @@
+package com.car.app.web.rest.util;
+
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.net.URISyntaxException;
+
+/**
+ * Utility class for handling pagination.
+ *
+ *
+ * Pagination uses the same principles as the Github API,
+ * and follow RFC 5988 (Link header).
+ */
+public final class PaginationUtil {
+
+ private PaginationUtil() {
+ }
+
+ public static HttpHeaders generatePaginationHttpHeaders(Page page, String baseUrl) {
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-Total-Count", "" + Long.toString(page.getTotalElements()));
+ String link = "";
+ if ((page.getNumber() + 1) < page.getTotalPages()) {
+ link = "<" + generateUri(baseUrl, page.getNumber() + 1, page.getSize()) + ">; rel=\"next\",";
+ }
+ // prev link
+ if ((page.getNumber()) > 0) {
+ link += "<" + generateUri(baseUrl, page.getNumber() - 1, page.getSize()) + ">; rel=\"prev\",";
+ }
+ // last and first link
+ int lastPage = 0;
+ if (page.getTotalPages() > 0) {
+ lastPage = page.getTotalPages() - 1;
+ }
+ link += "<" + generateUri(baseUrl, lastPage, page.getSize()) + ">; rel=\"last\",";
+ link += "<" + generateUri(baseUrl, 0, page.getSize()) + ">; rel=\"first\"";
+ headers.add(HttpHeaders.LINK, link);
+ return headers;
+ }
+
+ private static String generateUri(String baseUrl, int page, int size) {
+ return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString();
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/vm/LoggerVM.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/vm/LoggerVM.java
new file mode 100644
index 0000000000..ff76466df7
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/vm/LoggerVM.java
@@ -0,0 +1,48 @@
+package com.car.app.web.rest.vm;
+
+import ch.qos.logback.classic.Logger;
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+/**
+ * View Model object for storing a Logback logger.
+ */
+public class LoggerVM {
+
+ private String name;
+
+ private String level;
+
+ public LoggerVM(Logger logger) {
+ this.name = logger.getName();
+ this.level = logger.getEffectiveLevel().toString();
+ }
+
+ @JsonCreator
+ public LoggerVM() {
+ // Empty public constructor used by Jackson.
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLevel() {
+ return level;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ @Override
+ public String toString() {
+ return "LoggerVM{" +
+ "name='" + name + '\'' +
+ ", level='" + level + '\'' +
+ '}';
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/vm/package-info.java b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/vm/package-info.java
new file mode 100644
index 0000000000..02b0247926
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/java/com/car/app/web/rest/vm/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * View Models used by Spring MVC REST controllers.
+ */
+package com.car.app.web.rest.vm;
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/.h2.server.properties b/jhipster/jhipster-microservice/car-app/src/main/resources/.h2.server.properties
new file mode 100644
index 0000000000..54cdead92e
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/.h2.server.properties
@@ -0,0 +1,5 @@
+#H2 Server Properties
+0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/carapp|carapp
+webAllowOthers=true
+webPort=8082
+webSSL=false
diff --git a/jhipster/src/main/resources/banner.txt b/jhipster/jhipster-microservice/car-app/src/main/resources/banner.txt
similarity index 100%
rename from jhipster/src/main/resources/banner.txt
rename to jhipster/jhipster-microservice/car-app/src/main/resources/banner.txt
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/application-dev.yml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/application-dev.yml
new file mode 100644
index 0000000000..87462edc75
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/application-dev.yml
@@ -0,0 +1,148 @@
+# ===================================================================
+# Spring Boot configuration for the "dev" profile.
+#
+# This configuration overrides the application.yml file.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ prefer-ip-address: true
+ client:
+ enabled: true
+ healthcheck:
+ enabled: true
+ registerWithEureka: true
+ fetchRegistry: true
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
+
+spring:
+ profiles:
+ active: dev
+ include: swagger
+ devtools:
+ restart:
+ enabled: true
+ livereload:
+ enabled: false # we use gulp + BrowserSync for livereload
+ jackson:
+ serialization.indent_output: true
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:h2:file:./target/h2db/db/carapp;DB_CLOSE_DELAY=-1
+ username: carapp
+ password:
+ h2:
+ console:
+ enabled: false
+ jpa:
+ database-platform: io.github.jhipster.domain.util.FixedH2Dialect
+ database: H2
+ show-sql: true
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: true
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: true
+ hibernate.cache.region.factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
+ hibernate.cache.hazelcast.instance_name: carapp
+ hibernate.cache.use_minimal_puts: true
+ hibernate.cache.hazelcast.use_lite_member: true
+ mail:
+ host: localhost
+ port: 25
+ username:
+ password:
+ messages:
+ cache-seconds: 1
+ thymeleaf:
+ cache: false
+
+liquibase:
+ contexts: dev
+
+# ===================================================================
+# To enable SSL, generate a certificate using:
+# keytool -genkey -alias carapp -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
+#
+# You can also use Let's Encrypt:
+# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
+#
+# Then, modify the server.ssl properties so your "server" configuration looks like:
+#
+# server:
+# port: 8443
+# ssl:
+# key-store: keystore.p12
+# key-store-password:
+# keyStoreType: PKCS12
+# keyAlias: carapp
+# ===================================================================
+server:
+ port: 8081
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ http:
+ version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
+ cache: # Cache configuration
+ hazelcast: # Hazelcast distributed cache
+ time-to-live-seconds: 3600
+ backup-count: 1
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ token-validity-in-seconds-for-remember-me: 2592000
+ mail: # specific JHipster mail property, for standard properties see MailProperties
+ from: carapp@localhost
+ base-url: http://127.0.0.1:8081
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+ graphite: # Use the "graphite" Maven profile to have the Graphite dependencies
+ enabled: false
+ host: localhost
+ port: 2003
+ prefix: carapp
+ prometheus: # Use the "prometheus" Maven profile to have the Prometheus dependencies
+ enabled: false
+ endpoint: /prometheusMetrics
+ logs: # Reports Dropwizard metrics in the logs
+ enabled: false
+ reportFrequency: 60 # in seconds
+ logging:
+ logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
+ enabled: false
+ host: localhost
+ port: 5000
+ queue-size: 512
+ spectator-metrics: # Reports Spectator Circuit Breaker metrics in the logs
+ enabled: false
+ # edit spring.metrics.export.delay-millis to set report frequency
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/application-prod.yml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/application-prod.yml
new file mode 100644
index 0000000000..9a7f557371
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/application-prod.yml
@@ -0,0 +1,150 @@
+# ===================================================================
+# Spring Boot configuration for the "prod" profile.
+#
+# This configuration overrides the application.yml file.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ prefer-ip-address: true
+ client:
+ enabled: true
+ healthcheck:
+ enabled: true
+ registerWithEureka: true
+ fetchRegistry: true
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
+
+spring:
+ devtools:
+ restart:
+ enabled: false
+ livereload:
+ enabled: false
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:mysql://localhost:3306/carapp?useUnicode=true&characterEncoding=utf8&useSSL=false
+ username: root
+ password:
+ hikari:
+ data-source-properties:
+ cachePrepStmts: true
+ prepStmtCacheSize: 250
+ prepStmtCacheSqlLimit: 2048
+ useServerPrepStmts: true
+ jpa:
+ database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
+ database: MYSQL
+ show-sql: false
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: true
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: false
+ hibernate.cache.region.factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
+ hibernate.cache.hazelcast.instance_name: carapp
+ hibernate.cache.use_minimal_puts: true
+ hibernate.cache.hazelcast.use_lite_member: true
+ mail:
+ host: localhost
+ port: 25
+ username:
+ password:
+ thymeleaf:
+ cache: true
+
+liquibase:
+ contexts: prod
+
+# ===================================================================
+# To enable SSL, generate a certificate using:
+# keytool -genkey -alias carapp -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
+#
+# You can also use Let's Encrypt:
+# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
+#
+# Then, modify the server.ssl properties so your "server" configuration looks like:
+#
+# server:
+# port: 443
+# ssl:
+# key-store: keystore.p12
+# key-store-password:
+# keyStoreType: PKCS12
+# keyAlias: carapp
+# ===================================================================
+server:
+ port: 8081
+ compression:
+ enabled: true
+ mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json
+ min-response-size: 1024
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ http:
+ version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
+ cache: # Used by the CachingHttpHeadersFilter
+ timeToLiveInDays: 1461
+ cache: # Cache configuration
+ hazelcast: # Hazelcast distributed cache
+ time-to-live-seconds: 3600
+ backup-count: 1
+ security:
+ authentication:
+ jwt:
+ secret: 0ebd193e0552c4ac548217d353abbde0761a1997
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ token-validity-in-seconds-for-remember-me: 2592000
+ mail: # specific JHipster mail property, for standard properties see MailProperties
+ from: carapp@localhost
+ base-url: http://my-server-url-to-change # Modify according to your server's URL
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+ graphite:
+ enabled: false
+ host: localhost
+ port: 2003
+ prefix: carapp
+ prometheus:
+ enabled: false
+ endpoint: /prometheusMetrics
+ logs: # Reports Dropwizard metrics in the logs
+ enabled: false
+ reportFrequency: 60 # in seconds
+ logging:
+ logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
+ enabled: false
+ host: localhost
+ port: 5000
+ queue-size: 512
+ spectator-metrics: # Reports Spectator Circuit Breaker metrics in the logs
+ enabled: false
+ # edit spring.metrics.export.delay-millis to set report frequency
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/application.yml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/application.yml
new file mode 100644
index 0000000000..40ccb38cad
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/application.yml
@@ -0,0 +1,119 @@
+# ===================================================================
+# Spring Boot configuration.
+#
+# This configuration will be overriden by the Spring profile you use,
+# for example application-dev.yml if you use the "dev" profile.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ appname: carapp
+ instanceId: carapp:${spring.application.instance_id:${random.value}}
+ statusPageUrlPath: ${management.context-path}/info
+ healthCheckUrlPath: ${management.context-path}/health
+ metadata-map:
+ profile: ${spring.profiles.active}
+ version: ${info.project.version}
+ribbon:
+ eureka:
+ enabled: true
+# See https://github.com/Netflix/Hystrix/wiki/Configuration
+#hystrix:
+# command:
+# default:
+# execution:
+# isolation:
+# thread:
+# timeoutInMilliseconds: 10000
+
+management:
+ security:
+ roles: ADMIN
+ context-path: /management
+ health:
+ mail:
+ enabled: false # When using the MailService, configure an SMTP server and set this to true
+spring:
+ application:
+ name: carapp
+ jackson:
+ serialization.write_dates_as_timestamps: false
+ jpa:
+ open-in-view: false
+ hibernate:
+ ddl-auto: none
+ naming:
+ physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
+ implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ messages:
+ basename: i18n/messages
+ mvc:
+ favicon:
+ enabled: false
+ thymeleaf:
+ mode: XHTML
+
+security:
+ basic:
+ enabled: false
+
+server:
+ session:
+ cookie:
+ http-only: true
+
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ async:
+ core-pool-size: 2
+ max-pool-size: 50
+ queue-capacity: 10000
+ # By default CORS is disabled. Uncomment to enable.
+ #cors:
+ #allowed-origins: "*"
+ #allowed-methods: GET, PUT, POST, DELETE, OPTIONS
+ #allowed-headers: "*"
+ #exposed-headers:
+ #allow-credentials: true
+ #max-age: 1800
+ mail:
+ from: carapp@localhost
+ swagger:
+ default-include-pattern: /api/.*
+ title: carapp API
+ description: carapp API documentation
+ version: 0.0.1
+ terms-of-service-url:
+ contact-name:
+ contact-url:
+ contact-email:
+ license:
+ license-url:
+ ribbon:
+ display-on-active-profiles: dev
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/bootstrap-prod.yml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/bootstrap-prod.yml
new file mode 100644
index 0000000000..524e37ea53
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/bootstrap-prod.yml
@@ -0,0 +1,22 @@
+# ===================================================================
+# Spring Cloud Config bootstrap configuration for the "prod" profile
+# ===================================================================
+
+spring:
+ cloud:
+ config:
+ fail-fast: true
+ retry:
+ initial-interval: 1000
+ max-interval: 2000
+ max-attempts: 100
+ uri: http://admin:${jhipster.registry.password}@localhost:8761/config
+ # name of the config server's property source (file.yml) that we want to use
+ name: carapp
+ profile: prod # profile(s) of the property source
+ label: master # toggle to switch to a different version of the configuration as stored in git
+ # it can be set to any label, branch or commit of the config source git repository
+
+jhipster:
+ registry:
+ password: admin
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/bootstrap.yml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/bootstrap.yml
new file mode 100644
index 0000000000..c1a608c4a5
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/bootstrap.yml
@@ -0,0 +1,30 @@
+# ===================================================================
+# Spring Cloud Config bootstrap configuration for the "dev" profile
+# In prod profile, properties will be overwriten by the ones defined in bootstrap-prod.yml
+# ===================================================================
+
+jhipster:
+ registry:
+ password: admin
+
+spring:
+ application:
+ name: carapp
+ profiles:
+ # The commented value for `active` can be replaced with valid Spring profiles to load.
+ # Otherwise, it will be filled in by maven when building the WAR file
+ # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS`
+ active: #spring.profiles.active#
+ cloud:
+ config:
+ fail-fast: true
+ uri: http://admin:${jhipster.registry.password}@localhost:8761/config
+ # name of the config server's property source (file.yml) that we want to use
+ name: carapp
+ profile: dev # profile(s) of the property source
+ label: master # toggle to switch to a different version of the configuration as stored in git
+ # it can be set to any label, branch or commit of the config source git repository
+
+info:
+ project:
+ version: #project.version#
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
new file mode 100644
index 0000000000..2a1ee4d6bf
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/changelog/20170503041524_added_entity_Car.xml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/changelog/20170503041524_added_entity_Car.xml
new file mode 100644
index 0000000000..0b8c3f5136
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/changelog/20170503041524_added_entity_Car.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/master.xml b/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/master.xml
new file mode 100644
index 0000000000..398b615ea1
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/config/liquibase/master.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/i18n/messages.properties b/jhipster/jhipster-microservice/car-app/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000000..9a850cb789
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/i18n/messages.properties
@@ -0,0 +1,22 @@
+# Error page
+error.title=Your request cannot be processed
+error.subtitle=Sorry, an error has occurred.
+error.status=Status:
+error.message=Message:
+
+# Activation e-mail
+email.activation.title=carapp account activation
+email.activation.greeting=Dear {0}
+email.activation.text1=Your carapp account has been created, please click on the URL below to activate it:
+email.activation.text2=Regards,
+email.signature=carapp Team.
+
+# Creation email
+email.creation.text1=Your carapp account has been created, please click on the URL below to access it:
+
+# Reset e-mail
+email.reset.title=carapp password reset
+email.reset.greeting=Dear {0}
+email.reset.text1=For your carapp account a password reset was requested, please click on the URL below to reset it:
+email.reset.text2=Regards,
+
diff --git a/jhipster/jhipster-microservice/car-app/src/main/resources/logback-spring.xml b/jhipster/jhipster-microservice/car-app/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000..33c3d4fde4
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/main/resources/logback-spring.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
diff --git a/jhipster/src/main/resources/templates/error.html b/jhipster/jhipster-microservice/car-app/src/main/resources/templates/error.html
similarity index 100%
rename from jhipster/src/main/resources/templates/error.html
rename to jhipster/jhipster-microservice/car-app/src/main/resources/templates/error.html
diff --git a/jhipster/jhipster-microservice/car-app/src/test/java/com/car/app/web/rest/CarResourceIntTest.java b/jhipster/jhipster-microservice/car-app/src/test/java/com/car/app/web/rest/CarResourceIntTest.java
new file mode 100644
index 0000000000..b18e5950d3
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/test/java/com/car/app/web/rest/CarResourceIntTest.java
@@ -0,0 +1,244 @@
+package com.car.app.web.rest;
+
+import com.car.app.CarappApp;
+
+import com.car.app.domain.Car;
+import com.car.app.repository.CarRepository;
+import com.car.app.web.rest.errors.ExceptionTranslator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * Test class for the CarResource REST controller.
+ *
+ * @see CarResource
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = CarappApp.class)
+public class CarResourceIntTest {
+
+ private static final String DEFAULT_MAKE = "AAAAAAAAAA";
+ private static final String UPDATED_MAKE = "BBBBBBBBBB";
+
+ private static final String DEFAULT_BRAND = "AAAAAAAAAA";
+ private static final String UPDATED_BRAND = "BBBBBBBBBB";
+
+ private static final Double DEFAULT_PRICE = 1D;
+ private static final Double UPDATED_PRICE = 2D;
+
+ @Autowired
+ private CarRepository carRepository;
+
+ @Autowired
+ private MappingJackson2HttpMessageConverter jacksonMessageConverter;
+
+ @Autowired
+ private PageableHandlerMethodArgumentResolver pageableArgumentResolver;
+
+ @Autowired
+ private ExceptionTranslator exceptionTranslator;
+
+ @Autowired
+ private EntityManager em;
+
+ private MockMvc restCarMockMvc;
+
+ private Car car;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ CarResource carResource = new CarResource(carRepository);
+ this.restCarMockMvc = MockMvcBuilders.standaloneSetup(carResource)
+ .setCustomArgumentResolvers(pageableArgumentResolver)
+ .setControllerAdvice(exceptionTranslator)
+ .setMessageConverters(jacksonMessageConverter).build();
+ }
+
+ /**
+ * Create an entity for this test.
+ *
+ * This is a static method, as tests for other entities might also need it,
+ * if they test an entity which requires the current entity.
+ */
+ public static Car createEntity(EntityManager em) {
+ Car car = new Car()
+ .make(DEFAULT_MAKE)
+ .brand(DEFAULT_BRAND)
+ .price(DEFAULT_PRICE);
+ return car;
+ }
+
+ @Before
+ public void initTest() {
+ car = createEntity(em);
+ }
+
+ @Test
+ @Transactional
+ public void createCar() throws Exception {
+ int databaseSizeBeforeCreate = carRepository.findAll().size();
+
+ // Create the Car
+ restCarMockMvc.perform(post("/api/cars")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(car)))
+ .andExpect(status().isCreated());
+
+ // Validate the Car in the database
+ List carList = carRepository.findAll();
+ assertThat(carList).hasSize(databaseSizeBeforeCreate + 1);
+ Car testCar = carList.get(carList.size() - 1);
+ assertThat(testCar.getMake()).isEqualTo(DEFAULT_MAKE);
+ assertThat(testCar.getBrand()).isEqualTo(DEFAULT_BRAND);
+ assertThat(testCar.getPrice()).isEqualTo(DEFAULT_PRICE);
+ }
+
+ @Test
+ @Transactional
+ public void createCarWithExistingId() throws Exception {
+ int databaseSizeBeforeCreate = carRepository.findAll().size();
+
+ // Create the Car with an existing ID
+ car.setId(1L);
+
+ // An entity with an existing ID cannot be created, so this API call must fail
+ restCarMockMvc.perform(post("/api/cars")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(car)))
+ .andExpect(status().isBadRequest());
+
+ // Validate the Alice in the database
+ List carList = carRepository.findAll();
+ assertThat(carList).hasSize(databaseSizeBeforeCreate);
+ }
+
+ @Test
+ @Transactional
+ public void getAllCars() throws Exception {
+ // Initialize the database
+ carRepository.saveAndFlush(car);
+
+ // Get all the carList
+ restCarMockMvc.perform(get("/api/cars?sort=id,desc"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andExpect(jsonPath("$.[*].id").value(hasItem(car.getId().intValue())))
+ .andExpect(jsonPath("$.[*].make").value(hasItem(DEFAULT_MAKE.toString())))
+ .andExpect(jsonPath("$.[*].brand").value(hasItem(DEFAULT_BRAND.toString())))
+ .andExpect(jsonPath("$.[*].price").value(hasItem(DEFAULT_PRICE.doubleValue())));
+ }
+
+ @Test
+ @Transactional
+ public void getCar() throws Exception {
+ // Initialize the database
+ carRepository.saveAndFlush(car);
+
+ // Get the car
+ restCarMockMvc.perform(get("/api/cars/{id}", car.getId()))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andExpect(jsonPath("$.id").value(car.getId().intValue()))
+ .andExpect(jsonPath("$.make").value(DEFAULT_MAKE.toString()))
+ .andExpect(jsonPath("$.brand").value(DEFAULT_BRAND.toString()))
+ .andExpect(jsonPath("$.price").value(DEFAULT_PRICE.doubleValue()));
+ }
+
+ @Test
+ @Transactional
+ public void getNonExistingCar() throws Exception {
+ // Get the car
+ restCarMockMvc.perform(get("/api/cars/{id}", Long.MAX_VALUE))
+ .andExpect(status().isNotFound());
+ }
+
+ @Test
+ @Transactional
+ public void updateCar() throws Exception {
+ // Initialize the database
+ carRepository.saveAndFlush(car);
+ int databaseSizeBeforeUpdate = carRepository.findAll().size();
+
+ // Update the car
+ Car updatedCar = carRepository.findOne(car.getId());
+ updatedCar
+ .make(UPDATED_MAKE)
+ .brand(UPDATED_BRAND)
+ .price(UPDATED_PRICE);
+
+ restCarMockMvc.perform(put("/api/cars")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(updatedCar)))
+ .andExpect(status().isOk());
+
+ // Validate the Car in the database
+ List carList = carRepository.findAll();
+ assertThat(carList).hasSize(databaseSizeBeforeUpdate);
+ Car testCar = carList.get(carList.size() - 1);
+ assertThat(testCar.getMake()).isEqualTo(UPDATED_MAKE);
+ assertThat(testCar.getBrand()).isEqualTo(UPDATED_BRAND);
+ assertThat(testCar.getPrice()).isEqualTo(UPDATED_PRICE);
+ }
+
+ @Test
+ @Transactional
+ public void updateNonExistingCar() throws Exception {
+ int databaseSizeBeforeUpdate = carRepository.findAll().size();
+
+ // Create the Car
+
+ // If the entity doesn't have an ID, it will be created instead of just being updated
+ restCarMockMvc.perform(put("/api/cars")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(car)))
+ .andExpect(status().isCreated());
+
+ // Validate the Car in the database
+ List carList = carRepository.findAll();
+ assertThat(carList).hasSize(databaseSizeBeforeUpdate + 1);
+ }
+
+ @Test
+ @Transactional
+ public void deleteCar() throws Exception {
+ // Initialize the database
+ carRepository.saveAndFlush(car);
+ int databaseSizeBeforeDelete = carRepository.findAll().size();
+
+ // Get the car
+ restCarMockMvc.perform(delete("/api/cars/{id}", car.getId())
+ .accept(TestUtil.APPLICATION_JSON_UTF8))
+ .andExpect(status().isOk());
+
+ // Validate the database is empty
+ List carList = carRepository.findAll();
+ assertThat(carList).hasSize(databaseSizeBeforeDelete - 1);
+ }
+
+ @Test
+ @Transactional
+ public void equalsVerifier() throws Exception {
+ TestUtil.equalsVerifier(Car.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/test/java/com/car/app/web/rest/TestUtil.java b/jhipster/jhipster-microservice/car-app/src/test/java/com/car/app/web/rest/TestUtil.java
new file mode 100644
index 0000000000..631200376b
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/test/java/com/car/app/web/rest/TestUtil.java
@@ -0,0 +1,120 @@
+package com.car.app.web.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.http.MediaType;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeParseException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Utility class for testing REST controllers.
+ */
+public class TestUtil {
+
+ /** MediaType for JSON UTF8 */
+ public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(
+ MediaType.APPLICATION_JSON.getType(),
+ MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
+
+ /**
+ * Convert an object to JSON byte array.
+ *
+ * @param object
+ * the object to convert
+ * @return the JSON byte array
+ * @throws IOException
+ */
+ public static byte[] convertObjectToJsonBytes(Object object)
+ throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+
+ JavaTimeModule module = new JavaTimeModule();
+ mapper.registerModule(module);
+
+ return mapper.writeValueAsBytes(object);
+ }
+
+ /**
+ * Create a byte array with a specific size filled with specified data.
+ *
+ * @param size the size of the byte array
+ * @param data the data to put in the byte array
+ * @return the JSON byte array
+ */
+ public static byte[] createByteArray(int size, String data) {
+ byte[] byteArray = new byte[size];
+ for (int i = 0; i < size; i++) {
+ byteArray[i] = Byte.parseByte(data, 2);
+ }
+ return byteArray;
+ }
+
+ /**
+ * A matcher that tests that the examined string represents the same instant as the reference datetime.
+ */
+ public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher {
+
+ private final ZonedDateTime date;
+
+ public ZonedDateTimeMatcher(ZonedDateTime date) {
+ this.date = date;
+ }
+
+ @Override
+ protected boolean matchesSafely(String item, Description mismatchDescription) {
+ try {
+ if (!date.isEqual(ZonedDateTime.parse(item))) {
+ mismatchDescription.appendText("was ").appendValue(item);
+ return false;
+ }
+ return true;
+ } catch (DateTimeParseException e) {
+ mismatchDescription.appendText("was ").appendValue(item)
+ .appendText(", which could not be parsed as a ZonedDateTime");
+ return false;
+ }
+
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("a String representing the same Instant as ").appendValue(date);
+ }
+ }
+
+ /**
+ * Creates a matcher that matches when the examined string reprensents the same instant as the reference datetime
+ * @param date the reference datetime against which the examined string is checked
+ */
+ public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) {
+ return new ZonedDateTimeMatcher(date);
+ }
+
+ /**
+ * Verifies the equals/hashcode contract on the domain object.
+ */
+ public static void equalsVerifier(Class clazz) throws Exception {
+ Object domainObject1 = clazz.getConstructor().newInstance();
+ assertThat(domainObject1.toString()).isNotNull();
+ assertThat(domainObject1).isEqualTo(domainObject1);
+ assertThat(domainObject1.hashCode()).isEqualTo(domainObject1.hashCode());
+ // Test with an instance of another class
+ Object testOtherObject = new Object();
+ assertThat(domainObject1).isNotEqualTo(testOtherObject);
+ // Test with an instance of the same class
+ Object domainObject2 = clazz.getConstructor().newInstance();
+ assertThat(domainObject1).isNotEqualTo(domainObject2);
+ // HashCodes are equals because the objects are not persisted yet
+ assertThat(domainObject1.hashCode()).isEqualTo(domainObject2.hashCode());
+ }
+}
diff --git a/jhipster/jhipster-microservice/car-app/src/test/resources/config/application.yml b/jhipster/jhipster-microservice/car-app/src/test/resources/config/application.yml
new file mode 100644
index 0000000000..a9e3b02b64
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/test/resources/config/application.yml
@@ -0,0 +1,102 @@
+# ===================================================================
+# Spring Boot configuration.
+#
+# This configuration is used for unit/integration tests.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ client:
+ enabled: false
+ instance:
+ appname: carapp
+ instanceId: carapp:${spring.application.instance_id:${random.value}}
+
+spring:
+ application:
+ name: carapp
+ jackson:
+ serialization.write_dates_as_timestamps: false
+ cache:
+ type: none
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:h2:mem:carapp;DB_CLOSE_DELAY=-1
+ name:
+ username:
+ password:
+ jpa:
+ database-platform: io.github.jhipster.domain.util.FixedH2Dialect
+ database: H2
+ open-in-view: false
+ show-sql: true
+ hibernate:
+ ddl-auto: none
+ naming:
+ physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
+ implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: false
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: true
+ hibernate.hbm2ddl.auto: validate
+ mail:
+ host: localhost
+ messages:
+ basename: i18n/messages
+ mvc:
+ favicon:
+ enabled: false
+ thymeleaf:
+ mode: XHTML
+
+liquibase:
+ contexts: test
+
+security:
+ basic:
+ enabled: false
+
+server:
+ port: 10344
+ address: localhost
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ async:
+ core-pool-size: 2
+ max-pool-size: 50
+ queue-capacity: 10000
+ security:
+ authentication:
+ jwt:
+ secret: 0ebd193e0552c4ac548217d353abbde0761a1997
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/car-app/src/test/resources/config/bootstrap.yml b/jhipster/jhipster-microservice/car-app/src/test/resources/config/bootstrap.yml
new file mode 100644
index 0000000000..11cd6af21c
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/test/resources/config/bootstrap.yml
@@ -0,0 +1,4 @@
+spring:
+ cloud:
+ config:
+ enabled: false
diff --git a/jhipster/jhipster-microservice/car-app/src/test/resources/logback-test.xml b/jhipster/jhipster-microservice/car-app/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..aace473c4c
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/src/test/resources/logback-test.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/car-app/yarn.lock b/jhipster/jhipster-microservice/car-app/yarn.lock
new file mode 100644
index 0000000000..4333a516f1
--- /dev/null
+++ b/jhipster/jhipster-microservice/car-app/yarn.lock
@@ -0,0 +1,2439 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+abbrev@1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
+
+acorn-jsx@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+ dependencies:
+ acorn "^3.0.4"
+
+acorn@^3.0.4:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+
+ajv@^4.9.1:
+ version "4.11.8"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
+ dependencies:
+ co "^4.6.0"
+ json-stable-stringify "^1.0.1"
+
+amdefine@>=0.0.4:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
+
+ansi-escapes@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-styles@^2.0.0, ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+ansi@^0.3.0, ansi@~0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21"
+
+are-we-there-yet@~1.1.2:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
+ dependencies:
+ sprintf-js "~1.0.2"
+
+array-differ@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031"
+
+array-find-index@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-uniq@^1.0.0, array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+
+arrify@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+
+asn1@~0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+
+assert-plus@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+
+ast-query@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ast-query/-/ast-query-2.0.0.tgz#3588e79ad8de07ce50df1e781cc2bda1fd69a453"
+ dependencies:
+ acorn-jsx "^3.0.1"
+ class-extend "^0.1.1"
+ escodegen-wallaby "^1.6.7"
+ lodash "^4.6.1"
+ traverse "^0.6.6"
+
+async@^1.0.0, async@^1.4.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+
+async@^2.0.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611"
+ dependencies:
+ lodash "^4.14.0"
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+
+aws-sign2@~0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
+
+aws4@^1.2.1:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+
+balanced-match@^0.4.1:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
+ dependencies:
+ tweetnacl "^0.14.3"
+
+bin-version-check@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-2.1.0.tgz#e4e5df290b9069f7d111324031efc13fdd11a5b0"
+ dependencies:
+ bin-version "^1.0.0"
+ minimist "^1.1.0"
+ semver "^4.0.3"
+ semver-truncate "^1.0.0"
+
+bin-version@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-1.0.4.tgz#9eb498ee6fd76f7ab9a7c160436f89579435d78e"
+ dependencies:
+ find-versions "^1.0.0"
+
+"binaryextensions@1 || 2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.0.0.tgz#e597d1a7a6a3558a2d1c7241a16c99965e6aa40f"
+
+boolbase@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+
+boom@2.x.x:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
+ dependencies:
+ hoek "2.x.x"
+
+boxen@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/boxen/-/boxen-0.3.1.tgz#a7d898243ae622f7abb6bb604d740a76c6a5461b"
+ dependencies:
+ chalk "^1.1.1"
+ filled-array "^1.0.0"
+ object-assign "^4.0.1"
+ repeating "^2.0.0"
+ string-width "^1.0.1"
+ widest-line "^1.0.0"
+
+brace-expansion@^1.0.0:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59"
+ dependencies:
+ balanced-match "^0.4.1"
+ concat-map "0.0.1"
+
+buffer-shims@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
+
+builtin-modules@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+camelcase-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
+ dependencies:
+ camelcase "^2.0.0"
+ map-obj "^1.0.0"
+
+camelcase@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
+
+capture-stack-trace@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+cheerio@0.22.0:
+ version "0.22.0"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
+ dependencies:
+ css-select "~1.2.0"
+ dom-serializer "~0.1.0"
+ entities "~1.1.1"
+ htmlparser2 "^3.9.1"
+ lodash.assignin "^4.0.9"
+ lodash.bind "^4.1.4"
+ lodash.defaults "^4.0.1"
+ lodash.filter "^4.4.0"
+ lodash.flatten "^4.2.0"
+ lodash.foreach "^4.3.0"
+ lodash.map "^4.4.0"
+ lodash.merge "^4.4.0"
+ lodash.pick "^4.2.1"
+ lodash.reduce "^4.4.0"
+ lodash.reject "^4.4.0"
+ lodash.some "^4.4.0"
+
+cheerio@^0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925"
+ dependencies:
+ css-select "~1.0.0"
+ dom-serializer "~0.1.0"
+ entities "~1.1.1"
+ htmlparser2 "~3.8.1"
+ lodash "^3.2.0"
+
+class-extend@^0.1.0, class-extend@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/class-extend/-/class-extend-0.1.2.tgz#8057a82b00f53f82a5d62c50ef8cffdec6fabc34"
+ dependencies:
+ object-assign "^2.0.0"
+
+cli-boxes@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
+
+cli-cursor@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
+ dependencies:
+ restore-cursor "^1.0.1"
+
+cli-list@^0.1.1:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/cli-list/-/cli-list-0.1.8.tgz#aee6d45c4c59bf80068bb968089fb06f1aeddc0a"
+
+cli-table@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
+ dependencies:
+ colors "1.0.3"
+
+cli-width@^1.0.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-1.1.1.tgz#a4d293ef67ebb7b88d4a4d42c0ccf00c4d1e366d"
+
+cli-width@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+
+clone-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-1.0.0.tgz#eae0a2413f55c0942f818c229fefce845d7f3b1c"
+ dependencies:
+ is-regexp "^1.0.0"
+ is-supported-regexp-flag "^1.0.0"
+
+clone-stats@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1"
+
+clone@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149"
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+
+colors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
+
+combined-stream@^1.0.5, combined-stream@~1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
+concat-stream@^1.4.7:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
+ dependencies:
+ inherits "^2.0.3"
+ readable-stream "^2.2.2"
+ typedarray "^0.0.6"
+
+config-chain@~1.1.8:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2"
+ dependencies:
+ ini "^1.3.4"
+ proto-list "~1.2.1"
+
+configstore@^1.0.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021"
+ dependencies:
+ graceful-fs "^4.1.2"
+ mkdirp "^0.5.0"
+ object-assign "^4.0.1"
+ os-tmpdir "^1.0.0"
+ osenv "^0.1.0"
+ uuid "^2.0.1"
+ write-file-atomic "^1.1.2"
+ xdg-basedir "^2.0.0"
+
+configstore@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1"
+ dependencies:
+ dot-prop "^3.0.0"
+ graceful-fs "^4.1.2"
+ mkdirp "^0.5.0"
+ object-assign "^4.0.1"
+ os-tmpdir "^1.0.0"
+ osenv "^0.1.0"
+ uuid "^2.0.1"
+ write-file-atomic "^1.1.2"
+ xdg-basedir "^2.0.0"
+
+core-util-is@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+create-error-class@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
+ dependencies:
+ capture-stack-trace "^1.0.0"
+
+cross-spawn@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
+ dependencies:
+ lru-cache "^4.0.1"
+ which "^1.2.9"
+
+cross-spawn@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41"
+ dependencies:
+ lru-cache "^4.0.1"
+ which "^1.2.9"
+
+cryptiles@2.x.x:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
+ dependencies:
+ boom "2.x.x"
+
+css-select@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0"
+ dependencies:
+ boolbase "~1.0.0"
+ css-what "1.0"
+ domutils "1.4"
+ nth-check "~1.0.0"
+
+css-select@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+ dependencies:
+ boolbase "~1.0.0"
+ css-what "2.1"
+ domutils "1.5.1"
+ nth-check "~1.0.1"
+
+css-what@1.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c"
+
+css-what@2.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
+
+currently-unhandled@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
+ dependencies:
+ array-find-index "^1.0.1"
+
+dargs@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ dependencies:
+ assert-plus "^1.0.0"
+
+dateformat@^1.0.11:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9"
+ dependencies:
+ get-stdin "^4.0.1"
+ meow "^3.3.0"
+
+debug@^2.0.0, debug@^2.1.0, debug@^2.2.0:
+ version "2.6.6"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.6.tgz#a9fa6fbe9ca43cf1e79f73b75c0189cbb7d6db5a"
+ dependencies:
+ ms "0.7.3"
+
+decamelize@^1.0.0, decamelize@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+
+deep-extend@^0.4.0, deep-extend@~0.4.0:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
+
+deep-is@~0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+
+default-uid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/default-uid/-/default-uid-1.0.0.tgz#fcefa9df9f5ac40c8916d912dd1fe1146aa3c59e"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+
+detect-conflict@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e"
+
+detect-newline@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-1.0.3.tgz#e97b1003877d70c09af1af35bfadff168de4920d"
+ dependencies:
+ get-stdin "^4.0.1"
+ minimist "^1.1.0"
+
+diff@^2.1.2:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99"
+
+discontinuous-range@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a"
+
+dom-serializer@0, dom-serializer@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+ dependencies:
+ domelementtype "~1.1.1"
+ entities "~1.1.1"
+
+domelementtype@1, domelementtype@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+
+domelementtype@~1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
+domhandler@2.3, domhandler@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738"
+ dependencies:
+ domelementtype "1"
+
+domutils@1.4:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f"
+ dependencies:
+ domelementtype "1"
+
+domutils@1.5, domutils@1.5.1, domutils@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
+dot-prop@^2.0.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-2.4.0.tgz#848e28f7f1d50740c6747ab3cb07670462b6f89c"
+ dependencies:
+ is-obj "^1.0.0"
+
+dot-prop@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
+ dependencies:
+ is-obj "^1.0.0"
+
+downgrade-root@^1.0.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/downgrade-root/-/downgrade-root-1.2.2.tgz#531319715b0e81ffcc22eb28478ba27643e12c6c"
+ dependencies:
+ default-uid "^1.0.0"
+ is-root "^1.0.0"
+
+duplexer2@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+ dependencies:
+ readable-stream "^2.0.2"
+
+each-async@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/each-async/-/each-async-1.1.1.tgz#dee5229bdf0ab6ba2012a395e1b869abf8813473"
+ dependencies:
+ onetime "^1.0.0"
+ set-immediate-shim "^1.0.0"
+
+ecc-jsbn@~0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
+ dependencies:
+ jsbn "~0.1.0"
+
+editions@^1.1.1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.3.tgz#0907101bdda20fac3cbe334c27cbd0688dc99a5b"
+
+ejs@2.5.6, ejs@^2.3.1:
+ version "2.5.6"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88"
+
+entities@1.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26"
+
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
+error-ex@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
+ dependencies:
+ is-arrayish "^0.2.1"
+
+error@^7.0.2:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02"
+ dependencies:
+ string-template "~0.2.1"
+ xtend "~4.0.0"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+escodegen-wallaby@^1.6.7:
+ version "1.6.11"
+ resolved "https://registry.yarnpkg.com/escodegen-wallaby/-/escodegen-wallaby-1.6.11.tgz#39100cde743f9acdd24bd868db5a12fbacde6ed1"
+ dependencies:
+ esprima "^2.7.1"
+ estraverse "^1.9.1"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.2.0"
+
+esprima@^2.7.1:
+ version "2.7.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+
+esprima@^3.1.1:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+
+estraverse@^1.9.1:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
+
+esutils@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+
+execall@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73"
+ dependencies:
+ clone-regexp "^1.0.0"
+
+exit-hook@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
+
+extend@^3.0.0, extend@~3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+
+external-editor@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.1.tgz#12d7b0db850f7ff7e7081baf4005700060c4600b"
+ dependencies:
+ extend "^3.0.0"
+ spawn-sync "^1.0.15"
+ tmp "^0.0.29"
+
+extsprintf@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
+
+fast-levenshtein@~2.0.4:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+
+figures@^1.3.5:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
+ dependencies:
+ escape-string-regexp "^1.0.5"
+ object-assign "^4.1.0"
+
+filled-array@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/filled-array/-/filled-array-1.1.0.tgz#c3c4f6c663b923459a9aa29912d2d031f1507f84"
+
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ dependencies:
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+find-versions@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-1.2.1.tgz#cbde9f12e38575a0af1be1b9a2c5d5fd8f186b62"
+ dependencies:
+ array-uniq "^1.0.0"
+ get-stdin "^4.0.1"
+ meow "^3.5.0"
+ semver-regex "^1.0.0"
+
+first-chunk-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70"
+ dependencies:
+ readable-stream "^2.0.2"
+
+foreachasync@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6"
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+
+form-data@~2.1.1:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.12"
+
+formatio@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
+ dependencies:
+ samsam "~1.1"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+fullname@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fullname/-/fullname-2.1.0.tgz#c46bf0f7c3f24fd5b3358d00e4a41380eef87350"
+ dependencies:
+ npmconf "^2.1.1"
+ pify "^2.2.0"
+ pinkie-promise "^2.0.0"
+
+gauge@~1.2.5:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93"
+ dependencies:
+ ansi "^0.3.0"
+ has-unicode "^2.0.0"
+ lodash.pad "^4.1.0"
+ lodash.padend "^4.1.0"
+ lodash.padstart "^4.1.0"
+
+generator-jhipster@4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/generator-jhipster/-/generator-jhipster-4.0.8.tgz#9daff076cc628500726026fad6cfb0a8b55eb1b0"
+ dependencies:
+ chalk "1.1.3"
+ cheerio "0.22.0"
+ ejs "2.5.6"
+ glob "7.1.1"
+ html-wiring "1.2.0"
+ insight "0.8.4"
+ jhipster-core "1.2.8"
+ js-yaml "3.8.1"
+ lodash "4.17.4"
+ mkdirp "0.5.1"
+ pluralize "3.1.0"
+ randexp "0.4.4"
+ semver "5.3.0"
+ shelljs "0.7.6"
+ yeoman-generator "0.24.1"
+ yo "1.8.5"
+
+get-stdin@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ dependencies:
+ assert-plus "^1.0.0"
+
+gh-got@^2.2.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/gh-got/-/gh-got-2.4.0.tgz#aa51418911ca5e4f92437114cd1209383a4aa019"
+ dependencies:
+ got "^5.2.0"
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+
+github-username@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/github-username/-/github-username-2.1.0.tgz#200e5a104af42ba08a54096c708d4b6ec2fa256b"
+ dependencies:
+ gh-got "^2.2.0"
+ meow "^3.5.0"
+
+glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^6.0.1:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globby@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-4.1.0.tgz#080f54549ec1b82a6c60e631fc82e1211dbe95f8"
+ dependencies:
+ array-union "^1.0.1"
+ arrify "^1.0.0"
+ glob "^6.0.1"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+got@^5.0.0, got@^5.2.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35"
+ dependencies:
+ create-error-class "^3.0.1"
+ duplexer2 "^0.1.4"
+ is-redirect "^1.0.0"
+ is-retry-allowed "^1.0.0"
+ is-stream "^1.0.0"
+ lowercase-keys "^1.0.0"
+ node-status-codes "^1.0.0"
+ object-assign "^4.0.1"
+ parse-json "^2.1.0"
+ pinkie-promise "^2.0.0"
+ read-all-stream "^3.0.0"
+ readable-stream "^2.0.5"
+ timed-out "^3.0.0"
+ unzip-response "^1.0.2"
+ url-parse-lax "^1.0.0"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
+ version "4.1.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+
+grouped-queue@^0.3.0:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/grouped-queue/-/grouped-queue-0.3.3.tgz#c167d2a5319c5a0e0964ef6a25b7c2df8996c85c"
+ dependencies:
+ lodash "^4.17.2"
+
+gruntfile-editor@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/gruntfile-editor/-/gruntfile-editor-1.2.1.tgz#366fc1f93cbf045813e1448aef1da9f18289d5eb"
+ dependencies:
+ ast-query "^2.0.0"
+ lodash "^4.6.1"
+
+har-schema@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+
+har-validator@~4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
+ dependencies:
+ ajv "^4.9.1"
+ har-schema "^1.0.5"
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+
+has-unicode@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+
+hawk@~3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
+ dependencies:
+ boom "2.x.x"
+ cryptiles "2.x.x"
+ hoek "2.x.x"
+ sntp "1.x.x"
+
+hoek@2.x.x:
+ version "2.16.3"
+ resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+
+hosted-git-info@^2.1.4:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67"
+
+html-wiring@1.2.0, html-wiring@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/html-wiring/-/html-wiring-1.2.0.tgz#c5f90a776e0a27241dc6df9022c37186d0270f9e"
+ dependencies:
+ cheerio "^0.19.0"
+ detect-newline "^1.0.3"
+
+htmlparser2@^3.9.1:
+ version "3.9.2"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+ dependencies:
+ domelementtype "^1.3.0"
+ domhandler "^2.3.0"
+ domutils "^1.5.1"
+ entities "^1.1.1"
+ inherits "^2.0.1"
+ readable-stream "^2.0.2"
+
+htmlparser2@~3.8.1:
+ version "3.8.3"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068"
+ dependencies:
+ domelementtype "1"
+ domhandler "2.3"
+ domutils "1.5"
+ entities "1.0"
+ readable-stream "1.1"
+
+http-signature@~1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
+ dependencies:
+ assert-plus "^0.2.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+humanize-string@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/humanize-string/-/humanize-string-1.0.1.tgz#fce2d6c545efc25dea1f23235182c98da0180b42"
+ dependencies:
+ decamelize "^1.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+
+indent-string@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
+ dependencies:
+ repeating "^2.0.0"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
+inherits@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+
+ini@^1.2.0, ini@^1.3.4, ini@~1.3.0:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+
+inquirer@^0.10.0:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.10.1.tgz#ea25e4ce69ca145e05c99e46dcfec05e4012594a"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ ansi-regex "^2.0.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^1.0.1"
+ run-async "^0.1.0"
+ rx-lite "^3.1.2"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+inquirer@^0.11.0:
+ version "0.11.4"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.11.4.tgz#81e3374e8361beaff2d97016206d359d0b32fa4d"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ ansi-regex "^2.0.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^1.0.1"
+ run-async "^0.1.0"
+ rx-lite "^3.1.2"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+inquirer@^1.0.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^2.0.0"
+ external-editor "^1.1.0"
+ figures "^1.3.5"
+ lodash "^4.3.0"
+ mute-stream "0.0.6"
+ pinkie-promise "^2.0.0"
+ run-async "^2.2.0"
+ rx "^4.1.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+insight@0.8.4:
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/insight/-/insight-0.8.4.tgz#671caf65b47c9fe8c3d1b3206cf45bb211b75884"
+ dependencies:
+ async "^1.4.2"
+ chalk "^1.0.0"
+ configstore "^1.0.0"
+ inquirer "^0.10.0"
+ lodash.debounce "^3.0.1"
+ object-assign "^4.0.1"
+ os-name "^1.0.0"
+ request "^2.74.0"
+ tough-cookie "^2.0.0"
+ uuid "^3.0.0"
+
+insight@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/insight/-/insight-0.7.0.tgz#061f9189835bd38a97a60c2b76ea0c6b30099ff6"
+ dependencies:
+ async "^1.4.2"
+ chalk "^1.0.0"
+ configstore "^1.0.0"
+ inquirer "^0.10.0"
+ lodash.debounce "^3.0.1"
+ object-assign "^4.0.1"
+ os-name "^1.0.0"
+ request "^2.40.0"
+ tough-cookie "^2.0.0"
+
+interpret@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+
+is-builtin-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+ dependencies:
+ builtin-modules "^1.0.0"
+
+is-docker@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-1.1.0.tgz#f04374d4eee5310e9a8e113bf1495411e46176a1"
+
+is-finite@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-npm@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4"
+
+is-obj@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+
+is-promise@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
+is-redirect@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
+
+is-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
+
+is-retry-allowed@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
+
+is-root@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-root/-/is-root-1.0.0.tgz#07b6c233bc394cd9d02ba15c966bd6660d6342d5"
+
+is-stream@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+
+is-supported-regexp-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz#8b520c85fae7a253382d4b02652e045576e13bb8"
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+
+is-utf8@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+
+istextorbinary@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.1.0.tgz#dbed2a6f51be2f7475b68f89465811141b758874"
+ dependencies:
+ binaryextensions "1 || 2"
+ editions "^1.1.1"
+ textextensions "1 || 2"
+
+jhipster-core@1.2.8:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/jhipster-core/-/jhipster-core-1.2.8.tgz#2211a468761a7132c5ddc7f101400e2fc57d5be4"
+ dependencies:
+ lodash "4.17.4"
+
+jodid25519@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
+ dependencies:
+ jsbn "~0.1.0"
+
+js-yaml@3.8.1:
+ version "3.8.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628"
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^3.1.1"
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+
+json-schema@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+
+json-stable-stringify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+ dependencies:
+ jsonify "~0.0.0"
+
+json-stringify-safe@~5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
+jsprim@^1.2.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918"
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.0.2"
+ json-schema "0.2.3"
+ verror "1.3.6"
+
+latest-version@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-2.0.0.tgz#56f8d6139620847b8017f8f1f4d78e211324168b"
+ dependencies:
+ package-json "^2.0.0"
+
+levn@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ dependencies:
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+
+load-json-file@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+
+lodash._getnative@^3.0.0:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
+lodash.assignin@^4.0.9:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
+
+lodash.bind@^4.1.4:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
+
+lodash.debounce@^3.0.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-3.1.1.tgz#812211c378a94cc29d5aa4e3346cf0bfce3a7df5"
+ dependencies:
+ lodash._getnative "^3.0.0"
+
+lodash.defaults@^4.0.1:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+
+lodash.filter@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
+
+lodash.flatten@^4.2.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+
+lodash.foreach@^4.3.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
+
+lodash.map@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
+
+lodash.merge@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
+
+lodash.pad@^4.1.0:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70"
+
+lodash.padend@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e"
+
+lodash.padstart@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b"
+
+lodash.pick@^4.2.1:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
+
+lodash.reduce@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
+
+lodash.reject@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
+
+lodash.some@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
+
+lodash@4.17.4, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.3.0, lodash@^4.6.1:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
+lodash@^3.2.0, lodash@^3.3.1:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+
+log-symbols@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
+ dependencies:
+ chalk "^1.0.0"
+
+lolex@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31"
+
+loud-rejection@^1.0.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
+ dependencies:
+ currently-unhandled "^0.4.1"
+ signal-exit "^3.0.0"
+
+lowercase-keys@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
+
+lru-cache@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e"
+ dependencies:
+ pseudomap "^1.0.1"
+ yallist "^2.0.0"
+
+map-obj@^1.0.0, map-obj@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
+
+mem-fs-editor@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-2.3.0.tgz#42a0ae1f55e76fd03f09e7c7b15b6307bdf5cb13"
+ dependencies:
+ commondir "^1.0.1"
+ deep-extend "^0.4.0"
+ ejs "^2.3.1"
+ glob "^7.0.3"
+ globby "^4.0.0"
+ mkdirp "^0.5.0"
+ multimatch "^2.0.0"
+ rimraf "^2.2.8"
+ through2 "^2.0.0"
+ vinyl "^1.1.0"
+
+mem-fs@^1.1.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/mem-fs/-/mem-fs-1.1.3.tgz#b8ae8d2e3fcb6f5d3f9165c12d4551a065d989cc"
+ dependencies:
+ through2 "^2.0.0"
+ vinyl "^1.1.0"
+ vinyl-file "^2.0.0"
+
+meow@^3.0.0, meow@^3.3.0, meow@^3.5.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
+ dependencies:
+ camelcase-keys "^2.0.0"
+ decamelize "^1.1.2"
+ loud-rejection "^1.0.0"
+ map-obj "^1.0.1"
+ minimist "^1.1.3"
+ normalize-package-data "^2.3.4"
+ object-assign "^4.0.1"
+ read-pkg-up "^1.0.1"
+ redent "^1.0.0"
+ trim-newlines "^1.0.0"
+
+mime-db@~1.27.0:
+ version "1.27.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
+
+mime-types@^2.1.12, mime-types@~2.1.7:
+ version "2.1.15"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
+ dependencies:
+ mime-db "~1.27.0"
+
+"minimatch@2 || 3", minimatch@3.0.x, minimatch@^3.0.0, minimatch@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
+ dependencies:
+ brace-expansion "^1.0.0"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+
+mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ dependencies:
+ minimist "0.0.8"
+
+ms@0.7.3:
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff"
+
+multimatch@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b"
+ dependencies:
+ array-differ "^1.0.0"
+ array-union "^1.0.1"
+ arrify "^1.0.0"
+ minimatch "^3.0.0"
+
+mute-stream@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
+
+mute-stream@0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"
+
+node-status-codes@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f"
+
+nopt@^3.0.0, nopt@~3.0.1:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+ dependencies:
+ abbrev "1"
+
+normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb"
+ dependencies:
+ hosted-git-info "^2.1.4"
+ is-builtin-module "^1.0.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+npm-keyword@^4.1.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/npm-keyword/-/npm-keyword-4.2.0.tgz#98ffebfdbb1336f27ef5fe1baca0dcacd0acf6c0"
+ dependencies:
+ got "^5.0.0"
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+ registry-url "^3.0.3"
+
+npmconf@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/npmconf/-/npmconf-2.1.2.tgz#66606a4a736f1e77a059aa071a79c94ab781853a"
+ dependencies:
+ config-chain "~1.1.8"
+ inherits "~2.0.0"
+ ini "^1.2.0"
+ mkdirp "^0.5.0"
+ nopt "~3.0.1"
+ once "~1.3.0"
+ osenv "^0.1.0"
+ semver "2 || 3 || 4"
+ uid-number "0.0.5"
+
+npmlog@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-2.0.4.tgz#98b52530f2514ca90d09ec5b22c8846722375692"
+ dependencies:
+ ansi "~0.3.1"
+ are-we-there-yet "~1.1.2"
+ gauge "~1.2.5"
+
+nth-check@~1.0.0, nth-check@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
+ dependencies:
+ boolbase "~1.0.0"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+
+oauth-sign@~0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+
+object-assign@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa"
+
+object-assign@^4.0.1, object-assign@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+
+object-values@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/object-values/-/object-values-1.0.0.tgz#72af839630119e5b98c3b02bb8c27e3237158105"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ dependencies:
+ wrappy "1"
+
+once@~1.3.0:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20"
+ dependencies:
+ wrappy "1"
+
+onetime@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
+
+opn@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-3.0.3.tgz#b6d99e7399f78d65c3baaffef1fb288e9b85243a"
+ dependencies:
+ object-assign "^4.0.1"
+
+optionator@^0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+ dependencies:
+ deep-is "~0.1.3"
+ fast-levenshtein "~2.0.4"
+ levn "~0.3.0"
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+ wordwrap "~1.0.0"
+
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+
+os-name@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/os-name/-/os-name-1.0.3.tgz#1b379f64835af7c5a7f498b357cb95215c159edf"
+ dependencies:
+ osx-release "^1.0.0"
+ win-release "^1.0.0"
+
+os-shim@^0.1.2:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917"
+
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
+osenv@^0.1.0:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.0"
+
+osx-release@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/osx-release/-/osx-release-1.1.0.tgz#f217911a28136949af1bf9308b241e2737d3cd6c"
+ dependencies:
+ minimist "^1.1.0"
+
+package-json@^2.0.0, package-json@^2.1.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb"
+ dependencies:
+ got "^5.0.0"
+ registry-auth-token "^3.0.1"
+ registry-url "^3.0.3"
+ semver "^5.1.0"
+
+pad-component@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/pad-component/-/pad-component-0.0.1.tgz#ad1f22ce1bf0fdc0d6ddd908af17f351a404b8ac"
+
+parse-help@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/parse-help/-/parse-help-0.1.1.tgz#2f4df942e77a5581bba9967c0c3f48e4c66d7dda"
+ dependencies:
+ execall "^1.0.0"
+
+parse-json@^2.1.0, parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ dependencies:
+ error-ex "^1.2.0"
+
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ dependencies:
+ pinkie-promise "^2.0.0"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+path-parse@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+
+path-type@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+performance-now@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+
+pify@^2.0.0, pify@^2.2.0, pify@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+
+pinkie-promise@^2.0.0, pinkie-promise@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+
+pluralize@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-3.1.0.tgz#84213d0a12356069daa84060c559242633161368"
+
+prelude-ls@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+
+prepend-http@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
+
+pretty-bytes@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-3.0.1.tgz#27d0008d778063a0b4811bb35c79f1bd5d5fbccf"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+process-nextick-args@~1.0.6:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+
+proto-list@~1.2.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+
+pseudomap@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+
+punycode@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
+qs@~6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+
+randexp@0.4.4:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.4.tgz#ba68265f4a9f9e85f5814d34e160291f939f168e"
+ dependencies:
+ discontinuous-range "1.0.0"
+ ret "~0.1.10"
+
+rc@^1.0.1, rc@^1.1.6:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
+ dependencies:
+ deep-extend "~0.4.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+read-all-stream@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa"
+ dependencies:
+ pinkie-promise "^2.0.0"
+ readable-stream "^2.0.0"
+
+read-chunk@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194"
+
+read-pkg-up@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+ dependencies:
+ find-up "^1.0.0"
+ read-pkg "^1.0.0"
+
+read-pkg@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+ dependencies:
+ load-json-file "^1.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^1.0.0"
+
+readable-stream@1.1:
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "0.0.1"
+ string_decoder "~0.10.x"
+
+readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2:
+ version "2.2.9"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
+ dependencies:
+ buffer-shims "~1.0.0"
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "~1.0.0"
+ process-nextick-args "~1.0.6"
+ string_decoder "~1.0.0"
+ util-deprecate "~1.0.1"
+
+readline2@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35"
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ mute-stream "0.0.5"
+
+rechoir@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
+ dependencies:
+ resolve "^1.1.6"
+
+redent@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
+ dependencies:
+ indent-string "^2.1.0"
+ strip-indent "^1.0.1"
+
+registry-auth-token@^3.0.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006"
+ dependencies:
+ rc "^1.1.6"
+ safe-buffer "^5.0.1"
+
+registry-url@^3.0.3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
+ dependencies:
+ rc "^1.0.1"
+
+repeating@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
+ dependencies:
+ is-finite "^1.0.0"
+
+replace-ext@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924"
+
+request@^2.40.0, request@^2.74.0:
+ version "2.81.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
+ dependencies:
+ aws-sign2 "~0.6.0"
+ aws4 "^1.2.1"
+ caseless "~0.12.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.0"
+ forever-agent "~0.6.1"
+ form-data "~2.1.1"
+ har-validator "~4.2.1"
+ hawk "~3.1.3"
+ http-signature "~1.1.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.7"
+ oauth-sign "~0.8.1"
+ performance-now "^0.2.0"
+ qs "~6.4.0"
+ safe-buffer "^5.0.1"
+ stringstream "~0.0.4"
+ tough-cookie "~2.3.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.0.0"
+
+resolve@^1.1.6:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
+ dependencies:
+ path-parse "^1.0.5"
+
+restore-cursor@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
+ dependencies:
+ exit-hook "^1.0.0"
+ onetime "^1.0.0"
+
+ret@~0.1.10:
+ version "0.1.14"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.14.tgz#58c636837b12e161f8a380cf081c6a230fd1664e"
+
+rimraf@^2.2.0, rimraf@^2.2.8, rimraf@^2.4.4:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
+ dependencies:
+ glob "^7.0.5"
+
+root-check@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/root-check/-/root-check-1.0.0.tgz#c52a794bf0db9fad567536e41898f0c9e0a86697"
+ dependencies:
+ downgrade-root "^1.0.0"
+ sudo-block "^1.1.0"
+
+run-async@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389"
+ dependencies:
+ once "^1.3.0"
+
+run-async@^2.0.0, run-async@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+ dependencies:
+ is-promise "^2.1.0"
+
+rx-lite@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
+
+rx@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
+safe-buffer@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+
+samsam@1.1.2, samsam@~1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567"
+
+semver-diff@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
+ dependencies:
+ semver "^5.0.3"
+
+semver-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-1.0.0.tgz#92a4969065f9c70c694753d55248fc68f8f652c9"
+
+semver-truncate@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-1.1.2.tgz#57f41de69707a62709a7e0104ba2117109ea47e8"
+ dependencies:
+ semver "^5.3.0"
+
+"semver@2 || 3 || 4", semver@^4.0.3:
+ version "4.3.6"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
+
+"semver@2 || 3 || 4 || 5", semver@5.3.0, semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+
+set-immediate-shim@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+
+shelljs@0.7.6, shelljs@^0.7.0:
+ version "0.7.6"
+ resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad"
+ dependencies:
+ glob "^7.0.0"
+ interpret "^1.0.0"
+ rechoir "^0.6.2"
+
+signal-exit@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+
+sinon@^1.17.2:
+ version "1.17.7"
+ resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf"
+ dependencies:
+ formatio "1.1.1"
+ lolex "1.3.2"
+ samsam "1.1.2"
+ util ">=0.10.3 <1"
+
+slide@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
+
+sntp@1.x.x:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
+ dependencies:
+ hoek "2.x.x"
+
+sort-on@^1.0.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/sort-on/-/sort-on-1.3.0.tgz#0dfd5b364b23df7f2acd86985daeb889e1a7c840"
+ dependencies:
+ arrify "^1.0.0"
+ dot-prop "^2.0.0"
+
+source-map@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
+ dependencies:
+ amdefine ">=0.0.4"
+
+spawn-sync@^1.0.15:
+ version "1.0.15"
+ resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
+ dependencies:
+ concat-stream "^1.4.7"
+ os-shim "^0.1.2"
+
+spdx-correct@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+ dependencies:
+ spdx-license-ids "^1.0.2"
+
+spdx-expression-parse@~1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
+
+spdx-license-ids@^1.0.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
+
+sprintf-js@^1.0.3, sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+
+sshpk@^1.7.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ dashdash "^1.12.0"
+ getpass "^0.1.1"
+ optionalDependencies:
+ bcrypt-pbkdf "^1.0.0"
+ ecc-jsbn "~0.1.1"
+ jodid25519 "^1.0.0"
+ jsbn "~0.1.0"
+ tweetnacl "~0.14.0"
+
+string-length@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
+ dependencies:
+ strip-ansi "^3.0.0"
+
+string-template@~0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
+
+string-width@^1.0.0, string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+string_decoder@~0.10.x:
+ version "0.10.31"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
+
+string_decoder@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667"
+ dependencies:
+ buffer-shims "~1.0.0"
+
+stringstream@~0.0.4:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-bom-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca"
+ dependencies:
+ first-chunk-stream "^2.0.0"
+ strip-bom "^2.0.0"
+
+strip-bom@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+ dependencies:
+ is-utf8 "^0.2.0"
+
+strip-indent@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
+ dependencies:
+ get-stdin "^4.0.1"
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
+sudo-block@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/sudo-block/-/sudo-block-1.2.0.tgz#cc539bf8191624d4f507d83eeb45b4cea27f3463"
+ dependencies:
+ chalk "^1.0.0"
+ is-docker "^1.0.0"
+ is-root "^1.0.0"
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^3.1.2:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+ dependencies:
+ has-flag "^1.0.0"
+
+tabtab@^1.3.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/tabtab/-/tabtab-1.3.2.tgz#bb9c2ca6324f659fde7634c2caf3c096e1187ca7"
+ dependencies:
+ debug "^2.2.0"
+ inquirer "^1.0.2"
+ minimist "^1.2.0"
+ mkdirp "^0.5.1"
+ npmlog "^2.0.3"
+ object-assign "^4.1.0"
+
+taketalk@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/taketalk/-/taketalk-1.0.0.tgz#b4d4f0deed206ae7df775b129ea2ca6de52f26dd"
+ dependencies:
+ get-stdin "^4.0.1"
+ minimist "^1.1.0"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+
+"textextensions@1 || 2":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.1.0.tgz#1be0dc2a0dc244d44be8a09af6a85afb93c4dbc3"
+
+through2@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
+ dependencies:
+ readable-stream "^2.1.5"
+ xtend "~4.0.1"
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+
+timed-out@^3.0.0:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217"
+
+titleize@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/titleize/-/titleize-1.0.0.tgz#7d350722061830ba6617631e0cfd3ea08398d95a"
+
+tmp@^0.0.29:
+ version "0.0.29"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0"
+ dependencies:
+ os-tmpdir "~1.0.1"
+
+tough-cookie@^2.0.0, tough-cookie@~2.3.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
+ dependencies:
+ punycode "^1.4.1"
+
+traverse@^0.6.6:
+ version "0.6.6"
+ resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
+
+trim-newlines@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+
+twig@^0.8.2:
+ version "0.8.9"
+ resolved "https://registry.yarnpkg.com/twig/-/twig-0.8.9.tgz#b1594f002b684e5f029de3e54e87bec4f084b6c2"
+ dependencies:
+ minimatch "3.0.x"
+ walk "2.3.x"
+
+type-check@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ dependencies:
+ prelude-ls "~1.1.2"
+
+typedarray@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+
+uid-number@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.5.tgz#5a3db23ef5dbd55b81fce0ec9a2ac6fccdebb81e"
+
+underscore.string@^3.0.3:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db"
+ dependencies:
+ sprintf-js "^1.0.3"
+ util-deprecate "^1.0.2"
+
+untildify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0"
+ dependencies:
+ os-homedir "^1.0.0"
+
+unzip-response@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe"
+
+update-notifier@^0.6.0:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.6.3.tgz#776dec8daa13e962a341e8a1d98354306b67ae08"
+ dependencies:
+ boxen "^0.3.1"
+ chalk "^1.0.0"
+ configstore "^2.0.0"
+ is-npm "^1.0.0"
+ latest-version "^2.0.0"
+ semver-diff "^2.0.0"
+
+url-parse-lax@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
+ dependencies:
+ prepend-http "^1.0.1"
+
+user-home@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
+ dependencies:
+ os-homedir "^1.0.0"
+
+util-deprecate@^1.0.2, util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
+"util@>=0.10.3 <1":
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+ dependencies:
+ inherits "2.0.1"
+
+uuid@^2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
+
+uuid@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
+ dependencies:
+ spdx-correct "~1.0.0"
+ spdx-expression-parse "~1.0.0"
+
+verror@1.3.6:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
+ dependencies:
+ extsprintf "1.0.2"
+
+vinyl-file@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a"
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.3.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+ strip-bom-stream "^2.0.0"
+ vinyl "^1.1.0"
+
+vinyl@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884"
+ dependencies:
+ clone "^1.0.0"
+ clone-stats "^0.0.1"
+ replace-ext "0.0.1"
+
+walk@2.3.x:
+ version "2.3.9"
+ resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.9.tgz#31b4db6678f2ae01c39ea9fb8725a9031e558a7b"
+ dependencies:
+ foreachasync "^3.0.0"
+
+which@^1.2.9:
+ version "1.2.14"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5"
+ dependencies:
+ isexe "^2.0.0"
+
+widest-line@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c"
+ dependencies:
+ string-width "^1.0.1"
+
+win-release@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/win-release/-/win-release-1.1.1.tgz#5fa55e02be7ca934edfc12665632e849b72e5209"
+ dependencies:
+ semver "^5.0.1"
+
+wordwrap@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+
+write-file-atomic@^1.1.2:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f"
+ dependencies:
+ graceful-fs "^4.1.11"
+ imurmurhash "^0.1.4"
+ slide "^1.1.5"
+
+xdg-basedir@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2"
+ dependencies:
+ os-homedir "^1.0.0"
+
+xtend@~4.0.0, xtend@~4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+
+yallist@^2.0.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+
+yeoman-assert@^2.0.0:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/yeoman-assert/-/yeoman-assert-2.2.3.tgz#a5682a83632c50ac0ee84173a5a10fd6f3206474"
+
+yeoman-character@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/yeoman-character/-/yeoman-character-1.1.0.tgz#90d4b5beaf92759086177015b2fdfa2e0684d7c7"
+ dependencies:
+ supports-color "^3.1.2"
+
+yeoman-doctor@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/yeoman-doctor/-/yeoman-doctor-2.1.0.tgz#94ab784896a64f53a9fac452d5e9133e2750a236"
+ dependencies:
+ bin-version-check "^2.1.0"
+ chalk "^1.0.0"
+ each-async "^1.1.1"
+ log-symbols "^1.0.1"
+ object-values "^1.0.0"
+ semver "^5.0.3"
+ twig "^0.8.2"
+ user-home "^2.0.0"
+
+yeoman-environment@^1.1.0, yeoman-environment@^1.5.2, yeoman-environment@^1.6.1:
+ version "1.6.6"
+ resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-1.6.6.tgz#cd85fa67d156060e440d7807d7ef7cf0d2d1d671"
+ dependencies:
+ chalk "^1.0.0"
+ debug "^2.0.0"
+ diff "^2.1.2"
+ escape-string-regexp "^1.0.2"
+ globby "^4.0.0"
+ grouped-queue "^0.3.0"
+ inquirer "^1.0.2"
+ lodash "^4.11.1"
+ log-symbols "^1.0.1"
+ mem-fs "^1.1.0"
+ text-table "^0.2.0"
+ untildify "^2.0.0"
+
+yeoman-generator@0.24.1, yeoman-generator@^0.24.1:
+ version "0.24.1"
+ resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-0.24.1.tgz#1ca74429d9c5c95db0b22859ec180a2599bc1f8e"
+ dependencies:
+ async "^2.0.0"
+ chalk "^1.0.0"
+ class-extend "^0.1.0"
+ cli-table "^0.3.1"
+ cross-spawn "^4.0.0"
+ dargs "^4.0.0"
+ dateformat "^1.0.11"
+ debug "^2.1.0"
+ detect-conflict "^1.0.0"
+ error "^7.0.2"
+ find-up "^1.0.0"
+ github-username "^2.0.0"
+ glob "^7.0.3"
+ gruntfile-editor "^1.0.0"
+ html-wiring "^1.0.0"
+ istextorbinary "^2.1.0"
+ lodash "^4.11.1"
+ mem-fs-editor "^2.0.0"
+ mkdirp "^0.5.0"
+ nopt "^3.0.0"
+ path-exists "^2.0.0"
+ path-is-absolute "^1.0.0"
+ pretty-bytes "^3.0.1"
+ read-chunk "^1.0.0"
+ read-pkg-up "^1.0.1"
+ rimraf "^2.2.0"
+ run-async "^2.0.0"
+ shelljs "^0.7.0"
+ text-table "^0.2.0"
+ through2 "^2.0.0"
+ underscore.string "^3.0.3"
+ user-home "^2.0.0"
+ yeoman-assert "^2.0.0"
+ yeoman-environment "^1.1.0"
+ yeoman-test "^1.0.0"
+ yeoman-welcome "^1.0.0"
+
+yeoman-test@^1.0.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/yeoman-test/-/yeoman-test-1.6.0.tgz#abff291733e16e8923d9eefc8691c632888bf948"
+ dependencies:
+ inquirer "^1.0.2"
+ lodash "^4.3.0"
+ mkdirp "^0.5.1"
+ pinkie-promise "^2.0.1"
+ rimraf "^2.4.4"
+ sinon "^1.17.2"
+ yeoman-environment "^1.5.2"
+ yeoman-generator "^0.24.1"
+
+yeoman-welcome@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/yeoman-welcome/-/yeoman-welcome-1.0.1.tgz#f6cf198fd4fba8a771672c26cdfb8a64795c84ec"
+ dependencies:
+ chalk "^1.0.0"
+
+yo@1.8.5:
+ version "1.8.5"
+ resolved "https://registry.yarnpkg.com/yo/-/yo-1.8.5.tgz#776ab9ec79a7882f8d4f7a9e10214fdab050d928"
+ dependencies:
+ async "^1.0.0"
+ chalk "^1.0.0"
+ cli-list "^0.1.1"
+ configstore "^1.0.0"
+ cross-spawn "^3.0.1"
+ figures "^1.3.5"
+ fullname "^2.0.0"
+ got "^5.0.0"
+ humanize-string "^1.0.0"
+ inquirer "^0.11.0"
+ insight "^0.7.0"
+ lodash "^3.2.0"
+ meow "^3.0.0"
+ npm-keyword "^4.1.0"
+ opn "^3.0.2"
+ package-json "^2.1.0"
+ parse-help "^0.1.1"
+ read-pkg-up "^1.0.1"
+ repeating "^2.0.0"
+ root-check "^1.0.0"
+ sort-on "^1.0.0"
+ string-length "^1.0.0"
+ tabtab "^1.3.0"
+ titleize "^1.0.0"
+ update-notifier "^0.6.0"
+ user-home "^2.0.0"
+ yeoman-character "^1.0.0"
+ yeoman-doctor "^2.0.0"
+ yeoman-environment "^1.6.1"
+ yosay "^1.0.0"
+
+yosay@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/yosay/-/yosay-1.2.1.tgz#9466ef969830e85b474e267b50f7688693ed3b5b"
+ dependencies:
+ ansi-regex "^2.0.0"
+ ansi-styles "^2.0.0"
+ chalk "^1.0.0"
+ cli-boxes "^1.0.0"
+ pad-component "0.0.1"
+ repeating "^2.0.0"
+ string-width "^1.0.0"
+ strip-ansi "^3.0.0"
+ taketalk "^1.0.0"
+ wrap-ansi "^2.0.0"
diff --git a/jhipster/jhipster-microservice/dealer-app/.editorconfig b/jhipster/jhipster-microservice/dealer-app/.editorconfig
new file mode 100644
index 0000000000..a03599dd04
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/.editorconfig
@@ -0,0 +1,24 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 4
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[{package,bower}.json]
+indent_style = space
+indent_size = 2
diff --git a/jhipster/jhipster-microservice/dealer-app/.gitattributes b/jhipster/jhipster-microservice/dealer-app/.gitattributes
new file mode 100644
index 0000000000..2a13efa88f
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/.gitattributes
@@ -0,0 +1,22 @@
+# All text files should have the "lf" (Unix) line endings
+* text eol=lf
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.java text
+*.js text
+*.css text
+*.html text
+
+# Denote all files that are truly binary and should not be modified.
+*.png binary
+*.jpg binary
+*.jar binary
+*.pdf binary
+*.eot binary
+*.ttf binary
+*.gzip binary
+*.gz binary
+*.ai binary
+*.eps binary
+*.swf binary
diff --git a/jhipster/jhipster-microservice/dealer-app/.gitignore b/jhipster/jhipster-microservice/dealer-app/.gitignore
new file mode 100644
index 0000000000..74b29e2042
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/.gitignore
@@ -0,0 +1,141 @@
+######################
+# Project Specific
+######################
+/target/www/**
+/src/test/javascript/coverage/
+/src/test/javascript/PhantomJS*/
+
+######################
+# Node
+######################
+/node/
+node_tmp/
+node_modules/
+npm-debug.log.*
+
+######################
+# SASS
+######################
+.sass-cache/
+
+######################
+# Eclipse
+######################
+*.pydevproject
+.project
+.metadata
+tmp/
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+.factorypath
+/src/main/resources/rebel.xml
+
+# External tool builders
+.externalToolBuilders/**
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+######################
+# Intellij
+######################
+.idea/
+*.iml
+*.iws
+*.ipr
+*.ids
+*.orig
+
+######################
+# Visual Studio Code
+######################
+.vscode/
+
+######################
+# Maven
+######################
+/log/
+/target/
+
+######################
+# Gradle
+######################
+.gradle/
+/build/
+
+######################
+# Package Files
+######################
+*.jar
+*.war
+*.ear
+*.db
+
+######################
+# Windows
+######################
+# Windows image file caches
+Thumbs.db
+
+# Folder config file
+Desktop.ini
+
+######################
+# Mac OSX
+######################
+.DS_Store
+.svn
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+######################
+# Directories
+######################
+/bin/
+/deploy/
+
+######################
+# Logs
+######################
+*.log
+
+######################
+# Others
+######################
+*.class
+*.*~
+*~
+.merge_file*
+
+######################
+# Gradle Wrapper
+######################
+!gradle/wrapper/gradle-wrapper.jar
+
+######################
+# Maven Wrapper
+######################
+!.mvn/wrapper/maven-wrapper.jar
+
+######################
+# ESLint
+######################
+.eslintcache
diff --git a/jhipster/jhipster-microservice/dealer-app/.jhipster/Dealer.json b/jhipster/jhipster-microservice/dealer-app/.jhipster/Dealer.json
new file mode 100644
index 0000000000..e9b4a8112e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/.jhipster/Dealer.json
@@ -0,0 +1,21 @@
+{
+ "fluentMethods": true,
+ "relationships": [],
+ "fields": [
+ {
+ "fieldName": "name",
+ "fieldType": "String"
+ },
+ {
+ "fieldName": "address",
+ "fieldType": "String"
+ }
+ ],
+ "changelogDate": "20170503044952",
+ "dto": "no",
+ "service": "no",
+ "entityTableName": "dealer",
+ "pagination": "infinite-scroll",
+ "microserviceName": "dealerapp",
+ "searchEngine": false
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/.mvn/wrapper/maven-wrapper.jar b/jhipster/jhipster-microservice/dealer-app/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000..5fd4d5023f
Binary files /dev/null and b/jhipster/jhipster-microservice/dealer-app/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/jhipster/jhipster-microservice/dealer-app/.mvn/wrapper/maven-wrapper.properties b/jhipster/jhipster-microservice/dealer-app/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..c954cec91c
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
diff --git a/jhipster/jhipster-microservice/dealer-app/.yo-rc.json b/jhipster/jhipster-microservice/dealer-app/.yo-rc.json
new file mode 100644
index 0000000000..aa9167a92e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/.yo-rc.json
@@ -0,0 +1,29 @@
+{
+ "generator-jhipster": {
+ "jhipsterVersion": "4.0.8",
+ "baseName": "dealerapp",
+ "packageName": "com.dealer.app",
+ "packageFolder": "com/dealer/app",
+ "serverPort": "8082",
+ "authenticationType": "jwt",
+ "hibernateCache": "hazelcast",
+ "clusteredHttpSession": false,
+ "websocket": false,
+ "databaseType": "sql",
+ "devDatabaseType": "h2Disk",
+ "prodDatabaseType": "mysql",
+ "searchEngine": false,
+ "messageBroker": false,
+ "serviceDiscoveryType": "eureka",
+ "buildTool": "maven",
+ "enableSocialSignIn": false,
+ "jwtSecretKey": "d4c73e937677223a85c7fcebae7a6ce0c48c3b01",
+ "enableTranslation": false,
+ "applicationType": "microservice",
+ "testFrameworks": [],
+ "jhiPrefix": "jhi",
+ "skipClient": true,
+ "skipUserManagement": true,
+ "clientPackageManager": "yarn"
+ }
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/dealer-app/README.md b/jhipster/jhipster-microservice/dealer-app/README.md
new file mode 100644
index 0000000000..f0fb5fb820
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/README.md
@@ -0,0 +1,75 @@
+# dealerapp
+This application was generated using JHipster 4.0.8, you can find documentation and help at [https://jhipster.github.io/documentation-archive/v4.0.8](https://jhipster.github.io/documentation-archive/v4.0.8).
+
+This is a "microservice" application intended to be part of a microservice architecture, please refer to the [Doing microservices with JHipster][] page of the documentation for more information.
+
+This application is configured for Service Discovery and Configuration with the JHipster-Registry. On launch, it will refuse to start if it is not able to connect to the JHipster-Registry at [http://localhost:8761](http://localhost:8761). For more information, read our documentation on [Service Discovery and Configuration with the JHipster-Registry][].
+
+## Development
+
+To start your application in the dev profile, simply run:
+
+ ./mvnw
+
+
+For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][].
+
+
+## Building for production
+
+To optimize the dealerapp application for production, run:
+
+ ./mvnw -Pprod clean package
+
+To ensure everything worked, run:
+
+ java -jar target/*.war
+
+
+Refer to [Using JHipster in production][] for more details.
+
+## Testing
+
+To launch your application's tests, run:
+
+ ./mvnw clean test
+
+For more information, refer to the [Running tests page][].
+
+## Using Docker to simplify development (optional)
+
+You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services.
+For example, to start a mysql database in a docker container, run:
+
+ docker-compose -f src/main/docker/mysql.yml up -d
+
+To stop it and remove the container, run:
+
+ docker-compose -f src/main/docker/mysql.yml down
+
+You can also fully dockerize your application and all the services that it depends on.
+To achieve this, first build a docker image of your app by running:
+
+ ./mvnw package -Pprod docker:build
+
+Then run:
+
+ docker-compose -f src/main/docker/app.yml up -d
+
+For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`yo jhipster:docker-compose`), which is able to generate docker configurations for one or several JHipster applications.
+
+## Continuous Integration (optional)
+
+To configure CI for your project, run the ci-cd sub-generator (`yo jhipster:ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information.
+
+[JHipster Homepage and latest documentation]: https://jhipster.github.io
+[JHipster 4.0.8 archive]: https://jhipster.github.io/documentation-archive/v4.0.8
+[Doing microservices with JHipster]: https://jhipster.github.io/documentation-archive/v4.0.8/microservices-architecture/
+[Using JHipster in development]: https://jhipster.github.io/documentation-archive/v4.0.8/development/
+[Service Discovery and Configuration with the JHipster-Registry]: https://jhipster.github.io/documentation-archive/v4.0.8/microservices-architecture/#jhipster-registry
+[Using Docker and Docker-Compose]: https://jhipster.github.io/documentation-archive/v4.0.8/docker-compose
+[Using JHipster in production]: https://jhipster.github.io/documentation-archive/v4.0.8/production/
+[Running tests page]: https://jhipster.github.io/documentation-archive/v4.0.8/running-tests/
+[Setting up Continuous Integration]: https://jhipster.github.io/documentation-archive/v4.0.8/setting-up-ci/
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/mvnw b/jhipster/jhipster-microservice/dealer-app/mvnw
new file mode 100755
index 0000000000..a1ba1bf554
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/mvnw
@@ -0,0 +1,233 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ #
+ # Look for the Apple JDKs first to preserve the existing behaviour, and then look
+ # for the new JDKs provided by Oracle.
+ #
+ if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
+ #
+ # Oracle JDKs
+ #
+ export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=`/usr/libexec/java_home`
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Migwn, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ local basedir=$(pwd)
+ local wdir=$(pwd)
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ wdir=$(cd "$wdir/.."; pwd)
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} "$@"
diff --git a/jhipster/jhipster-microservice/dealer-app/mvnw.cmd b/jhipster/jhipster-microservice/dealer-app/mvnw.cmd
new file mode 100644
index 0000000000..2b934e89dd
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/mvnw.cmd
@@ -0,0 +1,145 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+set MAVEN_CMD_LINE_ARGS=%*
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/dealer-app/package.json b/jhipster/jhipster-microservice/dealer-app/package.json
new file mode 100644
index 0000000000..ac56dd89a7
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/package.json
@@ -0,0 +1,5 @@
+{
+ "devDependencies": {
+ "generator-jhipster": "4.0.8"
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/pom.xml b/jhipster/jhipster-microservice/dealer-app/pom.xml
new file mode 100644
index 0000000000..199d383e39
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/pom.xml
@@ -0,0 +1,934 @@
+
+
+ 4.0.0
+
+
+ spring-boot-starter-parent
+ org.springframework.boot
+ 1.5.2.RELEASE
+
+
+
+ com.dealer.app
+ dealerapp
+ 0.0.1-SNAPSHOT
+ war
+ Dealerapp
+
+
+ ${maven.version}
+
+
+
+ -Djava.security.egd=file:/dev/./urandom -Xmx256m
+ 3.6.2
+ 2.0.0
+ 2.5
+ 3.5
+ 0.4.13
+ 1.2
+ 5.2.8.Final
+ 2.6.0
+ 0.7.9
+ 1.8
+ 3.21.0-GA
+ 1.0.0
+ 1.1.0
+ 0.7.0
+ 3.6
+ 2.0.0
+ 4.8
+ jdt_apt
+ 1.1.0.Final
+ 3.6.0
+ 1.4.1
+ 3.0.1
+ yyyyMMddHHmmss
+ ${java.version}
+ ${java.version}
+ 3.0.0
+ 3.1.3
+ v6.10.0
+
+
+
+ 0.0.20
+
+ ${project.build.directory}/test-results
+ false
+ 3.2.2
+ 2.12.1
+ 3.2
+
+ src/main/webapp/content/**/*.*, src/main/webapp/bower_components/**/*.*, src/main/webapp/i18n/*.js, target/www/**/*.*
+
+ S3437,UndocumentedApi,BoldAndItalicTagsCheck
+
+
+ src/main/webapp/app/**/*.*
+ Web:BoldAndItalicTagsCheck
+
+ src/main/java/**/*
+ squid:S3437
+
+ src/main/java/**/*
+ squid:UndocumentedApi
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco-it.exec
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+ jacoco
+
+ ${project.testresult.directory}/karma
+
+ ${project.testresult.directory}/coverage/report-lcov/lcov.info
+
+ ${project.testresult.directory}/coverage/report-lcov/lcov.info
+
+ ${project.basedir}/src/main/
+ ${project.testresult.directory}/surefire-reports
+ ${project.basedir}/src/test/
+
+ 2.5.0
+
+ Camden.SR5
+ 2.6.1
+ 1.4.10.Final
+ 1.1.0.Final
+ v0.21.3
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+ io.github.jhipster
+ jhipster
+ ${jhipster.server.version}
+
+
+ io.dropwizard.metrics
+ metrics-core
+
+
+ io.dropwizard.metrics
+ metrics-annotation
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-json
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-jvm
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-servlet
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-servlets
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-hibernate5
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-hppc
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-json-org
+
+
+ com.h2database
+ h2
+
+
+ com.hazelcast
+ hazelcast
+
+
+ com.hazelcast
+ hazelcast-hibernate52
+ ${hazelcast-hibernate52.version}
+
+
+ com.hazelcast
+ hazelcast-spring
+
+
+ org.awaitility
+ awaitility
+ ${awaitility.version}
+ test
+
+
+ com.jayway.jsonpath
+ json-path
+ test
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${springfox.version}
+
+
+ org.mapstruct
+ mapstruct
+
+
+
+
+ io.springfox
+ springfox-bean-validators
+ ${springfox.version}
+
+
+ com.mattbertolini
+ liquibase-slf4j
+ ${liquibase-slf4j.version}
+
+
+ com.ryantenney.metrics
+ metrics-spring
+ ${metrics-spring.version}
+
+
+ com.codahale.metrics
+ metrics-annotation
+
+
+ com.codahale.metrics
+ metrics-core
+
+
+ com.codahale.metrics
+ metrics-healthchecks
+
+
+
+
+ com.zaxxer
+ HikariCP
+
+
+ tools
+ com.sun
+
+
+
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang.version}
+
+
+ javax.cache
+ cache-api
+
+
+ mysql
+ mysql-connector-java
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.hibernate
+ hibernate-envers
+
+
+ org.hibernate
+ hibernate-validator
+
+
+ org.liquibase
+ liquibase-core
+
+
+ jetty-servlet
+ org.eclipse.jetty
+
+
+
+
+ org.mapstruct
+ mapstruct-jdk8
+ ${mapstruct.version}
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework.boot
+ spring-boot-actuator
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-loader-tools
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+
+
+ io.jsonwebtoken
+ jjwt
+ ${jjwt.version}
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter
+
+
+ org.springframework.cloud
+ spring-cloud-starter-ribbon
+
+
+
+ io.netty
+ netty-transport-native-epoll
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-hystrix
+
+
+ org.springframework.cloud
+ spring-cloud-starter-spectator
+
+
+ org.springframework.retry
+ spring-retry
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
+
+ org.springframework.cloud
+ spring-cloud-starter-feign
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ ${logstash-logback-encoder.version}
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ ch.qos.logback
+ logback-access
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-cloud-connectors
+
+
+
+ org.springframework.security
+ spring-security-data
+
+
+
+
+ spring-boot:run
+
+
+ com.github.ekryd.sortpom
+ sortpom-maven-plugin
+ ${sortpom-maven-plugin.version}
+
+
+ verify
+
+ sort
+
+
+
+
+ true
+ 4
+ groupId,artifactId
+ groupId,artifactId
+ true
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-eclipse-plugin
+
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ ${maven-enforcer-plugin.version}
+
+
+ enforce-versions
+
+ enforce
+
+
+
+
+
+
+ You are running an older version of Maven. JHipster requires at least Maven ${maven.version}
+ [${maven.version},)
+
+
+ You are running an older version of Java. JHipster requires at least JDK ${java.version}
+ [${java.version}.0,)
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven-resources-plugin.version}
+
+
+ default-resources
+ validate
+
+ copy-resources
+
+
+ target/classes
+ false
+
+ #
+
+
+
+ src/main/resources/
+ true
+
+ **/*.xml
+ **/*.yml
+
+
+
+ src/main/resources/
+ false
+
+ **/*.xml
+ **/*.yml
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ alphabetical
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-maven-plugin.version}
+
+
+ pre-unit-tests
+
+ prepare-agent
+
+
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+
+
+
+
+ post-unit-test
+ test
+
+ report
+
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+ ${project.testresult.directory}/coverage/jacoco
+
+
+
+
+
+ org.sonarsource.scanner.maven
+ sonar-maven-plugin
+ ${sonar-maven-plugin.version}
+
+
+ org.liquibase
+ liquibase-maven-plugin
+ ${liquibase.version}
+
+ src/main/resources/config/liquibase/master.xml
+ src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml
+ org.h2.Driver
+ jdbc:h2:file:./target/h2db/db/dealerapp
+
+ dealerapp
+
+ hibernate:spring:com.dealer.app.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ true
+ debug
+
+
+
+ org.javassist
+ javassist
+ ${javassist.version}
+
+
+ org.liquibase.ext
+ liquibase-hibernate5
+ ${liquibase-hibernate5.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ ${project.parent.version}
+
+
+ javax.validation
+ validation-api
+ ${validation-api.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ true
+
+
+
+
+ com.spotify
+ docker-maven-plugin
+ ${docker-maven-plugin.version}
+
+ dealerapp
+ src/main/docker
+
+
+ /
+ ${project.build.directory}
+ ${project.build.finalName}.war
+
+
+
+
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+ org.jacoco
+
+ jacoco-maven-plugin
+
+
+ ${jacoco-maven-plugin.version}
+
+
+ prepare-agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ no-liquibase
+
+ ,no-liquibase
+
+
+
+ swagger
+
+ ,swagger
+
+
+
+ dev
+
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+
+
+
+
+
+ DEBUG
+
+ dev${profile.no-liquibase}
+
+
+
+ prod
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+
+
+
+ maven-clean-plugin
+
+
+
+ target/www/
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ build-info
+
+
+
+
+
+
+
+
+ INFO
+
+ prod${profile.swagger}${profile.no-liquibase}
+
+
+
+
+ cc
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+ src/main/webapp/
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ true
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+ none
+
+
+ default-testCompile
+ none
+
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+ ${scala-maven-plugin.version}
+
+
+ compile
+ compile
+
+ add-source
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ add-source
+ testCompile
+
+
+
+
+ incremental
+ true
+ ${scala.version}
+
+
+
+
+
+
+ DEBUG
+
+ dev,swagger
+
+
+
+
+ graphite
+
+
+ io.dropwizard.metrics
+ metrics-graphite
+
+
+
+
+
+ prometheus
+
+
+ io.prometheus
+ simpleclient
+ ${prometheus-simpleclient.version}
+
+
+ io.prometheus
+ simpleclient_servlet
+ ${prometheus-simpleclient.version}
+
+
+ io.prometheus
+ simpleclient_dropwizard
+ ${prometheus-simpleclient.version}
+
+
+
+
+
+ IDE
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/Dockerfile b/jhipster/jhipster-microservice/dealer-app/src/main/docker/Dockerfile
new file mode 100644
index 0000000000..cdef3a17e4
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/Dockerfile
@@ -0,0 +1,13 @@
+FROM openjdk:8-jre-alpine
+
+ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
+ JHIPSTER_SLEEP=0
+
+# add directly the war
+ADD *.war /app.war
+
+VOLUME /tmp
+EXPOSE 8082 5701/udp
+CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \
+ sleep ${JHIPSTER_SLEEP} && \
+ java -Djava.security.egd=file:/dev/./urandom -jar /app.war
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/app.yml b/jhipster/jhipster-microservice/dealer-app/src/main/docker/app.yml
new file mode 100644
index 0000000000..85a816052e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/app.yml
@@ -0,0 +1,19 @@
+version: '2'
+services:
+ dealerapp-app:
+ image: dealerapp
+ environment:
+ - SPRING_PROFILES_ACTIVE=prod,swagger
+ - SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/config
+ - SPRING_DATASOURCE_URL=jdbc:mysql://dealerapp-mysql:3306/dealerapp?useUnicode=true&characterEncoding=utf8&useSSL=false
+ - JHIPSTER_SLEEP=10 # gives time for the database to boot before the application
+ dealerapp-mysql:
+ extends:
+ file: mysql.yml
+ service: dealerapp-mysql
+ jhipster-registry:
+ extends:
+ file: jhipster-registry.yml
+ service: jhipster-registry
+ environment:
+ - SPRING_CLOUD_CONFIG_SERVER_NATIVE_SEARCH_LOCATIONS=file:./central-config/docker-config/
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/README.md b/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/README.md
new file mode 100644
index 0000000000..022a152863
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/README.md
@@ -0,0 +1,7 @@
+# Central configuration sources details
+
+The JHipster-Registry will use the following directories as its configuration source :
+- localhost-config : when running the registry in docker with the jhipster-registry.yml docker-compose file
+- docker-config : when running the registry and the app both in docker with the app.yml docker-compose file
+
+For more info, refer to http://jhipster.github.io/microservices-architecture/#registry_app_configuration
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/docker-config/application.yml b/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/docker-config/application.yml
new file mode 100644
index 0000000000..f11d367241
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/docker-config/application.yml
@@ -0,0 +1,15 @@
+# Common configuration shared between all applications
+configserver:
+ name: Docker JHipster Registry
+ status: Connected to the JHipster Registry running in Docker
+
+jhipster:
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@jhipster-registry:8761/eureka/
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/localhost-config/application.yml b/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/localhost-config/application.yml
new file mode 100644
index 0000000000..052a6d0535
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/central-server-config/localhost-config/application.yml
@@ -0,0 +1,15 @@
+# Common configuration shared between all applications
+configserver:
+ name: Docker JHipster Registry
+ status: Connected to the JHipster Registry running in Docker
+
+jhipster:
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/jhipster-registry.yml b/jhipster/jhipster-microservice/dealer-app/src/main/docker/jhipster-registry.yml
new file mode 100644
index 0000000000..58feb685d4
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/jhipster-registry.yml
@@ -0,0 +1,18 @@
+version: '2'
+services:
+ jhipster-registry:
+ image: jhipster/jhipster-registry:v2.5.8
+ volumes:
+ - ./central-server-config:/central-config
+ # When run with the "dev" Spring profile, the JHipster Registry will
+ # read the config from the local filesystem (central-server-config directory)
+ # When run with the "prod" Spring profile, it will read the config from a git repository
+ # See http://jhipster.github.io/microservices-architecture/#registry_app_configuration
+ environment:
+ - SPRING_PROFILES_ACTIVE=dev
+ - SECURITY_USER_PASSWORD=admin
+ - SPRING_CLOUD_CONFIG_SERVER_NATIVE_SEARCH_LOCATIONS=file:./central-config/localhost-config/
+ # - GIT_URI=https://github.com/jhipster/jhipster-registry/
+ # - GIT_SEARCH_PATHS=central-config
+ ports:
+ - 8761:8761
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/mysql.yml b/jhipster/jhipster-microservice/dealer-app/src/main/docker/mysql.yml
new file mode 100644
index 0000000000..866f39f947
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/mysql.yml
@@ -0,0 +1,13 @@
+version: '2'
+services:
+ dealerapp-mysql:
+ image: mysql:5.7.13
+ # volumes:
+ # - ~/volumes/jhipster/dealerapp/mysql/:/var/lib/mysql/
+ environment:
+ - MYSQL_USER=root
+ - MYSQL_ALLOW_EMPTY_PASSWORD=yes
+ - MYSQL_DATABASE=dealerapp
+ ports:
+ - 3306:3306
+ command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/docker/sonar.yml b/jhipster/jhipster-microservice/dealer-app/src/main/docker/sonar.yml
new file mode 100644
index 0000000000..0ed7462f28
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/docker/sonar.yml
@@ -0,0 +1,7 @@
+version: '2'
+services:
+ dealerapp-sonar:
+ image: sonarqube:6.2-alpine
+ ports:
+ - 9000:9000
+ - 9092:9092
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/ApplicationWebXml.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/ApplicationWebXml.java
new file mode 100644
index 0000000000..2321d72c70
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/ApplicationWebXml.java
@@ -0,0 +1,21 @@
+package com.dealer.app;
+
+import com.dealer.app.config.DefaultProfileUtil;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.support.SpringBootServletInitializer;
+
+/**
+ * This is a helper Java class that provides an alternative to creating a web.xml.
+ * This will be invoked only when the application is deployed to a servlet container like Tomcat, JBoss etc.
+ */
+public class ApplicationWebXml extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ /**
+ * set a default to use when no profile is configured.
+ */
+ DefaultProfileUtil.addDefaultProfile(application.application());
+ return application.sources(DealerappApp.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/DealerappApp.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/DealerappApp.java
new file mode 100644
index 0000000000..72075d8bfe
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/DealerappApp.java
@@ -0,0 +1,91 @@
+package com.dealer.app;
+
+import com.dealer.app.config.ApplicationProperties;
+import com.dealer.app.config.DefaultProfileUtil;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.actuate.autoconfigure.*;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.env.Environment;
+
+import javax.annotation.PostConstruct;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collection;
+
+@ComponentScan
+@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
+@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
+@EnableDiscoveryClient
+public class DealerappApp {
+
+ private static final Logger log = LoggerFactory.getLogger(DealerappApp.class);
+
+ private final Environment env;
+
+ public DealerappApp(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Initializes dealerapp.
+ *
+ * Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
+ *
+ * You can find more information on how profiles work with JHipster on http://jhipster.github.io/profiles/.
+ */
+ @PostConstruct
+ public void initApplication() {
+ Collection activeProfiles = Arrays.asList(env.getActiveProfiles());
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
+ log.error("You have misconfigured your application! It should not run " +
+ "with both the 'dev' and 'prod' profiles at the same time.");
+ }
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
+ log.error("You have misconfigured your application! It should not" +
+ "run with both the 'dev' and 'cloud' profiles at the same time.");
+ }
+ }
+
+ /**
+ * Main method, used to run the application.
+ *
+ * @param args the command line arguments
+ * @throws UnknownHostException if the local host name could not be resolved into an address
+ */
+ public static void main(String[] args) throws UnknownHostException {
+ SpringApplication app = new SpringApplication(DealerappApp.class);
+ DefaultProfileUtil.addDefaultProfile(app);
+ Environment env = app.run(args).getEnvironment();
+ String protocol = "http";
+ if (env.getProperty("server.ssl.key-store") != null) {
+ protocol = "https";
+ }
+ log.info("\n----------------------------------------------------------\n\t" +
+ "Application '{}' is running! Access URLs:\n\t" +
+ "Local: \t\t{}://localhost:{}\n\t" +
+ "External: \t{}://{}:{}\n\t" +
+ "Profile(s): \t{}\n----------------------------------------------------------",
+ env.getProperty("spring.application.name"),
+ protocol,
+ env.getProperty("server.port"),
+ protocol,
+ InetAddress.getLocalHost().getHostAddress(),
+ env.getProperty("server.port"),
+ env.getActiveProfiles());
+
+ String configServerStatus = env.getProperty("configserver.status");
+ log.info("\n----------------------------------------------------------\n\t" +
+ "Config Server: \t{}\n----------------------------------------------------------",
+ configServerStatus == null ? "Not found or not setup for this application" : configServerStatus);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/aop/logging/LoggingAspect.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/aop/logging/LoggingAspect.java
new file mode 100644
index 0000000000..a7364157c9
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/aop/logging/LoggingAspect.java
@@ -0,0 +1,79 @@
+package com.dealer.app.aop.logging;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+
+import java.util.Arrays;
+
+/**
+ * Aspect for logging execution of service and repository Spring components.
+ *
+ * By default, it only runs with the "dev" profile.
+ */
+@Aspect
+public class LoggingAspect {
+
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ private final Environment env;
+
+ public LoggingAspect(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Pointcut that matches all repositories, services and Web REST endpoints.
+ */
+ @Pointcut("within(com.dealer.app.repository..*) || within(com.dealer.app.service..*) || within(com.dealer.app.web.rest..*)")
+ public void loggingPointcut() {
+ // Method is empty as this is just a Pointcut, the implementations are in the advices.
+ }
+
+ /**
+ * Advice that logs methods throwing exceptions.
+ */
+ @AfterThrowing(pointcut = "loggingPointcut()", throwing = "e")
+ public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e);
+
+ } else {
+ log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL");
+ }
+ }
+
+ /**
+ * Advice that logs when a method is entered and exited.
+ */
+ @Around("loggingPointcut()")
+ public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ if (log.isDebugEnabled()) {
+ log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
+ }
+ try {
+ Object result = joinPoint.proceed();
+ if (log.isDebugEnabled()) {
+ log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), result);
+ }
+ return result;
+ } catch (IllegalArgumentException e) {
+ log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
+ joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
+
+ throw e;
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/ApplicationProperties.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/ApplicationProperties.java
new file mode 100644
index 0000000000..dc11fa63fe
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/ApplicationProperties.java
@@ -0,0 +1,15 @@
+package com.dealer.app.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Properties specific to JHipster.
+ *
+ *
+ * Properties are configured in the application.yml file.
+ *
+ */
+@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
+public class ApplicationProperties {
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/AsyncConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/AsyncConfiguration.java
new file mode 100644
index 0000000000..3b6de121fd
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/AsyncConfiguration.java
@@ -0,0 +1,46 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
+import io.github.jhipster.config.JHipsterProperties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.*;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+@Configuration
+@EnableAsync
+@EnableScheduling
+public class AsyncConfiguration implements AsyncConfigurer {
+
+ private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public AsyncConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Override
+ @Bean(name = "taskExecutor")
+ public Executor getAsyncExecutor() {
+ log.debug("Creating Async Task Executor");
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
+ executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
+ executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
+ executor.setThreadNamePrefix("dealerapp-Executor-");
+ return new ExceptionHandlingAsyncTaskExecutor(executor);
+ }
+
+ @Override
+ public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+ return new SimpleAsyncUncaughtExceptionHandler();
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/CacheConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/CacheConfiguration.java
new file mode 100644
index 0000000000..6626dea1a2
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/CacheConfiguration.java
@@ -0,0 +1,134 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.Hazelcast;
+import com.hazelcast.config.MapConfig;
+import com.hazelcast.config.EvictionPolicy;
+import com.hazelcast.config.MaxSizeConfig;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.context.annotation.*;
+import org.springframework.core.env.Environment;
+
+import javax.annotation.PreDestroy;
+
+@Configuration
+@EnableCaching
+@AutoConfigureAfter(value = { MetricsConfiguration.class })
+@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class })
+public class CacheConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);
+
+ private final Environment env;
+
+ private final DiscoveryClient discoveryClient;
+
+ private final ServerProperties serverProperties;
+
+ public CacheConfiguration(Environment env, DiscoveryClient discoveryClient, ServerProperties serverProperties) {
+ this.env = env;
+ this.discoveryClient = discoveryClient;
+ this.serverProperties = serverProperties;
+ }
+
+ @PreDestroy
+ public void destroy() {
+ log.info("Closing Cache Manager");
+ Hazelcast.shutdownAll();
+ }
+
+ @Bean
+ public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
+ log.debug("Starting HazelcastCacheManager");
+ CacheManager cacheManager = new com.hazelcast.spring.cache.HazelcastCacheManager(hazelcastInstance);
+ return cacheManager;
+ }
+
+ @Bean
+ public HazelcastInstance hazelcastInstance(JHipsterProperties jHipsterProperties) {
+ log.debug("Configuring Hazelcast");
+ Config config = new Config();
+ config.setInstanceName("dealerapp");
+ // The serviceId is by default the application's name, see Spring Boot's eureka.instance.appname property
+ String serviceId = discoveryClient.getLocalServiceInstance().getServiceId();
+ log.debug("Configuring Hazelcast clustering for instanceId: {}", serviceId);
+
+ // In development, everything goes through 127.0.0.1, with a different port
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ log.debug("Application is running with the \"dev\" profile, Hazelcast " +
+ "cluster will only work with localhost instances");
+
+ System.setProperty("hazelcast.local.localAddress", "127.0.0.1");
+ config.getNetworkConfig().setPort(serverProperties.getPort() + 5701);
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
+ for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
+ String clusterMember = "127.0.0.1:" + (instance.getPort() + 5701);
+ log.debug("Adding Hazelcast (dev) cluster member " + clusterMember);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
+ }
+ } else { // Production configuration, one host per instance all using port 5701
+ config.getNetworkConfig().setPort(5701);
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
+ for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
+ String clusterMember = instance.getHost() + ":5701";
+ log.debug("Adding Hazelcast (prod) cluster member " + clusterMember);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
+ }
+ }
+ config.getMapConfigs().put("default", initializeDefaultMapConfig());
+ config.getMapConfigs().put("com.dealer.app.domain.*", initializeDomainMapConfig(jHipsterProperties));
+ return Hazelcast.newHazelcastInstance(config);
+ }
+
+ private MapConfig initializeDefaultMapConfig() {
+ MapConfig mapConfig = new MapConfig();
+
+ /*
+ Number of backups. If 1 is set as the backup-count for example,
+ then all entries of the map will be copied to another JVM for
+ fail-safety. Valid numbers are 0 (no backup), 1, 2, 3.
+ */
+ mapConfig.setBackupCount(0);
+
+ /*
+ Valid values are:
+ NONE (no eviction),
+ LRU (Least Recently Used),
+ LFU (Least Frequently Used).
+ NONE is the default.
+ */
+ mapConfig.setEvictionPolicy(EvictionPolicy.LRU);
+
+ /*
+ Maximum size of the map. When max size is reached,
+ map is evicted based on the policy defined.
+ Any integer between 0 and Integer.MAX_VALUE. 0 means
+ Integer.MAX_VALUE. Default is 0.
+ */
+ mapConfig.setMaxSizeConfig(new MaxSizeConfig(0, MaxSizeConfig.MaxSizePolicy.USED_HEAP_SIZE));
+
+ return mapConfig;
+ }
+
+ private MapConfig initializeDomainMapConfig(JHipsterProperties jHipsterProperties) {
+ MapConfig mapConfig = new MapConfig();
+ mapConfig.setTimeToLiveSeconds(jHipsterProperties.getCache().getHazelcast().getTimeToLiveSeconds());
+ return mapConfig;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/CloudDatabaseConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/CloudDatabaseConfiguration.java
new file mode 100644
index 0000000000..2a64314685
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/CloudDatabaseConfiguration.java
@@ -0,0 +1,24 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.CacheManager;
+import org.springframework.cloud.config.java.AbstractCloudConfig;
+import org.springframework.context.annotation.*;
+
+import javax.sql.DataSource;
+
+@Configuration
+@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
+public class CloudDatabaseConfiguration extends AbstractCloudConfig {
+
+ private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
+
+ @Bean
+ public DataSource dataSource(CacheManager cacheManager) {
+ log.info("Configuring JDBC datasource from a cloud provider");
+ return connectionFactory().dataSource();
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/Constants.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/Constants.java
new file mode 100644
index 0000000000..0d2098b836
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/Constants.java
@@ -0,0 +1,16 @@
+package com.dealer.app.config;
+
+/**
+ * Application constants.
+ */
+public final class Constants {
+
+ //Regex for acceptable logins
+ public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
+
+ public static final String SYSTEM_ACCOUNT = "system";
+ public static final String ANONYMOUS_USER = "anonymoususer";
+
+ private Constants() {
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DatabaseConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DatabaseConfiguration.java
new file mode 100644
index 0000000000..04fced065b
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DatabaseConfiguration.java
@@ -0,0 +1,75 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;
+
+import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
+import liquibase.integration.spring.SpringLiquibase;
+import org.h2.tools.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+
+@Configuration
+@EnableJpaRepositories("com.dealer.app.repository")
+@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
+@EnableTransactionManagement
+public class DatabaseConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
+
+ private final Environment env;
+
+ public DatabaseConfiguration(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Open the TCP port for the H2 database, so it is available remotely.
+ *
+ * @return the H2 database TCP server
+ * @throws SQLException if the server failed to start
+ */
+ @Bean(initMethod = "start", destroyMethod = "stop")
+ @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
+ public Server h2TCPServer() throws SQLException {
+ return Server.createTcpServer("-tcp","-tcpAllowOthers");
+ }
+
+ @Bean
+ public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor,
+ DataSource dataSource, LiquibaseProperties liquibaseProperties) {
+
+ // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
+ SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env);
+ liquibase.setDataSource(dataSource);
+ liquibase.setChangeLog("classpath:config/liquibase/master.xml");
+ liquibase.setContexts(liquibaseProperties.getContexts());
+ liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
+ liquibase.setDropFirst(liquibaseProperties.isDropFirst());
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) {
+ liquibase.setShouldRun(false);
+ } else {
+ liquibase.setShouldRun(liquibaseProperties.isEnabled());
+ log.debug("Configuring Liquibase");
+ }
+ return liquibase;
+ }
+
+ @Bean
+ public Hibernate5Module hibernate5Module() {
+ return new Hibernate5Module();
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DateTimeFormatConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DateTimeFormatConfiguration.java
new file mode 100644
index 0000000000..cca6e4ca04
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DateTimeFormatConfiguration.java
@@ -0,0 +1,17 @@
+package com.dealer.app.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter {
+
+ @Override
+ public void addFormatters(FormatterRegistry registry) {
+ DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
+ registrar.setUseIsoFormat(true);
+ registrar.registerFormatters(registry);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DefaultProfileUtil.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DefaultProfileUtil.java
new file mode 100644
index 0000000000..c914c96954
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/DefaultProfileUtil.java
@@ -0,0 +1,48 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.core.env.Environment;
+
+import java.util.*;
+
+/**
+ * Utility class to load a Spring profile to be used as default
+ * when there is no spring.profiles.active set in the environment or as command line argument.
+ * If the value is not available in application.yml then dev profile will be used as default.
+ */
+public final class DefaultProfileUtil {
+
+ private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default";
+
+ private DefaultProfileUtil() {
+ }
+
+ /**
+ * Set a default to use when no profile is configured.
+ *
+ * @param app the Spring application
+ */
+ public static void addDefaultProfile(SpringApplication app) {
+ Map defProperties = new HashMap<>();
+ /*
+ * The default profile to use when no other profiles are defined
+ * This cannot be set in the application.yml file.
+ * See https://github.com/spring-projects/spring-boot/issues/1219
+ */
+ defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT);
+ app.setDefaultProperties(defProperties);
+ }
+
+ /**
+ * Get the profiles that are applied else get default profiles.
+ */
+ public static String[] getActiveProfiles(Environment env) {
+ String[] profiles = env.getActiveProfiles();
+ if (profiles.length == 0) {
+ return env.getDefaultProfiles();
+ }
+ return profiles;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LocaleConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LocaleConfiguration.java
new file mode 100644
index 0000000000..6416813b26
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LocaleConfiguration.java
@@ -0,0 +1,35 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.locale.AngularCookieLocaleResolver;
+
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+
+@Configuration
+public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware {
+
+ @Override
+ public void setEnvironment(Environment environment) {
+ // unused
+ }
+
+ @Bean(name = "localeResolver")
+ public LocaleResolver localeResolver() {
+ AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver();
+ cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY");
+ return cookieLocaleResolver;
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
+ localeChangeInterceptor.setParamName("language");
+ registry.addInterceptor(localeChangeInterceptor);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LoggingAspectConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LoggingAspectConfiguration.java
new file mode 100644
index 0000000000..b530771a85
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LoggingAspectConfiguration.java
@@ -0,0 +1,19 @@
+package com.dealer.app.config;
+
+import com.dealer.app.aop.logging.LoggingAspect;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.springframework.context.annotation.*;
+import org.springframework.core.env.Environment;
+
+@Configuration
+@EnableAspectJAutoProxy
+public class LoggingAspectConfiguration {
+
+ @Bean
+ @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
+ public LoggingAspect loggingAspect(Environment env) {
+ return new LoggingAspect(env);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LoggingConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LoggingConfiguration.java
new file mode 100644
index 0000000000..d7ad0602c4
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/LoggingConfiguration.java
@@ -0,0 +1,113 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import ch.qos.logback.classic.AsyncAppender;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.LoggerContextListener;
+import ch.qos.logback.core.spi.ContextAwareBase;
+import net.logstash.logback.appender.LogstashSocketAppender;
+import net.logstash.logback.stacktrace.ShortenedThrowableConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class LoggingConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
+
+ private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+
+ @Value("${spring.application.name}")
+ private String appName;
+
+ @Value("${server.port}")
+ private String serverPort;
+
+ @Value("${eureka.instance.instanceId}")
+ private String instanceId;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public LoggingConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
+ addLogstashAppender(context);
+
+ // Add context listener
+ LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener();
+ loggerContextListener.setContext(context);
+ context.addListener(loggerContextListener);
+ }
+ }
+
+ public void addLogstashAppender(LoggerContext context) {
+ log.info("Initializing Logstash logging");
+
+ LogstashSocketAppender logstashAppender = new LogstashSocketAppender();
+ logstashAppender.setName("LOGSTASH");
+ logstashAppender.setContext(context);
+ String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"," +
+ "\"instance_id\":\"" + instanceId + "\"}";
+
+ // Set the Logstash appender config from JHipster properties
+ logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost());
+ logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort());
+ logstashAppender.setCustomFields(customFields);
+
+ // Limit the maximum length of the forwarded stacktrace so that it won't exceed the 8KB UDP limit of logstash
+ ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter();
+ throwableConverter.setMaxLength(7500);
+ throwableConverter.setRootCauseFirst(true);
+ logstashAppender.setThrowableConverter(throwableConverter);
+
+ logstashAppender.start();
+
+ // Wrap the appender in an Async appender for performance
+ AsyncAppender asyncLogstashAppender = new AsyncAppender();
+ asyncLogstashAppender.setContext(context);
+ asyncLogstashAppender.setName("ASYNC_LOGSTASH");
+ asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
+ asyncLogstashAppender.addAppender(logstashAppender);
+ asyncLogstashAppender.start();
+
+ context.getLogger("ROOT").addAppender(asyncLogstashAppender);
+ }
+
+ /**
+ * Logback configuration is achieved by configuration file and API.
+ * When configuration file change is detected, the configuration is reset.
+ * This listener ensures that the programmatic configuration is also re-applied after reset.
+ */
+ class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener {
+
+ @Override
+ public boolean isResetResistant() {
+ return true;
+ }
+
+ @Override
+ public void onStart(LoggerContext context) {
+ addLogstashAppender(context);
+ }
+
+ @Override
+ public void onReset(LoggerContext context) {
+ addLogstashAppender(context);
+ }
+
+ @Override
+ public void onStop(LoggerContext context) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) {
+ // Nothing to do.
+ }
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/MetricsConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/MetricsConfiguration.java
new file mode 100644
index 0000000000..54b8f2af32
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/MetricsConfiguration.java
@@ -0,0 +1,113 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.JHipsterProperties;
+import io.github.jhipster.config.metrics.SpectatorLogMetricWriter;
+
+import com.netflix.spectator.api.Registry;
+import org.springframework.boot.actuate.autoconfigure.ExportMetricReader;
+import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter;
+import org.springframework.boot.actuate.metrics.writer.MetricWriter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.netflix.metrics.spectator.SpectatorMetricReader;
+
+import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Slf4jReporter;
+import com.codahale.metrics.health.HealthCheckRegistry;
+import com.codahale.metrics.jvm.*;
+import com.ryantenney.metrics.spring.config.annotation.EnableMetrics;
+import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter;
+import com.zaxxer.hikari.HikariDataSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.*;
+
+import javax.annotation.PostConstruct;
+import java.lang.management.ManagementFactory;
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+@EnableMetrics(proxyTargetClass = true)
+public class MetricsConfiguration extends MetricsConfigurerAdapter {
+
+ private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory";
+ private static final String PROP_METRIC_REG_JVM_GARBAGE = "jvm.garbage";
+ private static final String PROP_METRIC_REG_JVM_THREADS = "jvm.threads";
+ private static final String PROP_METRIC_REG_JVM_FILES = "jvm.files";
+ private static final String PROP_METRIC_REG_JVM_BUFFERS = "jvm.buffers";
+ private final Logger log = LoggerFactory.getLogger(MetricsConfiguration.class);
+
+ private MetricRegistry metricRegistry = new MetricRegistry();
+
+ private HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();
+
+ private final JHipsterProperties jHipsterProperties;
+
+ private HikariDataSource hikariDataSource;
+
+ public MetricsConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Autowired(required = false)
+ public void setHikariDataSource(HikariDataSource hikariDataSource) {
+ this.hikariDataSource = hikariDataSource;
+ }
+
+ @Override
+ @Bean
+ public MetricRegistry getMetricRegistry() {
+ return metricRegistry;
+ }
+
+ @Override
+ @Bean
+ public HealthCheckRegistry getHealthCheckRegistry() {
+ return healthCheckRegistry;
+ }
+
+ @PostConstruct
+ public void init() {
+ log.debug("Registering JVM gauges");
+ metricRegistry.register(PROP_METRIC_REG_JVM_MEMORY, new MemoryUsageGaugeSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_GARBAGE, new GarbageCollectorMetricSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_THREADS, new ThreadStatesGaugeSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_FILES, new FileDescriptorRatioGauge());
+ metricRegistry.register(PROP_METRIC_REG_JVM_BUFFERS, new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer()));
+ if (hikariDataSource != null) {
+ log.debug("Monitoring the datasource");
+ hikariDataSource.setMetricRegistry(metricRegistry);
+ }
+ if (jHipsterProperties.getMetrics().getJmx().isEnabled()) {
+ log.debug("Initializing Metrics JMX reporting");
+ JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build();
+ jmxReporter.start();
+ }
+ if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
+ log.info("Initializing Metrics Log reporting");
+ final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
+ .outputTo(LoggerFactory.getLogger("metrics"))
+ .convertRatesTo(TimeUnit.SECONDS)
+ .convertDurationsTo(TimeUnit.MILLISECONDS)
+ .build();
+ reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS);
+ }
+ }
+
+ /* Spectator metrics log reporting */
+ @Bean
+ @ConditionalOnProperty("jhipster.logging.spectator-metrics.enabled")
+ @ExportMetricReader
+ public SpectatorMetricReader SpectatorMetricReader(Registry registry) {
+ log.info("Initializing Spectator Metrics Log reporting");
+ return new SpectatorMetricReader(registry);
+ }
+
+ @Bean
+ @ConditionalOnProperty("jhipster.logging.spectator-metrics.enabled")
+ @ExportMetricWriter
+ MetricWriter metricWriter() {
+ return new SpectatorLogMetricWriter();
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/MicroserviceSecurityConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/MicroserviceSecurityConfiguration.java
new file mode 100644
index 0000000000..86a109e91f
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/MicroserviceSecurityConfiguration.java
@@ -0,0 +1,71 @@
+package com.dealer.app.config;
+
+import com.dealer.app.security.AuthoritiesConstants;
+import com.dealer.app.security.jwt.JWTConfigurer;
+import com.dealer.app.security.jwt.TokenProvider;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+public class MicroserviceSecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ private final TokenProvider tokenProvider;
+
+ public MicroserviceSecurityConfiguration(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web.ignoring()
+ .antMatchers(HttpMethod.OPTIONS, "/**")
+ .antMatchers("/app/**/*.{js,html}")
+ .antMatchers("/bower_components/**")
+ .antMatchers("/i18n/**")
+ .antMatchers("/content/**")
+ .antMatchers("/swagger-ui/index.html")
+ .antMatchers("/test/**")
+ .antMatchers("/h2-console/**");
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .csrf()
+ .disable()
+ .headers()
+ .frameOptions()
+ .disable()
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .authorizeRequests()
+ .antMatchers("/api/**").authenticated()
+ .antMatchers("/management/health").permitAll()
+ .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
+ .antMatchers("/swagger-resources/configuration/ui").permitAll()
+ .and()
+ .apply(securityConfigurerAdapter());
+ }
+
+ private JWTConfigurer securityConfigurerAdapter() {
+ return new JWTConfigurer(tokenProvider);
+ }
+
+ @Bean
+ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
+ return new SecurityEvaluationContextExtension();
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/ThymeleafConfiguration.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/ThymeleafConfiguration.java
new file mode 100644
index 0000000000..5bc92dd86d
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/ThymeleafConfiguration.java
@@ -0,0 +1,26 @@
+package com.dealer.app.config;
+
+import org.apache.commons.lang3.CharEncoding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.*;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+
+@Configuration
+public class ThymeleafConfiguration {
+
+ @SuppressWarnings("unused")
+ private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class);
+
+ @Bean
+ @Description("Thymeleaf template resolver serving HTML 5 emails")
+ public ClassLoaderTemplateResolver emailTemplateResolver() {
+ ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
+ emailTemplateResolver.setPrefix("mails/");
+ emailTemplateResolver.setSuffix(".html");
+ emailTemplateResolver.setTemplateMode("HTML5");
+ emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
+ emailTemplateResolver.setOrder(1);
+ return emailTemplateResolver;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/WebConfigurer.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/WebConfigurer.java
new file mode 100644
index 0000000000..f34caa57d7
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/WebConfigurer.java
@@ -0,0 +1,144 @@
+package com.dealer.app.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.servlet.InstrumentedFilter;
+import com.codahale.metrics.servlets.MetricsServlet;
+import com.hazelcast.core.HazelcastInstance;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.embedded.*;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
+import io.undertow.UndertowOptions;
+import org.springframework.boot.web.servlet.ServletContextInitializer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.util.*;
+import javax.servlet.*;
+
+/**
+ * Configuration of web application with Servlet 3.0 APIs.
+ */
+@Configuration
+public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
+
+ private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
+
+ private final Environment env;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ private final HazelcastInstance hazelcastInstance;
+
+ private MetricRegistry metricRegistry;
+
+ public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties, HazelcastInstance hazelcastInstance) {
+
+ this.env = env;
+ this.jHipsterProperties = jHipsterProperties;
+ this.hazelcastInstance = hazelcastInstance;
+ }
+
+ @Override
+ public void onStartup(ServletContext servletContext) throws ServletException {
+ if (env.getActiveProfiles().length != 0) {
+ log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
+ }
+ EnumSet disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
+ initMetrics(servletContext, disps);
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ initH2Console(servletContext);
+ }
+ log.info("Web application fully configured");
+ }
+
+ /**
+ * Customize the Servlet engine: Mime types, the document root, the cache.
+ */
+ @Override
+ public void customize(ConfigurableEmbeddedServletContainer container) {
+ MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
+ // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
+ mappings.add("html", "text/html;charset=utf-8");
+ // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
+ mappings.add("json", "text/html;charset=utf-8");
+ container.setMimeMappings(mappings);
+
+ /*
+ * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
+ * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
+ * See the JHipsterProperties class and your application-*.yml configuration files
+ * for more information.
+ */
+ if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
+ container instanceof UndertowEmbeddedServletContainerFactory) {
+
+ ((UndertowEmbeddedServletContainerFactory) container)
+ .addBuilderCustomizers(builder ->
+ builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
+ }
+ }
+
+ /**
+ * Initializes Metrics.
+ */
+ private void initMetrics(ServletContext servletContext, EnumSet disps) {
+ log.debug("Initializing Metrics registries");
+ servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
+ metricRegistry);
+ servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
+ metricRegistry);
+
+ log.debug("Registering Metrics Filter");
+ FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
+ new InstrumentedFilter());
+
+ metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
+ metricsFilter.setAsyncSupported(true);
+
+ log.debug("Registering Metrics Servlet");
+ ServletRegistration.Dynamic metricsAdminServlet =
+ servletContext.addServlet("metricsServlet", new MetricsServlet());
+
+ metricsAdminServlet.addMapping("/management/metrics/*");
+ metricsAdminServlet.setAsyncSupported(true);
+ metricsAdminServlet.setLoadOnStartup(2);
+ }
+
+ @Bean
+ public CorsFilter corsFilter() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = jHipsterProperties.getCors();
+ if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
+ log.debug("Registering CORS filter");
+ source.registerCorsConfiguration("/api/**", config);
+ source.registerCorsConfiguration("/v2/api-docs", config);
+ }
+ return new CorsFilter(source);
+ }
+
+ /**
+ * Initializes H2 console.
+ */
+ private void initH2Console(ServletContext servletContext) {
+ log.debug("Initialize H2 console");
+ ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
+ h2ConsoleServlet.addMapping("/h2-console/*");
+ h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
+ h2ConsoleServlet.setLoadOnStartup(1);
+ }
+
+ @Autowired(required = false)
+ public void setMetricRegistry(MetricRegistry metricRegistry) {
+ this.metricRegistry = metricRegistry;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/audit/AuditEventConverter.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/audit/AuditEventConverter.java
new file mode 100644
index 0000000000..6e04a2556f
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/audit/AuditEventConverter.java
@@ -0,0 +1,91 @@
+package com.dealer.app.config.audit;
+
+import com.dealer.app.domain.PersistentAuditEvent;
+
+import org.springframework.boot.actuate.audit.AuditEvent;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.*;
+
+@Component
+public class AuditEventConverter {
+
+ /**
+ * Convert a list of PersistentAuditEvent to a list of AuditEvent
+ *
+ * @param persistentAuditEvents the list to convert
+ * @return the converted list.
+ */
+ public List convertToAuditEvent(Iterable persistentAuditEvents) {
+ if (persistentAuditEvents == null) {
+ return Collections.emptyList();
+ }
+ List auditEvents = new ArrayList<>();
+ for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) {
+ auditEvents.add(convertToAuditEvent(persistentAuditEvent));
+ }
+ return auditEvents;
+ }
+
+ /**
+ * Convert a PersistentAuditEvent to an AuditEvent
+ *
+ * @param persistentAuditEvent the event to convert
+ * @return the converted list.
+ */
+ public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) {
+ Instant instant = persistentAuditEvent.getAuditEventDate().atZone(ZoneId.systemDefault()).toInstant();
+ return new AuditEvent(Date.from(instant), persistentAuditEvent.getPrincipal(),
+ persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData()));
+ }
+
+ /**
+ * Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface
+ *
+ * @param data the data to convert
+ * @return a map of String, Object
+ */
+ public Map convertDataToObjects(Map data) {
+ Map results = new HashMap<>();
+
+ if (data != null) {
+ for (Map.Entry entry : data.entrySet()) {
+ results.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Internal conversion. This method will allow to save additional data.
+ * By default, it will save the object as string
+ *
+ * @param data the data to convert
+ * @return a map of String, String
+ */
+ public Map convertDataToStrings(Map data) {
+ Map results = new HashMap<>();
+
+ if (data != null) {
+ for (Map.Entry entry : data.entrySet()) {
+ Object object = entry.getValue();
+
+ // Extract the data that will be saved.
+ if (object instanceof WebAuthenticationDetails) {
+ WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) object;
+ results.put("remoteAddress", authenticationDetails.getRemoteAddress());
+ results.put("sessionId", authenticationDetails.getSessionId());
+ } else if (object != null) {
+ results.put(entry.getKey(), object.toString());
+ } else {
+ results.put(entry.getKey(), "null");
+ }
+ }
+ }
+
+ return results;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/audit/package-info.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/audit/package-info.java
new file mode 100644
index 0000000000..fb44326371
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/audit/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Audit specific code.
+ */
+package com.dealer.app.config.audit;
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/package-info.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/package-info.java
new file mode 100644
index 0000000000..bb4cb44cbf
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/config/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring Framework configuration files.
+ */
+package com.dealer.app.config;
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/AbstractAuditingEntity.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/AbstractAuditingEntity.java
new file mode 100644
index 0000000000..1bfb1025cc
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/AbstractAuditingEntity.java
@@ -0,0 +1,80 @@
+package com.dealer.app.domain;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.hibernate.envers.Audited;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+import java.time.ZonedDateTime;
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+
+/**
+ * Base abstract class for entities which will hold definitions for created, last modified by and created,
+ * last modified by date.
+ */
+@MappedSuperclass
+@Audited
+@EntityListeners(AuditingEntityListener.class)
+public abstract class AbstractAuditingEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @CreatedBy
+ @Column(name = "created_by", nullable = false, length = 50, updatable = false)
+ @JsonIgnore
+ private String createdBy;
+
+ @CreatedDate
+ @Column(name = "created_date", nullable = false)
+ @JsonIgnore
+ private ZonedDateTime createdDate = ZonedDateTime.now();
+
+ @LastModifiedBy
+ @Column(name = "last_modified_by", length = 50)
+ @JsonIgnore
+ private String lastModifiedBy;
+
+ @LastModifiedDate
+ @Column(name = "last_modified_date")
+ @JsonIgnore
+ private ZonedDateTime lastModifiedDate = ZonedDateTime.now();
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public ZonedDateTime getCreatedDate() {
+ return createdDate;
+ }
+
+ public void setCreatedDate(ZonedDateTime createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ public String getLastModifiedBy() {
+ return lastModifiedBy;
+ }
+
+ public void setLastModifiedBy(String lastModifiedBy) {
+ this.lastModifiedBy = lastModifiedBy;
+ }
+
+ public ZonedDateTime getLastModifiedDate() {
+ return lastModifiedDate;
+ }
+
+ public void setLastModifiedDate(ZonedDateTime lastModifiedDate) {
+ this.lastModifiedDate = lastModifiedDate;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/Dealer.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/Dealer.java
new file mode 100644
index 0000000000..10c967a093
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/Dealer.java
@@ -0,0 +1,92 @@
+package com.dealer.app.domain;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * A Dealer.
+ */
+@Entity
+@Table(name = "dealer")
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+public class Dealer implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "name")
+ private String name;
+
+ @Column(name = "address")
+ private String address;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Dealer name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public Dealer address(String address) {
+ this.address = address;
+ return this;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Dealer dealer = (Dealer) o;
+ if (dealer.id == null || id == null) {
+ return false;
+ }
+ return Objects.equals(id, dealer.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(id);
+ }
+
+ @Override
+ public String toString() {
+ return "Dealer{" +
+ "id=" + id +
+ ", name='" + name + "'" +
+ ", address='" + address + "'" +
+ '}';
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/PersistentAuditEvent.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/PersistentAuditEvent.java
new file mode 100644
index 0000000000..a5c02428a5
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/PersistentAuditEvent.java
@@ -0,0 +1,78 @@
+package com.dealer.app.domain;
+
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Persist AuditEvent managed by the Spring Boot actuator
+ * @see org.springframework.boot.actuate.audit.AuditEvent
+ */
+@Entity
+@Table(name = "jhi_persistent_audit_event")
+public class PersistentAuditEvent implements Serializable {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "event_id")
+ private Long id;
+
+ @NotNull
+ @Column(nullable = false)
+ private String principal;
+
+ @Column(name = "event_date")
+ private LocalDateTime auditEventDate;
+ @Column(name = "event_type")
+ private String auditEventType;
+
+ @ElementCollection
+ @MapKeyColumn(name = "name")
+ @Column(name = "value")
+ @CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id"))
+ private Map data = new HashMap<>();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getPrincipal() {
+ return principal;
+ }
+
+ public void setPrincipal(String principal) {
+ this.principal = principal;
+ }
+
+ public LocalDateTime getAuditEventDate() {
+ return auditEventDate;
+ }
+
+ public void setAuditEventDate(LocalDateTime auditEventDate) {
+ this.auditEventDate = auditEventDate;
+ }
+
+ public String getAuditEventType() {
+ return auditEventType;
+ }
+
+ public void setAuditEventType(String auditEventType) {
+ this.auditEventType = auditEventType;
+ }
+
+ public Map getData() {
+ return data;
+ }
+
+ public void setData(Map data) {
+ this.data = data;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/package-info.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/package-info.java
new file mode 100644
index 0000000000..0651edd7cc
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/domain/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * JPA domain objects.
+ */
+package com.dealer.app.domain;
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/repository/DealerRepository.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/repository/DealerRepository.java
new file mode 100644
index 0000000000..ff77d228a9
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/repository/DealerRepository.java
@@ -0,0 +1,15 @@
+package com.dealer.app.repository;
+
+import com.dealer.app.domain.Dealer;
+
+import org.springframework.data.jpa.repository.*;
+
+import java.util.List;
+
+/**
+ * Spring Data JPA repository for the Dealer entity.
+ */
+@SuppressWarnings("unused")
+public interface DealerRepository extends JpaRepository {
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/repository/package-info.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/repository/package-info.java
new file mode 100644
index 0000000000..93be9f265d
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/repository/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring Data JPA repositories.
+ */
+package com.dealer.app.repository;
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/AuthoritiesConstants.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/AuthoritiesConstants.java
new file mode 100644
index 0000000000..c4ade99d0e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/AuthoritiesConstants.java
@@ -0,0 +1,16 @@
+package com.dealer.app.security;
+
+/**
+ * Constants for Spring Security authorities.
+ */
+public final class AuthoritiesConstants {
+
+ public static final String ADMIN = "ROLE_ADMIN";
+
+ public static final String USER = "ROLE_USER";
+
+ public static final String ANONYMOUS = "ROLE_ANONYMOUS";
+
+ private AuthoritiesConstants() {
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/SecurityUtils.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/SecurityUtils.java
new file mode 100644
index 0000000000..1b3c1e5c90
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/SecurityUtils.java
@@ -0,0 +1,68 @@
+package com.dealer.app.security;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * Utility class for Spring Security.
+ */
+public final class SecurityUtils {
+
+ private SecurityUtils() {
+ }
+
+ /**
+ * Get the login of the current user.
+ *
+ * @return the login of the current user
+ */
+ public static String getCurrentUserLogin() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ String userName = null;
+ if (authentication != null) {
+ if (authentication.getPrincipal() instanceof UserDetails) {
+ UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
+ userName = springSecurityUser.getUsername();
+ } else if (authentication.getPrincipal() instanceof String) {
+ userName = (String) authentication.getPrincipal();
+ }
+ }
+ return userName;
+ }
+
+ /**
+ * Check if a user is authenticated.
+ *
+ * @return true if the user is authenticated, false otherwise
+ */
+ public static boolean isAuthenticated() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ if (authentication != null) {
+ return authentication.getAuthorities().stream()
+ .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS));
+ }
+ return false;
+ }
+
+ /**
+ * If the current user has a specific authority (security role).
+ *
+ *
The name of this method comes from the isUserInRole() method in the Servlet API
+ *
+ * @param authority the authority to check
+ * @return true if the current user has the authority, false otherwise
+ */
+ public static boolean isCurrentUserInRole(String authority) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ if (authentication != null) {
+ return authentication.getAuthorities().stream()
+ .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
+ }
+ return false;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/SpringSecurityAuditorAware.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/SpringSecurityAuditorAware.java
new file mode 100644
index 0000000000..72e518b1bc
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/SpringSecurityAuditorAware.java
@@ -0,0 +1,19 @@
+package com.dealer.app.security;
+
+import com.dealer.app.config.Constants;
+
+import org.springframework.data.domain.AuditorAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Implementation of AuditorAware based on Spring Security.
+ */
+@Component
+public class SpringSecurityAuditorAware implements AuditorAware {
+
+ @Override
+ public String getCurrentAuditor() {
+ String userName = SecurityUtils.getCurrentUserLogin();
+ return userName != null ? userName : Constants.SYSTEM_ACCOUNT;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/JWTConfigurer.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/JWTConfigurer.java
new file mode 100644
index 0000000000..0f7b3cf164
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/JWTConfigurer.java
@@ -0,0 +1,23 @@
+package com.dealer.app.security.jwt;
+
+import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+public class JWTConfigurer extends SecurityConfigurerAdapter {
+
+ public static final String AUTHORIZATION_HEADER = "Authorization";
+
+ private TokenProvider tokenProvider;
+
+ public JWTConfigurer(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+ JWTFilter customFilter = new JWTFilter(tokenProvider);
+ http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/JWTFilter.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/JWTFilter.java
new file mode 100644
index 0000000000..55e22bd10a
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/JWTFilter.java
@@ -0,0 +1,58 @@
+package com.dealer.app.security.jwt;
+
+import java.io.IOException;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.GenericFilterBean;
+
+import io.jsonwebtoken.ExpiredJwtException;
+
+/**
+ * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is
+ * found.
+ */
+public class JWTFilter extends GenericFilterBean {
+
+ private final Logger log = LoggerFactory.getLogger(JWTFilter.class);
+
+ private TokenProvider tokenProvider;
+
+ public JWTFilter(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+ try {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+ String jwt = resolveToken(httpServletRequest);
+ if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
+ Authentication authentication = this.tokenProvider.getAuthentication(jwt);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ } catch (ExpiredJwtException eje) {
+ log.info("Security exception for user {} - {}",
+ eje.getClaims().getSubject(), eje.getMessage());
+
+ log.trace("Security exception trace: {}", eje);
+ ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ private String resolveToken(HttpServletRequest request){
+ String bearerToken = request.getHeader(JWTConfigurer.AUTHORIZATION_HEADER);
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
+ return bearerToken.substring(7, bearerToken.length());
+ }
+ return null;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/TokenProvider.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/TokenProvider.java
new file mode 100644
index 0000000000..47a5bf75af
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/security/jwt/TokenProvider.java
@@ -0,0 +1,109 @@
+package com.dealer.app.security.jwt;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import javax.annotation.PostConstruct;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.stereotype.Component;
+
+import io.jsonwebtoken.*;
+
+@Component
+public class TokenProvider {
+
+ private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
+
+ private static final String AUTHORITIES_KEY = "auth";
+
+ private String secretKey;
+
+ private long tokenValidityInMilliseconds;
+
+ private long tokenValidityInMillisecondsForRememberMe;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public TokenProvider(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @PostConstruct
+ public void init() {
+ this.secretKey =
+ jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret();
+
+ this.tokenValidityInMilliseconds =
+ 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds();
+ this.tokenValidityInMillisecondsForRememberMe =
+ 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe();
+ }
+
+ public String createToken(Authentication authentication, Boolean rememberMe) {
+ String authorities = authentication.getAuthorities().stream()
+ .map(GrantedAuthority::getAuthority)
+ .collect(Collectors.joining(","));
+
+ long now = (new Date()).getTime();
+ Date validity;
+ if (rememberMe) {
+ validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
+ } else {
+ validity = new Date(now + this.tokenValidityInMilliseconds);
+ }
+
+ return Jwts.builder()
+ .setSubject(authentication.getName())
+ .claim(AUTHORITIES_KEY, authorities)
+ .signWith(SignatureAlgorithm.HS512, secretKey)
+ .setExpiration(validity)
+ .compact();
+ }
+
+ public Authentication getAuthentication(String token) {
+ Claims claims = Jwts.parser()
+ .setSigningKey(secretKey)
+ .parseClaimsJws(token)
+ .getBody();
+
+ Collection extends GrantedAuthority> authorities =
+ Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
+ .map(SimpleGrantedAuthority::new)
+ .collect(Collectors.toList());
+
+ User principal = new User(claims.getSubject(), "", authorities);
+
+ return new UsernamePasswordAuthenticationToken(principal, "", authorities);
+ }
+
+ public boolean validateToken(String authToken) {
+ try {
+ Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
+ return true;
+ } catch (SignatureException e) {
+ log.info("Invalid JWT signature.");
+ log.trace("Invalid JWT signature trace: {}", e);
+ } catch (MalformedJwtException e) {
+ log.info("Invalid JWT token.");
+ log.trace("Invalid JWT token trace: {}", e);
+ } catch (ExpiredJwtException e) {
+ log.info("Expired JWT token.");
+ log.trace("Expired JWT token trace: {}", e);
+ } catch (UnsupportedJwtException e) {
+ log.info("Unsupported JWT token.");
+ log.trace("Unsupported JWT token trace: {}", e);
+ } catch (IllegalArgumentException e) {
+ log.info("JWT token compact of handler are invalid.");
+ log.trace("JWT token compact of handler are invalid trace: {}", e);
+ }
+ return false;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/service/package-info.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/service/package-info.java
new file mode 100644
index 0000000000..b6795f34a3
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/service/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Service layer beans.
+ */
+package com.dealer.app.service;
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/DealerResource.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/DealerResource.java
new file mode 100644
index 0000000000..bd49b3d179
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/DealerResource.java
@@ -0,0 +1,128 @@
+package com.dealer.app.web.rest;
+
+import com.codahale.metrics.annotation.Timed;
+import com.dealer.app.domain.Dealer;
+
+import com.dealer.app.repository.DealerRepository;
+import com.dealer.app.web.rest.util.HeaderUtil;
+import com.dealer.app.web.rest.util.PaginationUtil;
+import io.swagger.annotations.ApiParam;
+import io.github.jhipster.web.util.ResponseUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * REST controller for managing Dealer.
+ */
+@RestController
+@RequestMapping("/api")
+public class DealerResource {
+
+ private final Logger log = LoggerFactory.getLogger(DealerResource.class);
+
+ private static final String ENTITY_NAME = "dealer";
+
+ private final DealerRepository dealerRepository;
+
+ public DealerResource(DealerRepository dealerRepository) {
+ this.dealerRepository = dealerRepository;
+ }
+
+ /**
+ * POST /dealers : Create a new dealer.
+ *
+ * @param dealer the dealer to create
+ * @return the ResponseEntity with status 201 (Created) and with body the new dealer, or with status 400 (Bad Request) if the dealer has already an ID
+ * @throws URISyntaxException if the Location URI syntax is incorrect
+ */
+ @PostMapping("/dealers")
+ @Timed
+ public ResponseEntity createDealer(@RequestBody Dealer dealer) throws URISyntaxException {
+ log.debug("REST request to save Dealer : {}", dealer);
+ if (dealer.getId() != null) {
+ return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new dealer cannot already have an ID")).body(null);
+ }
+ Dealer result = dealerRepository.save(dealer);
+ return ResponseEntity.created(new URI("/api/dealers/" + result.getId()))
+ .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
+ .body(result);
+ }
+
+ /**
+ * PUT /dealers : Updates an existing dealer.
+ *
+ * @param dealer the dealer to update
+ * @return the ResponseEntity with status 200 (OK) and with body the updated dealer,
+ * or with status 400 (Bad Request) if the dealer is not valid,
+ * or with status 500 (Internal Server Error) if the dealer couldnt be updated
+ * @throws URISyntaxException if the Location URI syntax is incorrect
+ */
+ @PutMapping("/dealers")
+ @Timed
+ public ResponseEntity updateDealer(@RequestBody Dealer dealer) throws URISyntaxException {
+ log.debug("REST request to update Dealer : {}", dealer);
+ if (dealer.getId() == null) {
+ return createDealer(dealer);
+ }
+ Dealer result = dealerRepository.save(dealer);
+ return ResponseEntity.ok()
+ .headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, dealer.getId().toString()))
+ .body(result);
+ }
+
+ /**
+ * GET /dealers : get all the dealers.
+ *
+ * @param pageable the pagination information
+ * @return the ResponseEntity with status 200 (OK) and the list of dealers in body
+ * @throws URISyntaxException if there is an error to generate the pagination HTTP headers
+ */
+ @GetMapping("/dealers")
+ @Timed
+ public ResponseEntity> getAllDealers(@ApiParam Pageable pageable) {
+ log.debug("REST request to get a page of Dealers");
+ Page page = dealerRepository.findAll(pageable);
+ HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/dealers");
+ return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
+ }
+
+ /**
+ * GET /dealers/:id : get the "id" dealer.
+ *
+ * @param id the id of the dealer to retrieve
+ * @return the ResponseEntity with status 200 (OK) and with body the dealer, or with status 404 (Not Found)
+ */
+ @GetMapping("/dealers/{id}")
+ @Timed
+ public ResponseEntity getDealer(@PathVariable Long id) {
+ log.debug("REST request to get Dealer : {}", id);
+ Dealer dealer = dealerRepository.findOne(id);
+ return ResponseUtil.wrapOrNotFound(Optional.ofNullable(dealer));
+ }
+
+ /**
+ * DELETE /dealers/:id : delete the "id" dealer.
+ *
+ * @param id the id of the dealer to delete
+ * @return the ResponseEntity with status 200 (OK)
+ */
+ @DeleteMapping("/dealers/{id}")
+ @Timed
+ public ResponseEntity deleteDealer(@PathVariable Long id) {
+ log.debug("REST request to delete Dealer : {}", id);
+ dealerRepository.delete(id);
+ return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/LogsResource.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/LogsResource.java
new file mode 100644
index 0000000000..844fdfe7bb
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/LogsResource.java
@@ -0,0 +1,39 @@
+package com.dealer.app.web.rest;
+
+import com.dealer.app.web.rest.vm.LoggerVM;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import com.codahale.metrics.annotation.Timed;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Controller for view and managing Log Level at runtime.
+ */
+@RestController
+@RequestMapping("/management")
+public class LogsResource {
+
+ @GetMapping("/logs")
+ @Timed
+ public List getList() {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ return context.getLoggerList()
+ .stream()
+ .map(LoggerVM::new)
+ .collect(Collectors.toList());
+ }
+
+ @PutMapping("/logs")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ @Timed
+ public void changeLevel(@RequestBody LoggerVM jsonLogger) {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel()));
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/ProfileInfoResource.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/ProfileInfoResource.java
new file mode 100644
index 0000000000..4c2d37b3ff
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/ProfileInfoResource.java
@@ -0,0 +1,69 @@
+package com.dealer.app.web.rest;
+
+import com.dealer.app.config.DefaultProfileUtil;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import org.springframework.core.env.Environment;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Resource to return information about the currently running Spring profiles.
+ */
+@RestController
+@RequestMapping("/api")
+public class ProfileInfoResource {
+
+ private final Environment env;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public ProfileInfoResource(Environment env, JHipsterProperties jHipsterProperties) {
+ this.env = env;
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @GetMapping("/profile-info")
+ public ProfileInfoVM getActiveProfiles() {
+ String[] activeProfiles = DefaultProfileUtil.getActiveProfiles(env);
+ return new ProfileInfoVM(activeProfiles, getRibbonEnv(activeProfiles));
+ }
+
+ private String getRibbonEnv(String[] activeProfiles) {
+ String[] displayOnActiveProfiles = jHipsterProperties.getRibbon().getDisplayOnActiveProfiles();
+ if (displayOnActiveProfiles == null) {
+ return null;
+ }
+ List ribbonProfiles = new ArrayList<>(Arrays.asList(displayOnActiveProfiles));
+ List springBootProfiles = Arrays.asList(activeProfiles);
+ ribbonProfiles.retainAll(springBootProfiles);
+ if (!ribbonProfiles.isEmpty()) {
+ return ribbonProfiles.get(0);
+ }
+ return null;
+ }
+
+ class ProfileInfoVM {
+
+ private String[] activeProfiles;
+
+ private String ribbonEnv;
+
+ ProfileInfoVM(String[] activeProfiles, String ribbonEnv) {
+ this.activeProfiles = activeProfiles;
+ this.ribbonEnv = ribbonEnv;
+ }
+
+ public String[] getActiveProfiles() {
+ return activeProfiles;
+ }
+
+ public String getRibbonEnv() {
+ return ribbonEnv;
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/CustomParameterizedException.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/CustomParameterizedException.java
new file mode 100644
index 0000000000..3897d94eba
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/CustomParameterizedException.java
@@ -0,0 +1,34 @@
+package com.dealer.app.web.rest.errors;
+
+/**
+ * Custom, parameterized exception, which can be translated on the client side.
+ * For example:
+ *
+ *
+ * throw new CustomParameterizedException("myCustomError", "hello", "world");
+ *
+ *
+ * Can be translated with:
+ *
+ *
+ * "error.myCustomError" : "The server says {{params[0]}} to {{params[1]}}"
+ *
+ */
+public class CustomParameterizedException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+ private final String[] params;
+
+ public CustomParameterizedException(String message, String... params) {
+ super(message);
+ this.message = message;
+ this.params = params;
+ }
+
+ public ParameterizedErrorVM getErrorVM() {
+ return new ParameterizedErrorVM(message, params);
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ErrorConstants.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ErrorConstants.java
new file mode 100644
index 0000000000..4ace18012e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ErrorConstants.java
@@ -0,0 +1,14 @@
+package com.dealer.app.web.rest.errors;
+
+public final class ErrorConstants {
+
+ public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure";
+ public static final String ERR_ACCESS_DENIED = "error.accessDenied";
+ public static final String ERR_VALIDATION = "error.validation";
+ public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported";
+ public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError";
+
+ private ErrorConstants() {
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ErrorVM.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ErrorVM.java
new file mode 100644
index 0000000000..4f242f21e8
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ErrorVM.java
@@ -0,0 +1,52 @@
+package com.dealer.app.web.rest.errors;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * View Model for transferring error message with a list of field errors.
+ */
+public class ErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+ private final String description;
+
+ private List fieldErrors;
+
+ public ErrorVM(String message) {
+ this(message, null);
+ }
+
+ public ErrorVM(String message, String description) {
+ this.message = message;
+ this.description = description;
+ }
+
+ public ErrorVM(String message, String description, List fieldErrors) {
+ this.message = message;
+ this.description = description;
+ this.fieldErrors = fieldErrors;
+ }
+
+ public void add(String objectName, String field, String message) {
+ if (fieldErrors == null) {
+ fieldErrors = new ArrayList<>();
+ }
+ fieldErrors.add(new FieldErrorVM(objectName, field, message));
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public List getFieldErrors() {
+ return fieldErrors;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ExceptionTranslator.java
new file mode 100644
index 0000000000..17b1dc6c62
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ExceptionTranslator.java
@@ -0,0 +1,85 @@
+package com.dealer.app.web.rest.errors;
+
+import java.util.List;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.dao.ConcurrencyFailureException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.ResponseEntity.BodyBuilder;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * Controller advice to translate the server side exceptions to client-friendly json structures.
+ */
+@ControllerAdvice
+public class ExceptionTranslator {
+
+ @ExceptionHandler(ConcurrencyFailureException.class)
+ @ResponseStatus(HttpStatus.CONFLICT)
+ @ResponseBody
+ public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) {
+ return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE);
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ResponseBody
+ public ErrorVM processValidationError(MethodArgumentNotValidException ex) {
+ BindingResult result = ex.getBindingResult();
+ List fieldErrors = result.getFieldErrors();
+
+ return processFieldErrors(fieldErrors);
+ }
+
+ @ExceptionHandler(CustomParameterizedException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ResponseBody
+ public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) {
+ return ex.getErrorVM();
+ }
+
+ @ExceptionHandler(AccessDeniedException.class)
+ @ResponseStatus(HttpStatus.FORBIDDEN)
+ @ResponseBody
+ public ErrorVM processAccessDeniedException(AccessDeniedException e) {
+ return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage());
+ }
+
+ private ErrorVM processFieldErrors(List fieldErrors) {
+ ErrorVM dto = new ErrorVM(ErrorConstants.ERR_VALIDATION);
+
+ for (FieldError fieldError : fieldErrors) {
+ dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode());
+ }
+
+ return dto;
+ }
+
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+ @ResponseBody
+ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+ public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
+ return new ErrorVM(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage());
+ }
+
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity processRuntimeException(Exception ex) {
+ BodyBuilder builder;
+ ErrorVM errorVM;
+ ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
+ if (responseStatus != null) {
+ builder = ResponseEntity.status(responseStatus.value());
+ errorVM = new ErrorVM("error." + responseStatus.value().value(), responseStatus.reason());
+ } else {
+ builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
+ errorVM = new ErrorVM(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error");
+ }
+ return builder.body(errorVM);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/FieldErrorVM.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/FieldErrorVM.java
new file mode 100644
index 0000000000..036e358f41
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/FieldErrorVM.java
@@ -0,0 +1,33 @@
+package com.dealer.app.web.rest.errors;
+
+import java.io.Serializable;
+
+public class FieldErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String objectName;
+
+ private final String field;
+
+ private final String message;
+
+ public FieldErrorVM(String dto, String field, String message) {
+ this.objectName = dto;
+ this.field = field;
+ this.message = message;
+ }
+
+ public String getObjectName() {
+ return objectName;
+ }
+
+ public String getField() {
+ return field;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ParameterizedErrorVM.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ParameterizedErrorVM.java
new file mode 100644
index 0000000000..7b0d4f23fa
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/errors/ParameterizedErrorVM.java
@@ -0,0 +1,27 @@
+package com.dealer.app.web.rest.errors;
+
+import java.io.Serializable;
+
+/**
+ * View Model for sending a parameterized error message.
+ */
+public class ParameterizedErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private final String message;
+ private final String[] params;
+
+ public ParameterizedErrorVM(String message, String... params) {
+ this.message = message;
+ this.params = params;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String[] getParams() {
+ return params;
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/package-info.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/package-info.java
new file mode 100644
index 0000000000..20c6b71ccc
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring MVC REST controllers.
+ */
+package com.dealer.app.web.rest;
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/util/HeaderUtil.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/util/HeaderUtil.java
new file mode 100644
index 0000000000..63efc36663
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/util/HeaderUtil.java
@@ -0,0 +1,43 @@
+package com.dealer.app.web.rest.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+
+/**
+ * Utility class for HTTP headers creation.
+ */
+public final class HeaderUtil {
+
+ private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class);
+
+ private HeaderUtil() {
+ }
+
+ public static HttpHeaders createAlert(String message, String param) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-dealerappApp-alert", message);
+ headers.add("X-dealerappApp-params", param);
+ return headers;
+ }
+
+ public static HttpHeaders createEntityCreationAlert(String entityName, String param) {
+ return createAlert("A new " + entityName + " is created with identifier " + param, param);
+ }
+
+ public static HttpHeaders createEntityUpdateAlert(String entityName, String param) {
+ return createAlert("A " + entityName + " is updated with identifier " + param, param);
+ }
+
+ public static HttpHeaders createEntityDeletionAlert(String entityName, String param) {
+ return createAlert("A " + entityName + " is deleted with identifier " + param, param);
+ }
+
+ public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) {
+ log.error("Entity creation failed, {}", defaultMessage);
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-dealerappApp-error", defaultMessage);
+ headers.add("X-dealerappApp-params", entityName);
+ return headers;
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/util/PaginationUtil.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/util/PaginationUtil.java
new file mode 100644
index 0000000000..39b7eae434
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/util/PaginationUtil.java
@@ -0,0 +1,47 @@
+package com.dealer.app.web.rest.util;
+
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.net.URISyntaxException;
+
+/**
+ * Utility class for handling pagination.
+ *
+ *
+ * Pagination uses the same principles as the Github API,
+ * and follow RFC 5988 (Link header).
+ */
+public final class PaginationUtil {
+
+ private PaginationUtil() {
+ }
+
+ public static HttpHeaders generatePaginationHttpHeaders(Page page, String baseUrl) {
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-Total-Count", "" + Long.toString(page.getTotalElements()));
+ String link = "";
+ if ((page.getNumber() + 1) < page.getTotalPages()) {
+ link = "<" + generateUri(baseUrl, page.getNumber() + 1, page.getSize()) + ">; rel=\"next\",";
+ }
+ // prev link
+ if ((page.getNumber()) > 0) {
+ link += "<" + generateUri(baseUrl, page.getNumber() - 1, page.getSize()) + ">; rel=\"prev\",";
+ }
+ // last and first link
+ int lastPage = 0;
+ if (page.getTotalPages() > 0) {
+ lastPage = page.getTotalPages() - 1;
+ }
+ link += "<" + generateUri(baseUrl, lastPage, page.getSize()) + ">; rel=\"last\",";
+ link += "<" + generateUri(baseUrl, 0, page.getSize()) + ">; rel=\"first\"";
+ headers.add(HttpHeaders.LINK, link);
+ return headers;
+ }
+
+ private static String generateUri(String baseUrl, int page, int size) {
+ return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString();
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/vm/LoggerVM.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/vm/LoggerVM.java
new file mode 100644
index 0000000000..82afc8c883
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/vm/LoggerVM.java
@@ -0,0 +1,48 @@
+package com.dealer.app.web.rest.vm;
+
+import ch.qos.logback.classic.Logger;
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+/**
+ * View Model object for storing a Logback logger.
+ */
+public class LoggerVM {
+
+ private String name;
+
+ private String level;
+
+ public LoggerVM(Logger logger) {
+ this.name = logger.getName();
+ this.level = logger.getEffectiveLevel().toString();
+ }
+
+ @JsonCreator
+ public LoggerVM() {
+ // Empty public constructor used by Jackson.
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLevel() {
+ return level;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ @Override
+ public String toString() {
+ return "LoggerVM{" +
+ "name='" + name + '\'' +
+ ", level='" + level + '\'' +
+ '}';
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/vm/package-info.java b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/vm/package-info.java
new file mode 100644
index 0000000000..563901498e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/java/com/dealer/app/web/rest/vm/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * View Models used by Spring MVC REST controllers.
+ */
+package com.dealer.app.web.rest.vm;
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/.h2.server.properties b/jhipster/jhipster-microservice/dealer-app/src/main/resources/.h2.server.properties
new file mode 100644
index 0000000000..8d8f192ac0
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/.h2.server.properties
@@ -0,0 +1,5 @@
+#H2 Server Properties
+0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/dealerapp|dealerapp
+webAllowOthers=true
+webPort=8082
+webSSL=false
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/banner.txt b/jhipster/jhipster-microservice/dealer-app/src/main/resources/banner.txt
new file mode 100644
index 0000000000..c3d8cf725d
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+
+ ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗
+ ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ â•šâ•â•â–ˆâ–ˆâ•”â•â•â• ██╔â•â•â•â–ˆâ–ˆâ•— ██╔â•â•â•â•â• â•šâ•â•â–ˆâ–ˆâ•”â•â•â• ██╔â•â•â•â•â•â• ██╔â•â•â•â–ˆâ–ˆâ•—
+ ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╠╚█████╗ ██║ ██████╗ ███████╔â•
+ ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔â•â•â•â–ˆâ–ˆâ•‘ ██║ ██╔â•â•â•â•â• â•šâ•â•â•â–ˆâ–ˆâ•— ██║ ██╔â•â•â•â• ██╔â•â•â–ˆâ–ˆâ•‘
+ ${AnsiColor.GREEN}╚██████╔â•${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╠██║ ████████╗ ██║ ╚██╗
+ ${AnsiColor.GREEN} â•šâ•â•â•â•â•â• ${AnsiColor.RED} â•šâ•â• â•šâ•â• â•šâ•â•â•â•â•â•â•â• â•šâ•â• â•šâ•â•â•â•â•â• â•šâ•â• â•šâ•â•â•â•â•â•â•â• â•šâ•â• â•šâ•â•
+
+${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} ::
+:: http://jhipster.github.io ::${AnsiColor.DEFAULT}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application-dev.yml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application-dev.yml
new file mode 100644
index 0000000000..7231a7cfa9
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application-dev.yml
@@ -0,0 +1,148 @@
+# ===================================================================
+# Spring Boot configuration for the "dev" profile.
+#
+# This configuration overrides the application.yml file.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ prefer-ip-address: true
+ client:
+ enabled: true
+ healthcheck:
+ enabled: true
+ registerWithEureka: true
+ fetchRegistry: true
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
+
+spring:
+ profiles:
+ active: dev
+ include: swagger
+ devtools:
+ restart:
+ enabled: true
+ livereload:
+ enabled: false # we use gulp + BrowserSync for livereload
+ jackson:
+ serialization.indent_output: true
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:h2:file:./target/h2db/db/dealerapp;DB_CLOSE_DELAY=-1
+ username: dealerapp
+ password:
+ h2:
+ console:
+ enabled: false
+ jpa:
+ database-platform: io.github.jhipster.domain.util.FixedH2Dialect
+ database: H2
+ show-sql: true
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: true
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: true
+ hibernate.cache.region.factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
+ hibernate.cache.hazelcast.instance_name: dealerapp
+ hibernate.cache.use_minimal_puts: true
+ hibernate.cache.hazelcast.use_lite_member: true
+ mail:
+ host: localhost
+ port: 25
+ username:
+ password:
+ messages:
+ cache-seconds: 1
+ thymeleaf:
+ cache: false
+
+liquibase:
+ contexts: dev
+
+# ===================================================================
+# To enable SSL, generate a certificate using:
+# keytool -genkey -alias dealerapp -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
+#
+# You can also use Let's Encrypt:
+# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
+#
+# Then, modify the server.ssl properties so your "server" configuration looks like:
+#
+# server:
+# port: 8443
+# ssl:
+# key-store: keystore.p12
+# key-store-password:
+# keyStoreType: PKCS12
+# keyAlias: dealerapp
+# ===================================================================
+server:
+ port: 8082
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ http:
+ version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
+ cache: # Cache configuration
+ hazelcast: # Hazelcast distributed cache
+ time-to-live-seconds: 3600
+ backup-count: 1
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ token-validity-in-seconds-for-remember-me: 2592000
+ mail: # specific JHipster mail property, for standard properties see MailProperties
+ from: dealerapp@localhost
+ base-url: http://127.0.0.1:8082
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+ graphite: # Use the "graphite" Maven profile to have the Graphite dependencies
+ enabled: false
+ host: localhost
+ port: 2003
+ prefix: dealerapp
+ prometheus: # Use the "prometheus" Maven profile to have the Prometheus dependencies
+ enabled: false
+ endpoint: /prometheusMetrics
+ logs: # Reports Dropwizard metrics in the logs
+ enabled: false
+ reportFrequency: 60 # in seconds
+ logging:
+ logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
+ enabled: false
+ host: localhost
+ port: 5000
+ queue-size: 512
+ spectator-metrics: # Reports Spectator Circuit Breaker metrics in the logs
+ enabled: false
+ # edit spring.metrics.export.delay-millis to set report frequency
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application-prod.yml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application-prod.yml
new file mode 100644
index 0000000000..8c14e671ea
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application-prod.yml
@@ -0,0 +1,150 @@
+# ===================================================================
+# Spring Boot configuration for the "prod" profile.
+#
+# This configuration overrides the application.yml file.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ prefer-ip-address: true
+ client:
+ enabled: true
+ healthcheck:
+ enabled: true
+ registerWithEureka: true
+ fetchRegistry: true
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
+
+spring:
+ devtools:
+ restart:
+ enabled: false
+ livereload:
+ enabled: false
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:mysql://localhost:3306/dealerapp?useUnicode=true&characterEncoding=utf8&useSSL=false
+ username: root
+ password:
+ hikari:
+ data-source-properties:
+ cachePrepStmts: true
+ prepStmtCacheSize: 250
+ prepStmtCacheSqlLimit: 2048
+ useServerPrepStmts: true
+ jpa:
+ database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
+ database: MYSQL
+ show-sql: false
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: true
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: false
+ hibernate.cache.region.factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
+ hibernate.cache.hazelcast.instance_name: dealerapp
+ hibernate.cache.use_minimal_puts: true
+ hibernate.cache.hazelcast.use_lite_member: true
+ mail:
+ host: localhost
+ port: 25
+ username:
+ password:
+ thymeleaf:
+ cache: true
+
+liquibase:
+ contexts: prod
+
+# ===================================================================
+# To enable SSL, generate a certificate using:
+# keytool -genkey -alias dealerapp -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
+#
+# You can also use Let's Encrypt:
+# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
+#
+# Then, modify the server.ssl properties so your "server" configuration looks like:
+#
+# server:
+# port: 443
+# ssl:
+# key-store: keystore.p12
+# key-store-password:
+# keyStoreType: PKCS12
+# keyAlias: dealerapp
+# ===================================================================
+server:
+ port: 8082
+ compression:
+ enabled: true
+ mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json
+ min-response-size: 1024
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ http:
+ version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
+ cache: # Used by the CachingHttpHeadersFilter
+ timeToLiveInDays: 1461
+ cache: # Cache configuration
+ hazelcast: # Hazelcast distributed cache
+ time-to-live-seconds: 3600
+ backup-count: 1
+ security:
+ authentication:
+ jwt:
+ secret: d4c73e937677223a85c7fcebae7a6ce0c48c3b01
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ token-validity-in-seconds-for-remember-me: 2592000
+ mail: # specific JHipster mail property, for standard properties see MailProperties
+ from: dealerapp@localhost
+ base-url: http://my-server-url-to-change # Modify according to your server's URL
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+ graphite:
+ enabled: false
+ host: localhost
+ port: 2003
+ prefix: dealerapp
+ prometheus:
+ enabled: false
+ endpoint: /prometheusMetrics
+ logs: # Reports Dropwizard metrics in the logs
+ enabled: false
+ reportFrequency: 60 # in seconds
+ logging:
+ logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
+ enabled: false
+ host: localhost
+ port: 5000
+ queue-size: 512
+ spectator-metrics: # Reports Spectator Circuit Breaker metrics in the logs
+ enabled: false
+ # edit spring.metrics.export.delay-millis to set report frequency
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application.yml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application.yml
new file mode 100644
index 0000000000..8c77dfd2f3
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/application.yml
@@ -0,0 +1,119 @@
+# ===================================================================
+# Spring Boot configuration.
+#
+# This configuration will be overriden by the Spring profile you use,
+# for example application-dev.yml if you use the "dev" profile.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ appname: dealerapp
+ instanceId: dealerapp:${spring.application.instance_id:${random.value}}
+ statusPageUrlPath: ${management.context-path}/info
+ healthCheckUrlPath: ${management.context-path}/health
+ metadata-map:
+ profile: ${spring.profiles.active}
+ version: ${info.project.version}
+ribbon:
+ eureka:
+ enabled: true
+# See https://github.com/Netflix/Hystrix/wiki/Configuration
+#hystrix:
+# command:
+# default:
+# execution:
+# isolation:
+# thread:
+# timeoutInMilliseconds: 10000
+
+management:
+ security:
+ roles: ADMIN
+ context-path: /management
+ health:
+ mail:
+ enabled: false # When using the MailService, configure an SMTP server and set this to true
+spring:
+ application:
+ name: dealerapp
+ jackson:
+ serialization.write_dates_as_timestamps: false
+ jpa:
+ open-in-view: false
+ hibernate:
+ ddl-auto: none
+ naming:
+ physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
+ implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ messages:
+ basename: i18n/messages
+ mvc:
+ favicon:
+ enabled: false
+ thymeleaf:
+ mode: XHTML
+
+security:
+ basic:
+ enabled: false
+
+server:
+ session:
+ cookie:
+ http-only: true
+
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ async:
+ core-pool-size: 2
+ max-pool-size: 50
+ queue-capacity: 10000
+ # By default CORS is disabled. Uncomment to enable.
+ #cors:
+ #allowed-origins: "*"
+ #allowed-methods: GET, PUT, POST, DELETE, OPTIONS
+ #allowed-headers: "*"
+ #exposed-headers:
+ #allow-credentials: true
+ #max-age: 1800
+ mail:
+ from: dealerapp@localhost
+ swagger:
+ default-include-pattern: /api/.*
+ title: dealerapp API
+ description: dealerapp API documentation
+ version: 0.0.1
+ terms-of-service-url:
+ contact-name:
+ contact-url:
+ contact-email:
+ license:
+ license-url:
+ ribbon:
+ display-on-active-profiles: dev
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/bootstrap-prod.yml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/bootstrap-prod.yml
new file mode 100644
index 0000000000..5c7ed3cc2c
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/bootstrap-prod.yml
@@ -0,0 +1,22 @@
+# ===================================================================
+# Spring Cloud Config bootstrap configuration for the "prod" profile
+# ===================================================================
+
+spring:
+ cloud:
+ config:
+ fail-fast: true
+ retry:
+ initial-interval: 1000
+ max-interval: 2000
+ max-attempts: 100
+ uri: http://admin:${jhipster.registry.password}@localhost:8761/config
+ # name of the config server's property source (file.yml) that we want to use
+ name: dealerapp
+ profile: prod # profile(s) of the property source
+ label: master # toggle to switch to a different version of the configuration as stored in git
+ # it can be set to any label, branch or commit of the config source git repository
+
+jhipster:
+ registry:
+ password: admin
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/bootstrap.yml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/bootstrap.yml
new file mode 100644
index 0000000000..c9528d6c90
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/bootstrap.yml
@@ -0,0 +1,30 @@
+# ===================================================================
+# Spring Cloud Config bootstrap configuration for the "dev" profile
+# In prod profile, properties will be overwriten by the ones defined in bootstrap-prod.yml
+# ===================================================================
+
+jhipster:
+ registry:
+ password: admin
+
+spring:
+ application:
+ name: dealerapp
+ profiles:
+ # The commented value for `active` can be replaced with valid Spring profiles to load.
+ # Otherwise, it will be filled in by maven when building the WAR file
+ # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS`
+ active: #spring.profiles.active#
+ cloud:
+ config:
+ fail-fast: true
+ uri: http://admin:${jhipster.registry.password}@localhost:8761/config
+ # name of the config server's property source (file.yml) that we want to use
+ name: dealerapp
+ profile: dev # profile(s) of the property source
+ label: master # toggle to switch to a different version of the configuration as stored in git
+ # it can be set to any label, branch or commit of the config source git repository
+
+info:
+ project:
+ version: #project.version#
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
new file mode 100644
index 0000000000..2a1ee4d6bf
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/changelog/20170503044952_added_entity_Dealer.xml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/changelog/20170503044952_added_entity_Dealer.xml
new file mode 100644
index 0000000000..128e46a24e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/changelog/20170503044952_added_entity_Dealer.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/master.xml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/master.xml
new file mode 100644
index 0000000000..121f0ad396
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/config/liquibase/master.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/i18n/messages.properties b/jhipster/jhipster-microservice/dealer-app/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000000..a26917e0cc
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/i18n/messages.properties
@@ -0,0 +1,22 @@
+# Error page
+error.title=Your request cannot be processed
+error.subtitle=Sorry, an error has occurred.
+error.status=Status:
+error.message=Message:
+
+# Activation e-mail
+email.activation.title=dealerapp account activation
+email.activation.greeting=Dear {0}
+email.activation.text1=Your dealerapp account has been created, please click on the URL below to activate it:
+email.activation.text2=Regards,
+email.signature=dealerapp Team.
+
+# Creation email
+email.creation.text1=Your dealerapp account has been created, please click on the URL below to access it:
+
+# Reset e-mail
+email.reset.title=dealerapp password reset
+email.reset.greeting=Dear {0}
+email.reset.text1=For your dealerapp account a password reset was requested, please click on the URL below to reset it:
+email.reset.text2=Regards,
+
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/logback-spring.xml b/jhipster/jhipster-microservice/dealer-app/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000..2ae9944ae0
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/logback-spring.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/src/main/resources/templates/error.html b/jhipster/jhipster-microservice/dealer-app/src/main/resources/templates/error.html
new file mode 100644
index 0000000000..774b080d6c
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/main/resources/templates/error.html
@@ -0,0 +1,162 @@
+
+
+
+
+ Your request cannot be processed
+
+
+
+
+
Your request cannot be processed :(
+
+
Sorry, an error has occurred.
+
+ Status: ()
+
+ Message:
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/src/test/java/com/dealer/app/web/rest/DealerResourceIntTest.java b/jhipster/jhipster-microservice/dealer-app/src/test/java/com/dealer/app/web/rest/DealerResourceIntTest.java
new file mode 100644
index 0000000000..7778a2783e
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/test/java/com/dealer/app/web/rest/DealerResourceIntTest.java
@@ -0,0 +1,235 @@
+package com.dealer.app.web.rest;
+
+import com.dealer.app.DealerappApp;
+
+import com.dealer.app.domain.Dealer;
+import com.dealer.app.repository.DealerRepository;
+import com.dealer.app.web.rest.errors.ExceptionTranslator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+/**
+ * Test class for the DealerResource REST controller.
+ *
+ * @see DealerResource
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = DealerappApp.class)
+public class DealerResourceIntTest {
+
+ private static final String DEFAULT_NAME = "AAAAAAAAAA";
+ private static final String UPDATED_NAME = "BBBBBBBBBB";
+
+ private static final String DEFAULT_ADDRESS = "AAAAAAAAAA";
+ private static final String UPDATED_ADDRESS = "BBBBBBBBBB";
+
+ @Autowired
+ private DealerRepository dealerRepository;
+
+ @Autowired
+ private MappingJackson2HttpMessageConverter jacksonMessageConverter;
+
+ @Autowired
+ private PageableHandlerMethodArgumentResolver pageableArgumentResolver;
+
+ @Autowired
+ private ExceptionTranslator exceptionTranslator;
+
+ @Autowired
+ private EntityManager em;
+
+ private MockMvc restDealerMockMvc;
+
+ private Dealer dealer;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ DealerResource dealerResource = new DealerResource(dealerRepository);
+ this.restDealerMockMvc = MockMvcBuilders.standaloneSetup(dealerResource)
+ .setCustomArgumentResolvers(pageableArgumentResolver)
+ .setControllerAdvice(exceptionTranslator)
+ .setMessageConverters(jacksonMessageConverter).build();
+ }
+
+ /**
+ * Create an entity for this test.
+ *
+ * This is a static method, as tests for other entities might also need it,
+ * if they test an entity which requires the current entity.
+ */
+ public static Dealer createEntity(EntityManager em) {
+ Dealer dealer = new Dealer()
+ .name(DEFAULT_NAME)
+ .address(DEFAULT_ADDRESS);
+ return dealer;
+ }
+
+ @Before
+ public void initTest() {
+ dealer = createEntity(em);
+ }
+
+ @Test
+ @Transactional
+ public void createDealer() throws Exception {
+ int databaseSizeBeforeCreate = dealerRepository.findAll().size();
+
+ // Create the Dealer
+ restDealerMockMvc.perform(post("/api/dealers")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(dealer)))
+ .andExpect(status().isCreated());
+
+ // Validate the Dealer in the database
+ List dealerList = dealerRepository.findAll();
+ assertThat(dealerList).hasSize(databaseSizeBeforeCreate + 1);
+ Dealer testDealer = dealerList.get(dealerList.size() - 1);
+ assertThat(testDealer.getName()).isEqualTo(DEFAULT_NAME);
+ assertThat(testDealer.getAddress()).isEqualTo(DEFAULT_ADDRESS);
+ }
+
+ @Test
+ @Transactional
+ public void createDealerWithExistingId() throws Exception {
+ int databaseSizeBeforeCreate = dealerRepository.findAll().size();
+
+ // Create the Dealer with an existing ID
+ dealer.setId(1L);
+
+ // An entity with an existing ID cannot be created, so this API call must fail
+ restDealerMockMvc.perform(post("/api/dealers")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(dealer)))
+ .andExpect(status().isBadRequest());
+
+ // Validate the Alice in the database
+ List dealerList = dealerRepository.findAll();
+ assertThat(dealerList).hasSize(databaseSizeBeforeCreate);
+ }
+
+ @Test
+ @Transactional
+ public void getAllDealers() throws Exception {
+ // Initialize the database
+ dealerRepository.saveAndFlush(dealer);
+
+ // Get all the dealerList
+ restDealerMockMvc.perform(get("/api/dealers?sort=id,desc"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andExpect(jsonPath("$.[*].id").value(hasItem(dealer.getId().intValue())))
+ .andExpect(jsonPath("$.[*].name").value(hasItem(DEFAULT_NAME.toString())))
+ .andExpect(jsonPath("$.[*].address").value(hasItem(DEFAULT_ADDRESS.toString())));
+ }
+
+ @Test
+ @Transactional
+ public void getDealer() throws Exception {
+ // Initialize the database
+ dealerRepository.saveAndFlush(dealer);
+
+ // Get the dealer
+ restDealerMockMvc.perform(get("/api/dealers/{id}", dealer.getId()))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andExpect(jsonPath("$.id").value(dealer.getId().intValue()))
+ .andExpect(jsonPath("$.name").value(DEFAULT_NAME.toString()))
+ .andExpect(jsonPath("$.address").value(DEFAULT_ADDRESS.toString()));
+ }
+
+ @Test
+ @Transactional
+ public void getNonExistingDealer() throws Exception {
+ // Get the dealer
+ restDealerMockMvc.perform(get("/api/dealers/{id}", Long.MAX_VALUE))
+ .andExpect(status().isNotFound());
+ }
+
+ @Test
+ @Transactional
+ public void updateDealer() throws Exception {
+ // Initialize the database
+ dealerRepository.saveAndFlush(dealer);
+ int databaseSizeBeforeUpdate = dealerRepository.findAll().size();
+
+ // Update the dealer
+ Dealer updatedDealer = dealerRepository.findOne(dealer.getId());
+ updatedDealer
+ .name(UPDATED_NAME)
+ .address(UPDATED_ADDRESS);
+
+ restDealerMockMvc.perform(put("/api/dealers")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(updatedDealer)))
+ .andExpect(status().isOk());
+
+ // Validate the Dealer in the database
+ List dealerList = dealerRepository.findAll();
+ assertThat(dealerList).hasSize(databaseSizeBeforeUpdate);
+ Dealer testDealer = dealerList.get(dealerList.size() - 1);
+ assertThat(testDealer.getName()).isEqualTo(UPDATED_NAME);
+ assertThat(testDealer.getAddress()).isEqualTo(UPDATED_ADDRESS);
+ }
+
+ @Test
+ @Transactional
+ public void updateNonExistingDealer() throws Exception {
+ int databaseSizeBeforeUpdate = dealerRepository.findAll().size();
+
+ // Create the Dealer
+
+ // If the entity doesn't have an ID, it will be created instead of just being updated
+ restDealerMockMvc.perform(put("/api/dealers")
+ .contentType(TestUtil.APPLICATION_JSON_UTF8)
+ .content(TestUtil.convertObjectToJsonBytes(dealer)))
+ .andExpect(status().isCreated());
+
+ // Validate the Dealer in the database
+ List dealerList = dealerRepository.findAll();
+ assertThat(dealerList).hasSize(databaseSizeBeforeUpdate + 1);
+ }
+
+ @Test
+ @Transactional
+ public void deleteDealer() throws Exception {
+ // Initialize the database
+ dealerRepository.saveAndFlush(dealer);
+ int databaseSizeBeforeDelete = dealerRepository.findAll().size();
+
+ // Get the dealer
+ restDealerMockMvc.perform(delete("/api/dealers/{id}", dealer.getId())
+ .accept(TestUtil.APPLICATION_JSON_UTF8))
+ .andExpect(status().isOk());
+
+ // Validate the database is empty
+ List dealerList = dealerRepository.findAll();
+ assertThat(dealerList).hasSize(databaseSizeBeforeDelete - 1);
+ }
+
+ @Test
+ @Transactional
+ public void equalsVerifier() throws Exception {
+ TestUtil.equalsVerifier(Dealer.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/test/java/com/dealer/app/web/rest/TestUtil.java b/jhipster/jhipster-microservice/dealer-app/src/test/java/com/dealer/app/web/rest/TestUtil.java
new file mode 100644
index 0000000000..7a51b3380d
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/test/java/com/dealer/app/web/rest/TestUtil.java
@@ -0,0 +1,120 @@
+package com.dealer.app.web.rest;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.http.MediaType;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeParseException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Utility class for testing REST controllers.
+ */
+public class TestUtil {
+
+ /** MediaType for JSON UTF8 */
+ public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(
+ MediaType.APPLICATION_JSON.getType(),
+ MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
+
+ /**
+ * Convert an object to JSON byte array.
+ *
+ * @param object
+ * the object to convert
+ * @return the JSON byte array
+ * @throws IOException
+ */
+ public static byte[] convertObjectToJsonBytes(Object object)
+ throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+
+ JavaTimeModule module = new JavaTimeModule();
+ mapper.registerModule(module);
+
+ return mapper.writeValueAsBytes(object);
+ }
+
+ /**
+ * Create a byte array with a specific size filled with specified data.
+ *
+ * @param size the size of the byte array
+ * @param data the data to put in the byte array
+ * @return the JSON byte array
+ */
+ public static byte[] createByteArray(int size, String data) {
+ byte[] byteArray = new byte[size];
+ for (int i = 0; i < size; i++) {
+ byteArray[i] = Byte.parseByte(data, 2);
+ }
+ return byteArray;
+ }
+
+ /**
+ * A matcher that tests that the examined string represents the same instant as the reference datetime.
+ */
+ public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher {
+
+ private final ZonedDateTime date;
+
+ public ZonedDateTimeMatcher(ZonedDateTime date) {
+ this.date = date;
+ }
+
+ @Override
+ protected boolean matchesSafely(String item, Description mismatchDescription) {
+ try {
+ if (!date.isEqual(ZonedDateTime.parse(item))) {
+ mismatchDescription.appendText("was ").appendValue(item);
+ return false;
+ }
+ return true;
+ } catch (DateTimeParseException e) {
+ mismatchDescription.appendText("was ").appendValue(item)
+ .appendText(", which could not be parsed as a ZonedDateTime");
+ return false;
+ }
+
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("a String representing the same Instant as ").appendValue(date);
+ }
+ }
+
+ /**
+ * Creates a matcher that matches when the examined string reprensents the same instant as the reference datetime
+ * @param date the reference datetime against which the examined string is checked
+ */
+ public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) {
+ return new ZonedDateTimeMatcher(date);
+ }
+
+ /**
+ * Verifies the equals/hashcode contract on the domain object.
+ */
+ public static void equalsVerifier(Class clazz) throws Exception {
+ Object domainObject1 = clazz.getConstructor().newInstance();
+ assertThat(domainObject1.toString()).isNotNull();
+ assertThat(domainObject1).isEqualTo(domainObject1);
+ assertThat(domainObject1.hashCode()).isEqualTo(domainObject1.hashCode());
+ // Test with an instance of another class
+ Object testOtherObject = new Object();
+ assertThat(domainObject1).isNotEqualTo(testOtherObject);
+ // Test with an instance of the same class
+ Object domainObject2 = clazz.getConstructor().newInstance();
+ assertThat(domainObject1).isNotEqualTo(domainObject2);
+ // HashCodes are equals because the objects are not persisted yet
+ assertThat(domainObject1.hashCode()).isEqualTo(domainObject2.hashCode());
+ }
+}
diff --git a/jhipster/jhipster-microservice/dealer-app/src/test/resources/config/application.yml b/jhipster/jhipster-microservice/dealer-app/src/test/resources/config/application.yml
new file mode 100644
index 0000000000..8959533002
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/test/resources/config/application.yml
@@ -0,0 +1,102 @@
+# ===================================================================
+# Spring Boot configuration.
+#
+# This configuration is used for unit/integration tests.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ client:
+ enabled: false
+ instance:
+ appname: dealerapp
+ instanceId: dealerapp:${spring.application.instance_id:${random.value}}
+
+spring:
+ application:
+ name: dealerapp
+ jackson:
+ serialization.write_dates_as_timestamps: false
+ cache:
+ type: none
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:h2:mem:dealerapp;DB_CLOSE_DELAY=-1
+ name:
+ username:
+ password:
+ jpa:
+ database-platform: io.github.jhipster.domain.util.FixedH2Dialect
+ database: H2
+ open-in-view: false
+ show-sql: true
+ hibernate:
+ ddl-auto: none
+ naming:
+ physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
+ implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: false
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: true
+ hibernate.hbm2ddl.auto: validate
+ mail:
+ host: localhost
+ messages:
+ basename: i18n/messages
+ mvc:
+ favicon:
+ enabled: false
+ thymeleaf:
+ mode: XHTML
+
+liquibase:
+ contexts: test
+
+security:
+ basic:
+ enabled: false
+
+server:
+ port: 10344
+ address: localhost
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ async:
+ core-pool-size: 2
+ max-pool-size: 50
+ queue-capacity: 10000
+ security:
+ authentication:
+ jwt:
+ secret: d4c73e937677223a85c7fcebae7a6ce0c48c3b01
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/dealer-app/src/test/resources/config/bootstrap.yml b/jhipster/jhipster-microservice/dealer-app/src/test/resources/config/bootstrap.yml
new file mode 100644
index 0000000000..11cd6af21c
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/test/resources/config/bootstrap.yml
@@ -0,0 +1,4 @@
+spring:
+ cloud:
+ config:
+ enabled: false
diff --git a/jhipster/jhipster-microservice/dealer-app/src/test/resources/logback-test.xml b/jhipster/jhipster-microservice/dealer-app/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..83efcf9b13
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/src/test/resources/logback-test.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/dealer-app/yarn.lock b/jhipster/jhipster-microservice/dealer-app/yarn.lock
new file mode 100644
index 0000000000..4333a516f1
--- /dev/null
+++ b/jhipster/jhipster-microservice/dealer-app/yarn.lock
@@ -0,0 +1,2439 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+abbrev@1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
+
+acorn-jsx@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+ dependencies:
+ acorn "^3.0.4"
+
+acorn@^3.0.4:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+
+ajv@^4.9.1:
+ version "4.11.8"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
+ dependencies:
+ co "^4.6.0"
+ json-stable-stringify "^1.0.1"
+
+amdefine@>=0.0.4:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
+
+ansi-escapes@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-styles@^2.0.0, ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+ansi@^0.3.0, ansi@~0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21"
+
+are-we-there-yet@~1.1.2:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
+ dependencies:
+ sprintf-js "~1.0.2"
+
+array-differ@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031"
+
+array-find-index@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-uniq@^1.0.0, array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+
+arrify@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+
+asn1@~0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+
+assert-plus@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+
+ast-query@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ast-query/-/ast-query-2.0.0.tgz#3588e79ad8de07ce50df1e781cc2bda1fd69a453"
+ dependencies:
+ acorn-jsx "^3.0.1"
+ class-extend "^0.1.1"
+ escodegen-wallaby "^1.6.7"
+ lodash "^4.6.1"
+ traverse "^0.6.6"
+
+async@^1.0.0, async@^1.4.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+
+async@^2.0.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611"
+ dependencies:
+ lodash "^4.14.0"
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+
+aws-sign2@~0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
+
+aws4@^1.2.1:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+
+balanced-match@^0.4.1:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
+ dependencies:
+ tweetnacl "^0.14.3"
+
+bin-version-check@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-2.1.0.tgz#e4e5df290b9069f7d111324031efc13fdd11a5b0"
+ dependencies:
+ bin-version "^1.0.0"
+ minimist "^1.1.0"
+ semver "^4.0.3"
+ semver-truncate "^1.0.0"
+
+bin-version@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-1.0.4.tgz#9eb498ee6fd76f7ab9a7c160436f89579435d78e"
+ dependencies:
+ find-versions "^1.0.0"
+
+"binaryextensions@1 || 2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.0.0.tgz#e597d1a7a6a3558a2d1c7241a16c99965e6aa40f"
+
+boolbase@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+
+boom@2.x.x:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
+ dependencies:
+ hoek "2.x.x"
+
+boxen@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/boxen/-/boxen-0.3.1.tgz#a7d898243ae622f7abb6bb604d740a76c6a5461b"
+ dependencies:
+ chalk "^1.1.1"
+ filled-array "^1.0.0"
+ object-assign "^4.0.1"
+ repeating "^2.0.0"
+ string-width "^1.0.1"
+ widest-line "^1.0.0"
+
+brace-expansion@^1.0.0:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59"
+ dependencies:
+ balanced-match "^0.4.1"
+ concat-map "0.0.1"
+
+buffer-shims@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
+
+builtin-modules@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+camelcase-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
+ dependencies:
+ camelcase "^2.0.0"
+ map-obj "^1.0.0"
+
+camelcase@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
+
+capture-stack-trace@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+cheerio@0.22.0:
+ version "0.22.0"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
+ dependencies:
+ css-select "~1.2.0"
+ dom-serializer "~0.1.0"
+ entities "~1.1.1"
+ htmlparser2 "^3.9.1"
+ lodash.assignin "^4.0.9"
+ lodash.bind "^4.1.4"
+ lodash.defaults "^4.0.1"
+ lodash.filter "^4.4.0"
+ lodash.flatten "^4.2.0"
+ lodash.foreach "^4.3.0"
+ lodash.map "^4.4.0"
+ lodash.merge "^4.4.0"
+ lodash.pick "^4.2.1"
+ lodash.reduce "^4.4.0"
+ lodash.reject "^4.4.0"
+ lodash.some "^4.4.0"
+
+cheerio@^0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925"
+ dependencies:
+ css-select "~1.0.0"
+ dom-serializer "~0.1.0"
+ entities "~1.1.1"
+ htmlparser2 "~3.8.1"
+ lodash "^3.2.0"
+
+class-extend@^0.1.0, class-extend@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/class-extend/-/class-extend-0.1.2.tgz#8057a82b00f53f82a5d62c50ef8cffdec6fabc34"
+ dependencies:
+ object-assign "^2.0.0"
+
+cli-boxes@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
+
+cli-cursor@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
+ dependencies:
+ restore-cursor "^1.0.1"
+
+cli-list@^0.1.1:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/cli-list/-/cli-list-0.1.8.tgz#aee6d45c4c59bf80068bb968089fb06f1aeddc0a"
+
+cli-table@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
+ dependencies:
+ colors "1.0.3"
+
+cli-width@^1.0.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-1.1.1.tgz#a4d293ef67ebb7b88d4a4d42c0ccf00c4d1e366d"
+
+cli-width@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+
+clone-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-1.0.0.tgz#eae0a2413f55c0942f818c229fefce845d7f3b1c"
+ dependencies:
+ is-regexp "^1.0.0"
+ is-supported-regexp-flag "^1.0.0"
+
+clone-stats@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1"
+
+clone@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149"
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+
+colors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
+
+combined-stream@^1.0.5, combined-stream@~1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commondir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
+concat-stream@^1.4.7:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
+ dependencies:
+ inherits "^2.0.3"
+ readable-stream "^2.2.2"
+ typedarray "^0.0.6"
+
+config-chain@~1.1.8:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2"
+ dependencies:
+ ini "^1.3.4"
+ proto-list "~1.2.1"
+
+configstore@^1.0.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021"
+ dependencies:
+ graceful-fs "^4.1.2"
+ mkdirp "^0.5.0"
+ object-assign "^4.0.1"
+ os-tmpdir "^1.0.0"
+ osenv "^0.1.0"
+ uuid "^2.0.1"
+ write-file-atomic "^1.1.2"
+ xdg-basedir "^2.0.0"
+
+configstore@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1"
+ dependencies:
+ dot-prop "^3.0.0"
+ graceful-fs "^4.1.2"
+ mkdirp "^0.5.0"
+ object-assign "^4.0.1"
+ os-tmpdir "^1.0.0"
+ osenv "^0.1.0"
+ uuid "^2.0.1"
+ write-file-atomic "^1.1.2"
+ xdg-basedir "^2.0.0"
+
+core-util-is@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+create-error-class@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
+ dependencies:
+ capture-stack-trace "^1.0.0"
+
+cross-spawn@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
+ dependencies:
+ lru-cache "^4.0.1"
+ which "^1.2.9"
+
+cross-spawn@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41"
+ dependencies:
+ lru-cache "^4.0.1"
+ which "^1.2.9"
+
+cryptiles@2.x.x:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
+ dependencies:
+ boom "2.x.x"
+
+css-select@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0"
+ dependencies:
+ boolbase "~1.0.0"
+ css-what "1.0"
+ domutils "1.4"
+ nth-check "~1.0.0"
+
+css-select@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+ dependencies:
+ boolbase "~1.0.0"
+ css-what "2.1"
+ domutils "1.5.1"
+ nth-check "~1.0.1"
+
+css-what@1.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c"
+
+css-what@2.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
+
+currently-unhandled@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
+ dependencies:
+ array-find-index "^1.0.1"
+
+dargs@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ dependencies:
+ assert-plus "^1.0.0"
+
+dateformat@^1.0.11:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9"
+ dependencies:
+ get-stdin "^4.0.1"
+ meow "^3.3.0"
+
+debug@^2.0.0, debug@^2.1.0, debug@^2.2.0:
+ version "2.6.6"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.6.tgz#a9fa6fbe9ca43cf1e79f73b75c0189cbb7d6db5a"
+ dependencies:
+ ms "0.7.3"
+
+decamelize@^1.0.0, decamelize@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+
+deep-extend@^0.4.0, deep-extend@~0.4.0:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
+
+deep-is@~0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+
+default-uid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/default-uid/-/default-uid-1.0.0.tgz#fcefa9df9f5ac40c8916d912dd1fe1146aa3c59e"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+
+detect-conflict@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e"
+
+detect-newline@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-1.0.3.tgz#e97b1003877d70c09af1af35bfadff168de4920d"
+ dependencies:
+ get-stdin "^4.0.1"
+ minimist "^1.1.0"
+
+diff@^2.1.2:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99"
+
+discontinuous-range@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a"
+
+dom-serializer@0, dom-serializer@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+ dependencies:
+ domelementtype "~1.1.1"
+ entities "~1.1.1"
+
+domelementtype@1, domelementtype@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+
+domelementtype@~1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
+domhandler@2.3, domhandler@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738"
+ dependencies:
+ domelementtype "1"
+
+domutils@1.4:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f"
+ dependencies:
+ domelementtype "1"
+
+domutils@1.5, domutils@1.5.1, domutils@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
+dot-prop@^2.0.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-2.4.0.tgz#848e28f7f1d50740c6747ab3cb07670462b6f89c"
+ dependencies:
+ is-obj "^1.0.0"
+
+dot-prop@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
+ dependencies:
+ is-obj "^1.0.0"
+
+downgrade-root@^1.0.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/downgrade-root/-/downgrade-root-1.2.2.tgz#531319715b0e81ffcc22eb28478ba27643e12c6c"
+ dependencies:
+ default-uid "^1.0.0"
+ is-root "^1.0.0"
+
+duplexer2@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+ dependencies:
+ readable-stream "^2.0.2"
+
+each-async@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/each-async/-/each-async-1.1.1.tgz#dee5229bdf0ab6ba2012a395e1b869abf8813473"
+ dependencies:
+ onetime "^1.0.0"
+ set-immediate-shim "^1.0.0"
+
+ecc-jsbn@~0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
+ dependencies:
+ jsbn "~0.1.0"
+
+editions@^1.1.1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.3.tgz#0907101bdda20fac3cbe334c27cbd0688dc99a5b"
+
+ejs@2.5.6, ejs@^2.3.1:
+ version "2.5.6"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88"
+
+entities@1.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26"
+
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
+error-ex@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
+ dependencies:
+ is-arrayish "^0.2.1"
+
+error@^7.0.2:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02"
+ dependencies:
+ string-template "~0.2.1"
+ xtend "~4.0.0"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+escodegen-wallaby@^1.6.7:
+ version "1.6.11"
+ resolved "https://registry.yarnpkg.com/escodegen-wallaby/-/escodegen-wallaby-1.6.11.tgz#39100cde743f9acdd24bd868db5a12fbacde6ed1"
+ dependencies:
+ esprima "^2.7.1"
+ estraverse "^1.9.1"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.2.0"
+
+esprima@^2.7.1:
+ version "2.7.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+
+esprima@^3.1.1:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+
+estraverse@^1.9.1:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
+
+esutils@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+
+execall@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73"
+ dependencies:
+ clone-regexp "^1.0.0"
+
+exit-hook@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
+
+extend@^3.0.0, extend@~3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+
+external-editor@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.1.tgz#12d7b0db850f7ff7e7081baf4005700060c4600b"
+ dependencies:
+ extend "^3.0.0"
+ spawn-sync "^1.0.15"
+ tmp "^0.0.29"
+
+extsprintf@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
+
+fast-levenshtein@~2.0.4:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+
+figures@^1.3.5:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
+ dependencies:
+ escape-string-regexp "^1.0.5"
+ object-assign "^4.1.0"
+
+filled-array@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/filled-array/-/filled-array-1.1.0.tgz#c3c4f6c663b923459a9aa29912d2d031f1507f84"
+
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ dependencies:
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+find-versions@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-1.2.1.tgz#cbde9f12e38575a0af1be1b9a2c5d5fd8f186b62"
+ dependencies:
+ array-uniq "^1.0.0"
+ get-stdin "^4.0.1"
+ meow "^3.5.0"
+ semver-regex "^1.0.0"
+
+first-chunk-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70"
+ dependencies:
+ readable-stream "^2.0.2"
+
+foreachasync@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6"
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+
+form-data@~2.1.1:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.12"
+
+formatio@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
+ dependencies:
+ samsam "~1.1"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+fullname@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fullname/-/fullname-2.1.0.tgz#c46bf0f7c3f24fd5b3358d00e4a41380eef87350"
+ dependencies:
+ npmconf "^2.1.1"
+ pify "^2.2.0"
+ pinkie-promise "^2.0.0"
+
+gauge@~1.2.5:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93"
+ dependencies:
+ ansi "^0.3.0"
+ has-unicode "^2.0.0"
+ lodash.pad "^4.1.0"
+ lodash.padend "^4.1.0"
+ lodash.padstart "^4.1.0"
+
+generator-jhipster@4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/generator-jhipster/-/generator-jhipster-4.0.8.tgz#9daff076cc628500726026fad6cfb0a8b55eb1b0"
+ dependencies:
+ chalk "1.1.3"
+ cheerio "0.22.0"
+ ejs "2.5.6"
+ glob "7.1.1"
+ html-wiring "1.2.0"
+ insight "0.8.4"
+ jhipster-core "1.2.8"
+ js-yaml "3.8.1"
+ lodash "4.17.4"
+ mkdirp "0.5.1"
+ pluralize "3.1.0"
+ randexp "0.4.4"
+ semver "5.3.0"
+ shelljs "0.7.6"
+ yeoman-generator "0.24.1"
+ yo "1.8.5"
+
+get-stdin@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ dependencies:
+ assert-plus "^1.0.0"
+
+gh-got@^2.2.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/gh-got/-/gh-got-2.4.0.tgz#aa51418911ca5e4f92437114cd1209383a4aa019"
+ dependencies:
+ got "^5.2.0"
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+
+github-username@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/github-username/-/github-username-2.1.0.tgz#200e5a104af42ba08a54096c708d4b6ec2fa256b"
+ dependencies:
+ gh-got "^2.2.0"
+ meow "^3.5.0"
+
+glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^6.0.1:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globby@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-4.1.0.tgz#080f54549ec1b82a6c60e631fc82e1211dbe95f8"
+ dependencies:
+ array-union "^1.0.1"
+ arrify "^1.0.0"
+ glob "^6.0.1"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+got@^5.0.0, got@^5.2.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35"
+ dependencies:
+ create-error-class "^3.0.1"
+ duplexer2 "^0.1.4"
+ is-redirect "^1.0.0"
+ is-retry-allowed "^1.0.0"
+ is-stream "^1.0.0"
+ lowercase-keys "^1.0.0"
+ node-status-codes "^1.0.0"
+ object-assign "^4.0.1"
+ parse-json "^2.1.0"
+ pinkie-promise "^2.0.0"
+ read-all-stream "^3.0.0"
+ readable-stream "^2.0.5"
+ timed-out "^3.0.0"
+ unzip-response "^1.0.2"
+ url-parse-lax "^1.0.0"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
+ version "4.1.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+
+grouped-queue@^0.3.0:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/grouped-queue/-/grouped-queue-0.3.3.tgz#c167d2a5319c5a0e0964ef6a25b7c2df8996c85c"
+ dependencies:
+ lodash "^4.17.2"
+
+gruntfile-editor@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/gruntfile-editor/-/gruntfile-editor-1.2.1.tgz#366fc1f93cbf045813e1448aef1da9f18289d5eb"
+ dependencies:
+ ast-query "^2.0.0"
+ lodash "^4.6.1"
+
+har-schema@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+
+har-validator@~4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
+ dependencies:
+ ajv "^4.9.1"
+ har-schema "^1.0.5"
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+
+has-unicode@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+
+hawk@~3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
+ dependencies:
+ boom "2.x.x"
+ cryptiles "2.x.x"
+ hoek "2.x.x"
+ sntp "1.x.x"
+
+hoek@2.x.x:
+ version "2.16.3"
+ resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+
+hosted-git-info@^2.1.4:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67"
+
+html-wiring@1.2.0, html-wiring@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/html-wiring/-/html-wiring-1.2.0.tgz#c5f90a776e0a27241dc6df9022c37186d0270f9e"
+ dependencies:
+ cheerio "^0.19.0"
+ detect-newline "^1.0.3"
+
+htmlparser2@^3.9.1:
+ version "3.9.2"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+ dependencies:
+ domelementtype "^1.3.0"
+ domhandler "^2.3.0"
+ domutils "^1.5.1"
+ entities "^1.1.1"
+ inherits "^2.0.1"
+ readable-stream "^2.0.2"
+
+htmlparser2@~3.8.1:
+ version "3.8.3"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068"
+ dependencies:
+ domelementtype "1"
+ domhandler "2.3"
+ domutils "1.5"
+ entities "1.0"
+ readable-stream "1.1"
+
+http-signature@~1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
+ dependencies:
+ assert-plus "^0.2.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+humanize-string@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/humanize-string/-/humanize-string-1.0.1.tgz#fce2d6c545efc25dea1f23235182c98da0180b42"
+ dependencies:
+ decamelize "^1.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+
+indent-string@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
+ dependencies:
+ repeating "^2.0.0"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
+inherits@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+
+ini@^1.2.0, ini@^1.3.4, ini@~1.3.0:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+
+inquirer@^0.10.0:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.10.1.tgz#ea25e4ce69ca145e05c99e46dcfec05e4012594a"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ ansi-regex "^2.0.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^1.0.1"
+ run-async "^0.1.0"
+ rx-lite "^3.1.2"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+inquirer@^0.11.0:
+ version "0.11.4"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.11.4.tgz#81e3374e8361beaff2d97016206d359d0b32fa4d"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ ansi-regex "^2.0.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^1.0.1"
+ run-async "^0.1.0"
+ rx-lite "^3.1.2"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+inquirer@^1.0.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^2.0.0"
+ external-editor "^1.1.0"
+ figures "^1.3.5"
+ lodash "^4.3.0"
+ mute-stream "0.0.6"
+ pinkie-promise "^2.0.0"
+ run-async "^2.2.0"
+ rx "^4.1.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+insight@0.8.4:
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/insight/-/insight-0.8.4.tgz#671caf65b47c9fe8c3d1b3206cf45bb211b75884"
+ dependencies:
+ async "^1.4.2"
+ chalk "^1.0.0"
+ configstore "^1.0.0"
+ inquirer "^0.10.0"
+ lodash.debounce "^3.0.1"
+ object-assign "^4.0.1"
+ os-name "^1.0.0"
+ request "^2.74.0"
+ tough-cookie "^2.0.0"
+ uuid "^3.0.0"
+
+insight@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/insight/-/insight-0.7.0.tgz#061f9189835bd38a97a60c2b76ea0c6b30099ff6"
+ dependencies:
+ async "^1.4.2"
+ chalk "^1.0.0"
+ configstore "^1.0.0"
+ inquirer "^0.10.0"
+ lodash.debounce "^3.0.1"
+ object-assign "^4.0.1"
+ os-name "^1.0.0"
+ request "^2.40.0"
+ tough-cookie "^2.0.0"
+
+interpret@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+
+is-builtin-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+ dependencies:
+ builtin-modules "^1.0.0"
+
+is-docker@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-1.1.0.tgz#f04374d4eee5310e9a8e113bf1495411e46176a1"
+
+is-finite@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-npm@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4"
+
+is-obj@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+
+is-promise@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
+is-redirect@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
+
+is-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
+
+is-retry-allowed@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
+
+is-root@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-root/-/is-root-1.0.0.tgz#07b6c233bc394cd9d02ba15c966bd6660d6342d5"
+
+is-stream@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+
+is-supported-regexp-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz#8b520c85fae7a253382d4b02652e045576e13bb8"
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+
+is-utf8@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+
+istextorbinary@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.1.0.tgz#dbed2a6f51be2f7475b68f89465811141b758874"
+ dependencies:
+ binaryextensions "1 || 2"
+ editions "^1.1.1"
+ textextensions "1 || 2"
+
+jhipster-core@1.2.8:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/jhipster-core/-/jhipster-core-1.2.8.tgz#2211a468761a7132c5ddc7f101400e2fc57d5be4"
+ dependencies:
+ lodash "4.17.4"
+
+jodid25519@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
+ dependencies:
+ jsbn "~0.1.0"
+
+js-yaml@3.8.1:
+ version "3.8.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628"
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^3.1.1"
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+
+json-schema@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+
+json-stable-stringify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+ dependencies:
+ jsonify "~0.0.0"
+
+json-stringify-safe@~5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
+jsprim@^1.2.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918"
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.0.2"
+ json-schema "0.2.3"
+ verror "1.3.6"
+
+latest-version@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-2.0.0.tgz#56f8d6139620847b8017f8f1f4d78e211324168b"
+ dependencies:
+ package-json "^2.0.0"
+
+levn@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ dependencies:
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+
+load-json-file@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+
+lodash._getnative@^3.0.0:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
+lodash.assignin@^4.0.9:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
+
+lodash.bind@^4.1.4:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
+
+lodash.debounce@^3.0.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-3.1.1.tgz#812211c378a94cc29d5aa4e3346cf0bfce3a7df5"
+ dependencies:
+ lodash._getnative "^3.0.0"
+
+lodash.defaults@^4.0.1:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+
+lodash.filter@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
+
+lodash.flatten@^4.2.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+
+lodash.foreach@^4.3.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
+
+lodash.map@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
+
+lodash.merge@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
+
+lodash.pad@^4.1.0:
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70"
+
+lodash.padend@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e"
+
+lodash.padstart@^4.1.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b"
+
+lodash.pick@^4.2.1:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
+
+lodash.reduce@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
+
+lodash.reject@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
+
+lodash.some@^4.4.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
+
+lodash@4.17.4, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.3.0, lodash@^4.6.1:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
+lodash@^3.2.0, lodash@^3.3.1:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+
+log-symbols@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
+ dependencies:
+ chalk "^1.0.0"
+
+lolex@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31"
+
+loud-rejection@^1.0.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
+ dependencies:
+ currently-unhandled "^0.4.1"
+ signal-exit "^3.0.0"
+
+lowercase-keys@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
+
+lru-cache@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e"
+ dependencies:
+ pseudomap "^1.0.1"
+ yallist "^2.0.0"
+
+map-obj@^1.0.0, map-obj@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
+
+mem-fs-editor@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-2.3.0.tgz#42a0ae1f55e76fd03f09e7c7b15b6307bdf5cb13"
+ dependencies:
+ commondir "^1.0.1"
+ deep-extend "^0.4.0"
+ ejs "^2.3.1"
+ glob "^7.0.3"
+ globby "^4.0.0"
+ mkdirp "^0.5.0"
+ multimatch "^2.0.0"
+ rimraf "^2.2.8"
+ through2 "^2.0.0"
+ vinyl "^1.1.0"
+
+mem-fs@^1.1.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/mem-fs/-/mem-fs-1.1.3.tgz#b8ae8d2e3fcb6f5d3f9165c12d4551a065d989cc"
+ dependencies:
+ through2 "^2.0.0"
+ vinyl "^1.1.0"
+ vinyl-file "^2.0.0"
+
+meow@^3.0.0, meow@^3.3.0, meow@^3.5.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
+ dependencies:
+ camelcase-keys "^2.0.0"
+ decamelize "^1.1.2"
+ loud-rejection "^1.0.0"
+ map-obj "^1.0.1"
+ minimist "^1.1.3"
+ normalize-package-data "^2.3.4"
+ object-assign "^4.0.1"
+ read-pkg-up "^1.0.1"
+ redent "^1.0.0"
+ trim-newlines "^1.0.0"
+
+mime-db@~1.27.0:
+ version "1.27.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
+
+mime-types@^2.1.12, mime-types@~2.1.7:
+ version "2.1.15"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
+ dependencies:
+ mime-db "~1.27.0"
+
+"minimatch@2 || 3", minimatch@3.0.x, minimatch@^3.0.0, minimatch@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
+ dependencies:
+ brace-expansion "^1.0.0"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+
+mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ dependencies:
+ minimist "0.0.8"
+
+ms@0.7.3:
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff"
+
+multimatch@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b"
+ dependencies:
+ array-differ "^1.0.0"
+ array-union "^1.0.1"
+ arrify "^1.0.0"
+ minimatch "^3.0.0"
+
+mute-stream@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
+
+mute-stream@0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"
+
+node-status-codes@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f"
+
+nopt@^3.0.0, nopt@~3.0.1:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+ dependencies:
+ abbrev "1"
+
+normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb"
+ dependencies:
+ hosted-git-info "^2.1.4"
+ is-builtin-module "^1.0.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+npm-keyword@^4.1.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/npm-keyword/-/npm-keyword-4.2.0.tgz#98ffebfdbb1336f27ef5fe1baca0dcacd0acf6c0"
+ dependencies:
+ got "^5.0.0"
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+ registry-url "^3.0.3"
+
+npmconf@^2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/npmconf/-/npmconf-2.1.2.tgz#66606a4a736f1e77a059aa071a79c94ab781853a"
+ dependencies:
+ config-chain "~1.1.8"
+ inherits "~2.0.0"
+ ini "^1.2.0"
+ mkdirp "^0.5.0"
+ nopt "~3.0.1"
+ once "~1.3.0"
+ osenv "^0.1.0"
+ semver "2 || 3 || 4"
+ uid-number "0.0.5"
+
+npmlog@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-2.0.4.tgz#98b52530f2514ca90d09ec5b22c8846722375692"
+ dependencies:
+ ansi "~0.3.1"
+ are-we-there-yet "~1.1.2"
+ gauge "~1.2.5"
+
+nth-check@~1.0.0, nth-check@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
+ dependencies:
+ boolbase "~1.0.0"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+
+oauth-sign@~0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+
+object-assign@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa"
+
+object-assign@^4.0.1, object-assign@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+
+object-values@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/object-values/-/object-values-1.0.0.tgz#72af839630119e5b98c3b02bb8c27e3237158105"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ dependencies:
+ wrappy "1"
+
+once@~1.3.0:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20"
+ dependencies:
+ wrappy "1"
+
+onetime@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
+
+opn@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-3.0.3.tgz#b6d99e7399f78d65c3baaffef1fb288e9b85243a"
+ dependencies:
+ object-assign "^4.0.1"
+
+optionator@^0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+ dependencies:
+ deep-is "~0.1.3"
+ fast-levenshtein "~2.0.4"
+ levn "~0.3.0"
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+ wordwrap "~1.0.0"
+
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+
+os-name@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/os-name/-/os-name-1.0.3.tgz#1b379f64835af7c5a7f498b357cb95215c159edf"
+ dependencies:
+ osx-release "^1.0.0"
+ win-release "^1.0.0"
+
+os-shim@^0.1.2:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917"
+
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
+osenv@^0.1.0:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.0"
+
+osx-release@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/osx-release/-/osx-release-1.1.0.tgz#f217911a28136949af1bf9308b241e2737d3cd6c"
+ dependencies:
+ minimist "^1.1.0"
+
+package-json@^2.0.0, package-json@^2.1.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb"
+ dependencies:
+ got "^5.0.0"
+ registry-auth-token "^3.0.1"
+ registry-url "^3.0.3"
+ semver "^5.1.0"
+
+pad-component@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/pad-component/-/pad-component-0.0.1.tgz#ad1f22ce1bf0fdc0d6ddd908af17f351a404b8ac"
+
+parse-help@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/parse-help/-/parse-help-0.1.1.tgz#2f4df942e77a5581bba9967c0c3f48e4c66d7dda"
+ dependencies:
+ execall "^1.0.0"
+
+parse-json@^2.1.0, parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ dependencies:
+ error-ex "^1.2.0"
+
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ dependencies:
+ pinkie-promise "^2.0.0"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+path-parse@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+
+path-type@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+performance-now@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+
+pify@^2.0.0, pify@^2.2.0, pify@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+
+pinkie-promise@^2.0.0, pinkie-promise@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+
+pluralize@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-3.1.0.tgz#84213d0a12356069daa84060c559242633161368"
+
+prelude-ls@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+
+prepend-http@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
+
+pretty-bytes@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-3.0.1.tgz#27d0008d778063a0b4811bb35c79f1bd5d5fbccf"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+process-nextick-args@~1.0.6:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+
+proto-list@~1.2.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+
+pseudomap@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+
+punycode@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
+qs@~6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+
+randexp@0.4.4:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.4.tgz#ba68265f4a9f9e85f5814d34e160291f939f168e"
+ dependencies:
+ discontinuous-range "1.0.0"
+ ret "~0.1.10"
+
+rc@^1.0.1, rc@^1.1.6:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
+ dependencies:
+ deep-extend "~0.4.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+read-all-stream@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa"
+ dependencies:
+ pinkie-promise "^2.0.0"
+ readable-stream "^2.0.0"
+
+read-chunk@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194"
+
+read-pkg-up@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+ dependencies:
+ find-up "^1.0.0"
+ read-pkg "^1.0.0"
+
+read-pkg@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+ dependencies:
+ load-json-file "^1.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^1.0.0"
+
+readable-stream@1.1:
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "0.0.1"
+ string_decoder "~0.10.x"
+
+readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2:
+ version "2.2.9"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
+ dependencies:
+ buffer-shims "~1.0.0"
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "~1.0.0"
+ process-nextick-args "~1.0.6"
+ string_decoder "~1.0.0"
+ util-deprecate "~1.0.1"
+
+readline2@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35"
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ mute-stream "0.0.5"
+
+rechoir@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
+ dependencies:
+ resolve "^1.1.6"
+
+redent@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
+ dependencies:
+ indent-string "^2.1.0"
+ strip-indent "^1.0.1"
+
+registry-auth-token@^3.0.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006"
+ dependencies:
+ rc "^1.1.6"
+ safe-buffer "^5.0.1"
+
+registry-url@^3.0.3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
+ dependencies:
+ rc "^1.0.1"
+
+repeating@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
+ dependencies:
+ is-finite "^1.0.0"
+
+replace-ext@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924"
+
+request@^2.40.0, request@^2.74.0:
+ version "2.81.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
+ dependencies:
+ aws-sign2 "~0.6.0"
+ aws4 "^1.2.1"
+ caseless "~0.12.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.0"
+ forever-agent "~0.6.1"
+ form-data "~2.1.1"
+ har-validator "~4.2.1"
+ hawk "~3.1.3"
+ http-signature "~1.1.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.7"
+ oauth-sign "~0.8.1"
+ performance-now "^0.2.0"
+ qs "~6.4.0"
+ safe-buffer "^5.0.1"
+ stringstream "~0.0.4"
+ tough-cookie "~2.3.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.0.0"
+
+resolve@^1.1.6:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
+ dependencies:
+ path-parse "^1.0.5"
+
+restore-cursor@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
+ dependencies:
+ exit-hook "^1.0.0"
+ onetime "^1.0.0"
+
+ret@~0.1.10:
+ version "0.1.14"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.14.tgz#58c636837b12e161f8a380cf081c6a230fd1664e"
+
+rimraf@^2.2.0, rimraf@^2.2.8, rimraf@^2.4.4:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
+ dependencies:
+ glob "^7.0.5"
+
+root-check@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/root-check/-/root-check-1.0.0.tgz#c52a794bf0db9fad567536e41898f0c9e0a86697"
+ dependencies:
+ downgrade-root "^1.0.0"
+ sudo-block "^1.1.0"
+
+run-async@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389"
+ dependencies:
+ once "^1.3.0"
+
+run-async@^2.0.0, run-async@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+ dependencies:
+ is-promise "^2.1.0"
+
+rx-lite@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
+
+rx@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
+safe-buffer@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+
+samsam@1.1.2, samsam@~1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567"
+
+semver-diff@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
+ dependencies:
+ semver "^5.0.3"
+
+semver-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-1.0.0.tgz#92a4969065f9c70c694753d55248fc68f8f652c9"
+
+semver-truncate@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-1.1.2.tgz#57f41de69707a62709a7e0104ba2117109ea47e8"
+ dependencies:
+ semver "^5.3.0"
+
+"semver@2 || 3 || 4", semver@^4.0.3:
+ version "4.3.6"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
+
+"semver@2 || 3 || 4 || 5", semver@5.3.0, semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+
+set-immediate-shim@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+
+shelljs@0.7.6, shelljs@^0.7.0:
+ version "0.7.6"
+ resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad"
+ dependencies:
+ glob "^7.0.0"
+ interpret "^1.0.0"
+ rechoir "^0.6.2"
+
+signal-exit@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+
+sinon@^1.17.2:
+ version "1.17.7"
+ resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf"
+ dependencies:
+ formatio "1.1.1"
+ lolex "1.3.2"
+ samsam "1.1.2"
+ util ">=0.10.3 <1"
+
+slide@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
+
+sntp@1.x.x:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
+ dependencies:
+ hoek "2.x.x"
+
+sort-on@^1.0.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/sort-on/-/sort-on-1.3.0.tgz#0dfd5b364b23df7f2acd86985daeb889e1a7c840"
+ dependencies:
+ arrify "^1.0.0"
+ dot-prop "^2.0.0"
+
+source-map@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
+ dependencies:
+ amdefine ">=0.0.4"
+
+spawn-sync@^1.0.15:
+ version "1.0.15"
+ resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
+ dependencies:
+ concat-stream "^1.4.7"
+ os-shim "^0.1.2"
+
+spdx-correct@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+ dependencies:
+ spdx-license-ids "^1.0.2"
+
+spdx-expression-parse@~1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
+
+spdx-license-ids@^1.0.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
+
+sprintf-js@^1.0.3, sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+
+sshpk@^1.7.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ dashdash "^1.12.0"
+ getpass "^0.1.1"
+ optionalDependencies:
+ bcrypt-pbkdf "^1.0.0"
+ ecc-jsbn "~0.1.1"
+ jodid25519 "^1.0.0"
+ jsbn "~0.1.0"
+ tweetnacl "~0.14.0"
+
+string-length@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
+ dependencies:
+ strip-ansi "^3.0.0"
+
+string-template@~0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
+
+string-width@^1.0.0, string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+string_decoder@~0.10.x:
+ version "0.10.31"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
+
+string_decoder@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667"
+ dependencies:
+ buffer-shims "~1.0.0"
+
+stringstream@~0.0.4:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-bom-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca"
+ dependencies:
+ first-chunk-stream "^2.0.0"
+ strip-bom "^2.0.0"
+
+strip-bom@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+ dependencies:
+ is-utf8 "^0.2.0"
+
+strip-indent@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
+ dependencies:
+ get-stdin "^4.0.1"
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
+sudo-block@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/sudo-block/-/sudo-block-1.2.0.tgz#cc539bf8191624d4f507d83eeb45b4cea27f3463"
+ dependencies:
+ chalk "^1.0.0"
+ is-docker "^1.0.0"
+ is-root "^1.0.0"
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^3.1.2:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+ dependencies:
+ has-flag "^1.0.0"
+
+tabtab@^1.3.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/tabtab/-/tabtab-1.3.2.tgz#bb9c2ca6324f659fde7634c2caf3c096e1187ca7"
+ dependencies:
+ debug "^2.2.0"
+ inquirer "^1.0.2"
+ minimist "^1.2.0"
+ mkdirp "^0.5.1"
+ npmlog "^2.0.3"
+ object-assign "^4.1.0"
+
+taketalk@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/taketalk/-/taketalk-1.0.0.tgz#b4d4f0deed206ae7df775b129ea2ca6de52f26dd"
+ dependencies:
+ get-stdin "^4.0.1"
+ minimist "^1.1.0"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+
+"textextensions@1 || 2":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.1.0.tgz#1be0dc2a0dc244d44be8a09af6a85afb93c4dbc3"
+
+through2@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
+ dependencies:
+ readable-stream "^2.1.5"
+ xtend "~4.0.1"
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+
+timed-out@^3.0.0:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217"
+
+titleize@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/titleize/-/titleize-1.0.0.tgz#7d350722061830ba6617631e0cfd3ea08398d95a"
+
+tmp@^0.0.29:
+ version "0.0.29"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0"
+ dependencies:
+ os-tmpdir "~1.0.1"
+
+tough-cookie@^2.0.0, tough-cookie@~2.3.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
+ dependencies:
+ punycode "^1.4.1"
+
+traverse@^0.6.6:
+ version "0.6.6"
+ resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
+
+trim-newlines@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+
+twig@^0.8.2:
+ version "0.8.9"
+ resolved "https://registry.yarnpkg.com/twig/-/twig-0.8.9.tgz#b1594f002b684e5f029de3e54e87bec4f084b6c2"
+ dependencies:
+ minimatch "3.0.x"
+ walk "2.3.x"
+
+type-check@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ dependencies:
+ prelude-ls "~1.1.2"
+
+typedarray@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+
+uid-number@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.5.tgz#5a3db23ef5dbd55b81fce0ec9a2ac6fccdebb81e"
+
+underscore.string@^3.0.3:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db"
+ dependencies:
+ sprintf-js "^1.0.3"
+ util-deprecate "^1.0.2"
+
+untildify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0"
+ dependencies:
+ os-homedir "^1.0.0"
+
+unzip-response@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe"
+
+update-notifier@^0.6.0:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.6.3.tgz#776dec8daa13e962a341e8a1d98354306b67ae08"
+ dependencies:
+ boxen "^0.3.1"
+ chalk "^1.0.0"
+ configstore "^2.0.0"
+ is-npm "^1.0.0"
+ latest-version "^2.0.0"
+ semver-diff "^2.0.0"
+
+url-parse-lax@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
+ dependencies:
+ prepend-http "^1.0.1"
+
+user-home@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
+ dependencies:
+ os-homedir "^1.0.0"
+
+util-deprecate@^1.0.2, util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
+"util@>=0.10.3 <1":
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+ dependencies:
+ inherits "2.0.1"
+
+uuid@^2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
+
+uuid@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
+ dependencies:
+ spdx-correct "~1.0.0"
+ spdx-expression-parse "~1.0.0"
+
+verror@1.3.6:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
+ dependencies:
+ extsprintf "1.0.2"
+
+vinyl-file@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a"
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.3.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+ strip-bom-stream "^2.0.0"
+ vinyl "^1.1.0"
+
+vinyl@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884"
+ dependencies:
+ clone "^1.0.0"
+ clone-stats "^0.0.1"
+ replace-ext "0.0.1"
+
+walk@2.3.x:
+ version "2.3.9"
+ resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.9.tgz#31b4db6678f2ae01c39ea9fb8725a9031e558a7b"
+ dependencies:
+ foreachasync "^3.0.0"
+
+which@^1.2.9:
+ version "1.2.14"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5"
+ dependencies:
+ isexe "^2.0.0"
+
+widest-line@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c"
+ dependencies:
+ string-width "^1.0.1"
+
+win-release@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/win-release/-/win-release-1.1.1.tgz#5fa55e02be7ca934edfc12665632e849b72e5209"
+ dependencies:
+ semver "^5.0.1"
+
+wordwrap@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+
+write-file-atomic@^1.1.2:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f"
+ dependencies:
+ graceful-fs "^4.1.11"
+ imurmurhash "^0.1.4"
+ slide "^1.1.5"
+
+xdg-basedir@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2"
+ dependencies:
+ os-homedir "^1.0.0"
+
+xtend@~4.0.0, xtend@~4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+
+yallist@^2.0.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+
+yeoman-assert@^2.0.0:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/yeoman-assert/-/yeoman-assert-2.2.3.tgz#a5682a83632c50ac0ee84173a5a10fd6f3206474"
+
+yeoman-character@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/yeoman-character/-/yeoman-character-1.1.0.tgz#90d4b5beaf92759086177015b2fdfa2e0684d7c7"
+ dependencies:
+ supports-color "^3.1.2"
+
+yeoman-doctor@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/yeoman-doctor/-/yeoman-doctor-2.1.0.tgz#94ab784896a64f53a9fac452d5e9133e2750a236"
+ dependencies:
+ bin-version-check "^2.1.0"
+ chalk "^1.0.0"
+ each-async "^1.1.1"
+ log-symbols "^1.0.1"
+ object-values "^1.0.0"
+ semver "^5.0.3"
+ twig "^0.8.2"
+ user-home "^2.0.0"
+
+yeoman-environment@^1.1.0, yeoman-environment@^1.5.2, yeoman-environment@^1.6.1:
+ version "1.6.6"
+ resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-1.6.6.tgz#cd85fa67d156060e440d7807d7ef7cf0d2d1d671"
+ dependencies:
+ chalk "^1.0.0"
+ debug "^2.0.0"
+ diff "^2.1.2"
+ escape-string-regexp "^1.0.2"
+ globby "^4.0.0"
+ grouped-queue "^0.3.0"
+ inquirer "^1.0.2"
+ lodash "^4.11.1"
+ log-symbols "^1.0.1"
+ mem-fs "^1.1.0"
+ text-table "^0.2.0"
+ untildify "^2.0.0"
+
+yeoman-generator@0.24.1, yeoman-generator@^0.24.1:
+ version "0.24.1"
+ resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-0.24.1.tgz#1ca74429d9c5c95db0b22859ec180a2599bc1f8e"
+ dependencies:
+ async "^2.0.0"
+ chalk "^1.0.0"
+ class-extend "^0.1.0"
+ cli-table "^0.3.1"
+ cross-spawn "^4.0.0"
+ dargs "^4.0.0"
+ dateformat "^1.0.11"
+ debug "^2.1.0"
+ detect-conflict "^1.0.0"
+ error "^7.0.2"
+ find-up "^1.0.0"
+ github-username "^2.0.0"
+ glob "^7.0.3"
+ gruntfile-editor "^1.0.0"
+ html-wiring "^1.0.0"
+ istextorbinary "^2.1.0"
+ lodash "^4.11.1"
+ mem-fs-editor "^2.0.0"
+ mkdirp "^0.5.0"
+ nopt "^3.0.0"
+ path-exists "^2.0.0"
+ path-is-absolute "^1.0.0"
+ pretty-bytes "^3.0.1"
+ read-chunk "^1.0.0"
+ read-pkg-up "^1.0.1"
+ rimraf "^2.2.0"
+ run-async "^2.0.0"
+ shelljs "^0.7.0"
+ text-table "^0.2.0"
+ through2 "^2.0.0"
+ underscore.string "^3.0.3"
+ user-home "^2.0.0"
+ yeoman-assert "^2.0.0"
+ yeoman-environment "^1.1.0"
+ yeoman-test "^1.0.0"
+ yeoman-welcome "^1.0.0"
+
+yeoman-test@^1.0.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/yeoman-test/-/yeoman-test-1.6.0.tgz#abff291733e16e8923d9eefc8691c632888bf948"
+ dependencies:
+ inquirer "^1.0.2"
+ lodash "^4.3.0"
+ mkdirp "^0.5.1"
+ pinkie-promise "^2.0.1"
+ rimraf "^2.4.4"
+ sinon "^1.17.2"
+ yeoman-environment "^1.5.2"
+ yeoman-generator "^0.24.1"
+
+yeoman-welcome@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/yeoman-welcome/-/yeoman-welcome-1.0.1.tgz#f6cf198fd4fba8a771672c26cdfb8a64795c84ec"
+ dependencies:
+ chalk "^1.0.0"
+
+yo@1.8.5:
+ version "1.8.5"
+ resolved "https://registry.yarnpkg.com/yo/-/yo-1.8.5.tgz#776ab9ec79a7882f8d4f7a9e10214fdab050d928"
+ dependencies:
+ async "^1.0.0"
+ chalk "^1.0.0"
+ cli-list "^0.1.1"
+ configstore "^1.0.0"
+ cross-spawn "^3.0.1"
+ figures "^1.3.5"
+ fullname "^2.0.0"
+ got "^5.0.0"
+ humanize-string "^1.0.0"
+ inquirer "^0.11.0"
+ insight "^0.7.0"
+ lodash "^3.2.0"
+ meow "^3.0.0"
+ npm-keyword "^4.1.0"
+ opn "^3.0.2"
+ package-json "^2.1.0"
+ parse-help "^0.1.1"
+ read-pkg-up "^1.0.1"
+ repeating "^2.0.0"
+ root-check "^1.0.0"
+ sort-on "^1.0.0"
+ string-length "^1.0.0"
+ tabtab "^1.3.0"
+ titleize "^1.0.0"
+ update-notifier "^0.6.0"
+ user-home "^2.0.0"
+ yeoman-character "^1.0.0"
+ yeoman-doctor "^2.0.0"
+ yeoman-environment "^1.6.1"
+ yosay "^1.0.0"
+
+yosay@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/yosay/-/yosay-1.2.1.tgz#9466ef969830e85b474e267b50f7688693ed3b5b"
+ dependencies:
+ ansi-regex "^2.0.0"
+ ansi-styles "^2.0.0"
+ chalk "^1.0.0"
+ cli-boxes "^1.0.0"
+ pad-component "0.0.1"
+ repeating "^2.0.0"
+ string-width "^1.0.0"
+ strip-ansi "^3.0.0"
+ taketalk "^1.0.0"
+ wrap-ansi "^2.0.0"
diff --git a/jhipster/jhipster-microservice/gateway-app/.bowerrc b/jhipster/jhipster-microservice/gateway-app/.bowerrc
new file mode 100644
index 0000000000..ad10799262
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.bowerrc
@@ -0,0 +1,3 @@
+{
+ "directory": "src/main/webapp/bower_components"
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/.editorconfig b/jhipster/jhipster-microservice/gateway-app/.editorconfig
new file mode 100644
index 0000000000..a03599dd04
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.editorconfig
@@ -0,0 +1,24 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 4
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[{package,bower}.json]
+indent_style = space
+indent_size = 2
diff --git a/jhipster/jhipster-microservice/gateway-app/.eslintignore b/jhipster/jhipster-microservice/gateway-app/.eslintignore
new file mode 100644
index 0000000000..047e14a334
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.eslintignore
@@ -0,0 +1,6 @@
+# more info here - http://eslint.org/docs/user-guide/configuring.html#ignoring-files-and-directories
+
+# node_modules ignored by default
+
+# ignore bower_components
+src/main/webapp/bower_components
diff --git a/jhipster/jhipster-microservice/gateway-app/.eslintrc.json b/jhipster/jhipster-microservice/gateway-app/.eslintrc.json
new file mode 100644
index 0000000000..50047b2bc0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.eslintrc.json
@@ -0,0 +1,29 @@
+// See: http://eslint.org/
+// See: https://www.npmjs.com/package/eslint-plugin-angular
+{
+ "extends": [
+ "eslint:recommended",
+ "angular"
+ ],
+ "env": {
+ "node": true,
+ "browser": true
+ },
+ // severity for a rule should be one of the following: 0 = off, 1 = warning, 2 = error
+ "rules": {
+ // coding style
+ "wrap-iife": [2, "inside"],
+ "eqeqeq": 2,
+ "no-use-before-define": [2, "nofunc"],
+ "no-unused-vars": [2, {"vars": "local", "args": "none"}],
+ "no-multi-str": 2,
+ "no-irregular-whitespace": 2,
+ "semi": [2, "always"],
+ "indent": 2,
+
+ // os/git options
+ // we want to run on all OSes
+ "linebreak-style": 0,
+ "eol-last": 2
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/.gitattributes b/jhipster/jhipster-microservice/gateway-app/.gitattributes
new file mode 100644
index 0000000000..2a13efa88f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.gitattributes
@@ -0,0 +1,22 @@
+# All text files should have the "lf" (Unix) line endings
+* text eol=lf
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.java text
+*.js text
+*.css text
+*.html text
+
+# Denote all files that are truly binary and should not be modified.
+*.png binary
+*.jpg binary
+*.jar binary
+*.pdf binary
+*.eot binary
+*.ttf binary
+*.gzip binary
+*.gz binary
+*.ai binary
+*.eps binary
+*.swf binary
diff --git a/jhipster/jhipster-microservice/gateway-app/.gitignore b/jhipster/jhipster-microservice/gateway-app/.gitignore
new file mode 100644
index 0000000000..74b29e2042
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.gitignore
@@ -0,0 +1,141 @@
+######################
+# Project Specific
+######################
+/target/www/**
+/src/test/javascript/coverage/
+/src/test/javascript/PhantomJS*/
+
+######################
+# Node
+######################
+/node/
+node_tmp/
+node_modules/
+npm-debug.log.*
+
+######################
+# SASS
+######################
+.sass-cache/
+
+######################
+# Eclipse
+######################
+*.pydevproject
+.project
+.metadata
+tmp/
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+.factorypath
+/src/main/resources/rebel.xml
+
+# External tool builders
+.externalToolBuilders/**
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+######################
+# Intellij
+######################
+.idea/
+*.iml
+*.iws
+*.ipr
+*.ids
+*.orig
+
+######################
+# Visual Studio Code
+######################
+.vscode/
+
+######################
+# Maven
+######################
+/log/
+/target/
+
+######################
+# Gradle
+######################
+.gradle/
+/build/
+
+######################
+# Package Files
+######################
+*.jar
+*.war
+*.ear
+*.db
+
+######################
+# Windows
+######################
+# Windows image file caches
+Thumbs.db
+
+# Folder config file
+Desktop.ini
+
+######################
+# Mac OSX
+######################
+.DS_Store
+.svn
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+######################
+# Directories
+######################
+/bin/
+/deploy/
+
+######################
+# Logs
+######################
+*.log
+
+######################
+# Others
+######################
+*.class
+*.*~
+*~
+.merge_file*
+
+######################
+# Gradle Wrapper
+######################
+!gradle/wrapper/gradle-wrapper.jar
+
+######################
+# Maven Wrapper
+######################
+!.mvn/wrapper/maven-wrapper.jar
+
+######################
+# ESLint
+######################
+.eslintcache
diff --git a/jhipster/jhipster-microservice/gateway-app/.jhipster/Car.json b/jhipster/jhipster-microservice/gateway-app/.jhipster/Car.json
new file mode 100644
index 0000000000..ceaef3cd63
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.jhipster/Car.json
@@ -0,0 +1,25 @@
+{
+ "fluentMethods": true,
+ "relationships": [],
+ "fields": [
+ {
+ "fieldName": "make",
+ "fieldType": "String"
+ },
+ {
+ "fieldName": "brand",
+ "fieldType": "String"
+ },
+ {
+ "fieldName": "price",
+ "fieldType": "Double"
+ }
+ ],
+ "changelogDate": "20170503041524",
+ "dto": "no",
+ "service": "no",
+ "entityTableName": "car",
+ "pagination": "infinite-scroll",
+ "microserviceName": "carapp",
+ "searchEngine": false
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/.jhipster/Dealer.json b/jhipster/jhipster-microservice/gateway-app/.jhipster/Dealer.json
new file mode 100644
index 0000000000..e9b4a8112e
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.jhipster/Dealer.json
@@ -0,0 +1,21 @@
+{
+ "fluentMethods": true,
+ "relationships": [],
+ "fields": [
+ {
+ "fieldName": "name",
+ "fieldType": "String"
+ },
+ {
+ "fieldName": "address",
+ "fieldType": "String"
+ }
+ ],
+ "changelogDate": "20170503044952",
+ "dto": "no",
+ "service": "no",
+ "entityTableName": "dealer",
+ "pagination": "infinite-scroll",
+ "microserviceName": "dealerapp",
+ "searchEngine": false
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/.mvn/wrapper/maven-wrapper.jar b/jhipster/jhipster-microservice/gateway-app/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000..5fd4d5023f
Binary files /dev/null and b/jhipster/jhipster-microservice/gateway-app/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/jhipster/jhipster-microservice/gateway-app/.mvn/wrapper/maven-wrapper.properties b/jhipster/jhipster-microservice/gateway-app/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..c954cec91c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
diff --git a/jhipster/jhipster-microservice/gateway-app/.yo-rc.json b/jhipster/jhipster-microservice/gateway-app/.yo-rc.json
new file mode 100644
index 0000000000..c6e9540740
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/.yo-rc.json
@@ -0,0 +1,29 @@
+{
+ "generator-jhipster": {
+ "jhipsterVersion": "4.0.8",
+ "baseName": "gateway",
+ "packageName": "com.gateway",
+ "packageFolder": "com/gateway",
+ "serverPort": "8080",
+ "authenticationType": "jwt",
+ "hibernateCache": "hazelcast",
+ "clusteredHttpSession": false,
+ "websocket": false,
+ "databaseType": "sql",
+ "devDatabaseType": "h2Disk",
+ "prodDatabaseType": "mysql",
+ "searchEngine": false,
+ "messageBroker": false,
+ "serviceDiscoveryType": "eureka",
+ "buildTool": "maven",
+ "enableSocialSignIn": false,
+ "jwtSecretKey": "cd875958b5c9a8fa965803fd599da3c13abf1015",
+ "clientFramework": "angular1",
+ "useSass": false,
+ "clientPackageManager": "yarn",
+ "applicationType": "gateway",
+ "testFrameworks": [],
+ "jhiPrefix": "jhi",
+ "enableTranslation": false
+ }
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/README.md b/jhipster/jhipster-microservice/gateway-app/README.md
new file mode 100644
index 0000000000..8b10a5934f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/README.md
@@ -0,0 +1,116 @@
+# gateway
+This application was generated using JHipster 4.0.8, you can find documentation and help at [https://jhipster.github.io/documentation-archive/v4.0.8](https://jhipster.github.io/documentation-archive/v4.0.8).
+
+This is a "gateway" application intended to be part of a microservice architecture, please refer to the [Doing microservices with JHipster][] page of the documentation for more information.
+
+This application is configured for Service Discovery and Configuration with the JHipster-Registry. On launch, it will refuse to start if it is not able to connect to the JHipster-Registry at [http://localhost:8761](http://localhost:8761). For more information, read our documentation on [Service Discovery and Configuration with the JHipster-Registry][].
+
+## Development
+
+Before you can build this project, you must install and configure the following dependencies on your machine:
+
+1. [Node.js][]: We use Node to run a development web server and build the project.
+ Depending on your system, you can install Node either from source or as a pre-packaged bundle.
+2. [Yarn][]: We use Yarn to manage Node dependencies.
+ Depending on your system, you can install Yarn either from source or as a pre-packaged bundle.
+
+After installing Node, you should be able to run the following command to install development tools.
+You will only need to run this command when dependencies change in [package.json](package.json).
+
+ yarn install
+
+We use [Gulp][] as our build system. Install the Gulp command-line tool globally with:
+
+ yarn global add gulp-cli
+
+Run the following commands in two separate terminals to create a blissful development experience where your browser
+auto-refreshes when files change on your hard drive.
+
+ ./mvnw
+ gulp
+
+[Bower][] is used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by
+specifying a newer version in [bower.json](bower.json). You can also run `bower update` and `bower install` to manage dependencies.
+Add the `-h` flag on any command to see how you can use it. For example, `bower update -h`.
+
+For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][].
+
+
+## Building for production
+
+To optimize the gateway application for production, run:
+
+ ./mvnw -Pprod clean package
+
+This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references these new files.
+To ensure everything worked, run:
+
+ java -jar target/*.war
+
+Then navigate to [http://localhost:8080](http://localhost:8080) in your browser.
+
+Refer to [Using JHipster in production][] for more details.
+
+## Testing
+
+To launch your application's tests, run:
+
+ ./mvnw clean test
+
+### Client tests
+
+Unit tests are run by [Karma][] and written with [Jasmine][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with:
+
+ gulp test
+
+
+
+For more information, refer to the [Running tests page][].
+
+## Using Docker to simplify development (optional)
+
+You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services.
+For example, to start a mysql database in a docker container, run:
+
+ docker-compose -f src/main/docker/mysql.yml up -d
+
+To stop it and remove the container, run:
+
+ docker-compose -f src/main/docker/mysql.yml down
+
+You can also fully dockerize your application and all the services that it depends on.
+To achieve this, first build a docker image of your app by running:
+
+ ./mvnw package -Pprod docker:build
+
+Then run:
+
+ docker-compose -f src/main/docker/app.yml up -d
+
+For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`yo jhipster:docker-compose`), which is able to generate docker configurations for one or several JHipster applications.
+
+## Continuous Integration (optional)
+
+To configure CI for your project, run the ci-cd sub-generator (`yo jhipster:ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information.
+
+[JHipster Homepage and latest documentation]: https://jhipster.github.io
+[JHipster 4.0.8 archive]: https://jhipster.github.io/documentation-archive/v4.0.8
+[Doing microservices with JHipster]: https://jhipster.github.io/documentation-archive/v4.0.8/microservices-architecture/
+[Using JHipster in development]: https://jhipster.github.io/documentation-archive/v4.0.8/development/
+[Service Discovery and Configuration with the JHipster-Registry]: https://jhipster.github.io/documentation-archive/v4.0.8/microservices-architecture/#jhipster-registry
+[Using Docker and Docker-Compose]: https://jhipster.github.io/documentation-archive/v4.0.8/docker-compose
+[Using JHipster in production]: https://jhipster.github.io/documentation-archive/v4.0.8/production/
+[Running tests page]: https://jhipster.github.io/documentation-archive/v4.0.8/running-tests/
+[Setting up Continuous Integration]: https://jhipster.github.io/documentation-archive/v4.0.8/setting-up-ci/
+
+
+[Node.js]: https://nodejs.org/
+[Yarn]: https://yarnpkg.org/
+[Bower]: http://bower.io/
+[Gulp]: http://gulpjs.com/
+[BrowserSync]: http://www.browsersync.io/
+[Karma]: http://karma-runner.github.io/
+[Jasmine]: http://jasmine.github.io/2.0/introduction.html
+[Protractor]: https://angular.github.io/protractor/
+[Leaflet]: http://leafletjs.com/
+[DefinitelyTyped]: http://definitelytyped.org/
diff --git a/jhipster/jhipster-microservice/gateway-app/bower.json b/jhipster/jhipster-microservice/gateway-app/bower.json
new file mode 100644
index 0000000000..eedc9790c2
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/bower.json
@@ -0,0 +1,52 @@
+{
+ "version": "0.0.0",
+ "name": "gateway",
+ "appPath": "src/main/webapp/",
+ "testPath": "src/test/javascript/spec",
+ "dependencies": {
+ "angular": "1.5.8",
+ "angular-aria": "1.5.8",
+ "angular-bootstrap": "1.3.3",
+ "angular-cache-buster": "0.4.3",
+ "angular-cookies": "1.5.8",
+ "ngstorage": "0.3.10",
+ "angular-loading-bar": "0.9.0",
+ "angular-resource": "1.5.8",
+ "angular-sanitize": "1.5.8",
+ "angular-ui-router": "0.3.1",
+ "bootstrap": "3.3.7",
+ "bootstrap-ui-datetime-picker": "2.4.3",
+ "jquery": "3.1.0",
+ "json3": "3.3.2",
+ "messageformat": "0.3.1",
+ "modernizr": "3.3.1",
+ "ng-file-upload": "12.0.4",
+ "ngInfiniteScroll": "1.3.0",
+ "swagger-ui": "2.1.5"
+ },
+ "devDependencies": {
+ "angular-mocks": "1.5.8"
+ },
+ "overrides": {
+ "angular": {
+ "dependencies": {
+ "jquery": "3.1.0"
+ }
+ },
+ "angular-cache-buster": {
+ "dependencies": {
+ "angular": "1.5.8"
+ }
+ },
+ "bootstrap": {
+ "main": [
+ "dist/css/bootstrap.css"
+ ]
+ }
+ },
+ "resolutions": {
+ "angular": "1.5.8",
+ "angular-bootstrap": "2.0.0",
+ "jquery": "3.1.0"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/gulp/build.js b/jhipster/jhipster-microservice/gateway-app/gulp/build.js
new file mode 100644
index 0000000000..ecb5c362ce
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulp/build.js
@@ -0,0 +1,51 @@
+'use strict';
+
+var fs = require('fs'),
+ gulp = require('gulp'),
+ lazypipe = require('lazypipe'),
+ footer = require('gulp-footer'),
+ sourcemaps = require('gulp-sourcemaps'),
+ rev = require('gulp-rev'),
+ htmlmin = require('gulp-htmlmin'),
+ ngAnnotate = require('gulp-ng-annotate'),
+ prefix = require('gulp-autoprefixer'),
+ cssnano = require('gulp-cssnano'),
+ uglify = require('gulp-uglify'),
+ useref = require("gulp-useref"),
+ revReplace = require("gulp-rev-replace"),
+ plumber = require('gulp-plumber'),
+ gulpIf = require('gulp-if'),
+ handleErrors = require('./handle-errors');
+
+var config = require('./config');
+
+var initTask = lazypipe()
+ .pipe(sourcemaps.init);
+var jsTask = lazypipe()
+ .pipe(ngAnnotate)
+ .pipe(uglify);
+var cssTask = lazypipe()
+ .pipe(prefix)
+ .pipe(cssnano);
+
+module.exports = function() {
+ var templates = fs.readFileSync(config.tmp + '/templates.js');
+ var manifest = gulp.src(config.revManifest);
+
+ return gulp.src([config.app + '**/*.html',
+ '!' + config.app + 'app/**/*.html',
+ '!' + config.app + 'swagger-ui/**/*',
+ '!' + config.bower + '**/*.html'])
+ .pipe(plumber({errorHandler: handleErrors}))
+ //init sourcemaps and prepend semicolon
+ .pipe(useref({}, initTask))
+ //append html templates
+ .pipe(gulpIf('**/app.js', footer(templates)))
+ .pipe(gulpIf('*.js', jsTask()))
+ .pipe(gulpIf('*.css', cssTask()))
+ .pipe(gulpIf('*.html', htmlmin({collapseWhitespace: true})))
+ .pipe(gulpIf('**/*.!(html)', rev()))
+ .pipe(revReplace({manifest: manifest}))
+ .pipe(sourcemaps.write('.'))
+ .pipe(gulp.dest(config.dist));
+};
diff --git a/jhipster/jhipster-microservice/gateway-app/gulp/config.js b/jhipster/jhipster-microservice/gateway-app/gulp/config.js
new file mode 100644
index 0000000000..49df6e6ddd
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulp/config.js
@@ -0,0 +1,23 @@
+'use strict';
+
+module.exports = {
+ app: 'src/main/webapp/',
+ dist: 'target/www/',
+ swaggerDist: 'target/www/swagger-ui/',
+ test: 'src/test/javascript/',
+ bower: 'src/main/webapp/bower_components/',
+ tmp: 'target/tmp',
+ revManifest: 'target/tmp/rev-manifest.json',
+ port: 9000,
+ apiPort: 8080,
+ liveReloadPort: 35729,
+ uri: 'http://localhost:',
+ constantTemplate:
+ '(function () {\n' +
+ ' \'use strict\';\n' +
+ ' // DO NOT EDIT THIS FILE, EDIT THE GULP TASK NGCONSTANT SETTINGS INSTEAD WHICH GENERATES THIS FILE\n' +
+ ' angular\n' +
+ ' .module(\'<%- moduleName %>\')\n' +
+ '<% constants.forEach(function(constant) { %> .constant(\'<%- constant.name %>\', <%= constant.value %>)\n<% }) %>;\n' +
+ '})();\n'
+};
diff --git a/jhipster/jhipster-microservice/gateway-app/gulp/copy.js b/jhipster/jhipster-microservice/gateway-app/gulp/copy.js
new file mode 100644
index 0000000000..2e6e71ba71
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulp/copy.js
@@ -0,0 +1,81 @@
+'use strict';
+
+var gulp = require('gulp'),
+ rev = require('gulp-rev'),
+ plumber = require('gulp-plumber'),
+ es = require('event-stream'),
+ flatten = require('gulp-flatten'),
+ replace = require('gulp-replace'),
+ bowerFiles = require('main-bower-files'),
+ changed = require('gulp-changed');
+
+var handleErrors = require('./handle-errors');
+var config = require('./config');
+
+module.exports = {
+ fonts: fonts,
+ common: common,
+ swagger: swagger,
+ images: images
+}
+
+function fonts() {
+ return es.merge(gulp.src(config.bower + 'bootstrap/fonts/*.*')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.dist + 'content/fonts/'))
+ .pipe(rev())
+ .pipe(gulp.dest(config.dist + 'content/fonts/'))
+ .pipe(rev.manifest(config.revManifest, {
+ base: config.dist,
+ merge: true
+ }))
+ .pipe(gulp.dest(config.dist)),
+ gulp.src(config.app + 'content/**/*.{woff,woff2,svg,ttf,eot,otf}')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.dist + 'content/fonts/'))
+ .pipe(flatten())
+ .pipe(rev())
+ .pipe(gulp.dest(config.dist + 'content/fonts/'))
+ .pipe(rev.manifest(config.revManifest, {
+ base: config.dist,
+ merge: true
+ }))
+ .pipe(gulp.dest(config.dist))
+ );
+}
+
+function common() {
+ return gulp.src([config.app + 'robots.txt', config.app + 'favicon.ico', config.app + '.htaccess'], { dot: true })
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.dist))
+ .pipe(gulp.dest(config.dist));
+}
+
+function swagger() {
+ return es.merge(
+ gulp.src([config.bower + 'swagger-ui/dist/**',
+ '!' + config.bower + 'swagger-ui/dist/index.html',
+ '!' + config.bower + 'swagger-ui/dist/swagger-ui.min.js',
+ '!' + config.bower + 'swagger-ui/dist/swagger-ui.js'])
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.swaggerDist))
+ .pipe(gulp.dest(config.swaggerDist)),
+ gulp.src(config.app + 'swagger-ui/index.html')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.swaggerDist))
+ .pipe(replace('../bower_components/swagger-ui/dist/', ''))
+ .pipe(replace('swagger-ui.js', 'lib/swagger-ui.min.js'))
+ .pipe(gulp.dest(config.swaggerDist)),
+ gulp.src(config.bower + 'swagger-ui/dist/swagger-ui.min.js')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.swaggerDist + 'lib/'))
+ .pipe(gulp.dest(config.swaggerDist + 'lib/'))
+ );
+}
+
+function images() {
+ return gulp.src(bowerFiles({filter: ['**/*.{gif,jpg,png}']}), { base: config.bower })
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.dist + 'bower_components'))
+ .pipe(gulp.dest(config.dist + 'bower_components'));
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/gulp/handle-errors.js b/jhipster/jhipster-microservice/gateway-app/gulp/handle-errors.js
new file mode 100644
index 0000000000..98c4d4a64c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulp/handle-errors.js
@@ -0,0 +1,22 @@
+'use strict';
+
+var notify = require("gulp-notify");
+var argv = require('yargs').argv;
+
+module.exports = function() {
+
+ var args = Array.prototype.slice.call(arguments);
+ var notification = argv.notification === undefined ? true : argv.notification;
+ // Send error to notification center with gulp-notify
+ if(notification) {
+ notify.onError({
+ title: "JHipster Gulp Build",
+ subtitle: "Failure!",
+ message: "Error: <%= error.message %>",
+ sound: "Beep"
+ }).apply(this, args);
+ }
+ // Keep gulp from hanging on this task
+ this.emit('end');
+
+};
diff --git a/jhipster/jhipster-microservice/gateway-app/gulp/inject.js b/jhipster/jhipster-microservice/gateway-app/gulp/inject.js
new file mode 100644
index 0000000000..1808e31a99
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulp/inject.js
@@ -0,0 +1,69 @@
+'use strict';
+
+var gulp = require('gulp'),
+ plumber = require('gulp-plumber'),
+ inject = require('gulp-inject'),
+ es = require('event-stream'),
+ naturalSort = require('gulp-natural-sort'),
+ angularFilesort = require('gulp-angular-filesort'),
+ bowerFiles = require('main-bower-files');
+
+var handleErrors = require('./handle-errors');
+
+var config = require('./config');
+
+module.exports = {
+ app: app,
+ vendor: vendor,
+ test: test,
+ troubleshoot: troubleshoot
+}
+
+function app() {
+ return gulp.src(config.app + 'index.html')
+ .pipe(inject(gulp.src(config.app + 'app/**/*.js')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(naturalSort())
+ .pipe(angularFilesort()), {relative: true}))
+ .pipe(gulp.dest(config.app));
+}
+
+function vendor() {
+ var stream = gulp.src(config.app + 'index.html')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(inject(gulp.src(bowerFiles(), {read: false}), {
+ name: 'bower',
+ relative: true
+ }))
+ .pipe(gulp.dest(config.app));
+
+ return stream;
+}
+
+function test() {
+ return gulp.src(config.test + 'karma.conf.js')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(inject(gulp.src(bowerFiles({includeDev: true, filter: ['**/*.js']}), {read: false}), {
+ starttag: '// bower:js',
+ endtag: '// endbower',
+ transform: function (filepath) {
+ return '\'' + filepath.substring(1, filepath.length) + '\',';
+ }
+ }))
+ .pipe(gulp.dest(config.test));
+}
+
+function troubleshoot() {
+ /* this task removes the troubleshooting content from index.html*/
+ return gulp.src(config.app + 'index.html')
+ .pipe(plumber({errorHandler: handleErrors}))
+ /* having empty src as we dont have to read any files*/
+ .pipe(inject(gulp.src('', {read: false}), {
+ starttag: '',
+ removeTags: true,
+ transform: function () {
+ return '';
+ }
+ }))
+ .pipe(gulp.dest(config.app));
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/gulp/serve.js b/jhipster/jhipster-microservice/gateway-app/gulp/serve.js
new file mode 100644
index 0000000000..243a01784f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulp/serve.js
@@ -0,0 +1,62 @@
+'use strict';
+
+var gulp = require('gulp'),
+ util = require('./utils'),
+ url = require('url'),
+ browserSync = require('browser-sync'),
+ proxy = require('proxy-middleware');
+
+var config = require('./config');
+
+module.exports = function () {
+ var baseUri = config.uri + config.apiPort;
+ // Routes to proxy to the backend. Routes ending with a / will setup
+ // a redirect so that if accessed without a trailing slash, will
+ // redirect. This is required for some endpoints for proxy-middleware
+ // to correctly handle them.
+ var proxyRoutes = [
+ '/'
+ ];
+
+ var requireTrailingSlash = proxyRoutes.filter(function (r) {
+ return util.endsWith(r, '/');
+ }).map(function (r) {
+ // Strip trailing slash so we can use the route to match requests
+ // with non trailing slash
+ return r.substr(0, r.length - 1);
+ });
+
+ var proxies = [
+ // Ensure trailing slash in routes that require it
+ function (req, res, next) {
+ requireTrailingSlash.forEach(function (route){
+ if (url.parse(req.url).path === route) {
+ res.statusCode = 301;
+ res.setHeader('Location', route + '/');
+ res.end();
+ }
+ });
+
+ next();
+ }
+ ]
+ .concat(
+ // Build a list of proxies for routes: [route1_proxy, route2_proxy, ...]
+ proxyRoutes.map(function (r) {
+ var options = url.parse(baseUri + r);
+ options.route = r;
+ options.preserveHost = true;
+ return proxy(options);
+ }));
+
+ browserSync({
+ open: true,
+ port: config.port,
+ server: {
+ baseDir: config.app,
+ middleware: proxies
+ }
+ });
+
+ gulp.start('watch');
+};
diff --git a/jhipster/jhipster-microservice/gateway-app/gulp/utils.js b/jhipster/jhipster-microservice/gateway-app/gulp/utils.js
new file mode 100644
index 0000000000..abb80d3e31
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulp/utils.js
@@ -0,0 +1,36 @@
+'use strict';
+
+var fs = require('fs');
+
+module.exports = {
+ endsWith : endsWith,
+ parseVersion : parseVersion,
+ isLintFixed : isLintFixed
+};
+
+function endsWith(str, suffix) {
+ return str.indexOf('/', str.length - suffix.length) !== -1;
+}
+
+var parseString = require('xml2js').parseString;
+// return the version number from `pom.xml` file
+function parseVersion() {
+ var version = null;
+ var pomXml = fs.readFileSync('pom.xml', 'utf8');
+ parseString(pomXml, function (err, result) {
+ if (result.project.version && result.project.version[0]) {
+ version = result.project.version[0];
+ } else if (result.project.parent && result.project.parent[0] && result.project.parent[0].version && result.project.parent[0].version[0]) {
+ version = result.project.parent[0].version[0];
+ }
+ });
+ if (version === null) {
+ throw new Error('pom.xml is malformed. No version is defined');
+ }
+ return version;
+}
+
+function isLintFixed(file) {
+ // Has ESLint fixed the file contents?
+ return file.eslint !== null && file.eslint.fixed;
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/gulpfile.js b/jhipster/jhipster-microservice/gateway-app/gulpfile.js
new file mode 100644
index 0000000000..e4b86a0df0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/gulpfile.js
@@ -0,0 +1,166 @@
+// Generated on 2017-05-03 using generator-jhipster 4.0.8
+'use strict';
+
+var gulp = require('gulp'),
+ rev = require('gulp-rev'),
+ templateCache = require('gulp-angular-templatecache'),
+ htmlmin = require('gulp-htmlmin'),
+ imagemin = require('gulp-imagemin'),
+ ngConstant = require('gulp-ng-constant'),
+ rename = require('gulp-rename'),
+ eslint = require('gulp-eslint'),
+ del = require('del'),
+ runSequence = require('run-sequence'),
+ browserSync = require('browser-sync'),
+ KarmaServer = require('karma').Server,
+ plumber = require('gulp-plumber'),
+ changed = require('gulp-changed'),
+ gulpIf = require('gulp-if');
+
+var handleErrors = require('./gulp/handle-errors'),
+ serve = require('./gulp/serve'),
+ util = require('./gulp/utils'),
+ copy = require('./gulp/copy'),
+ inject = require('./gulp/inject'),
+ build = require('./gulp/build');
+
+var config = require('./gulp/config');
+
+gulp.task('clean', function () {
+ return del([config.dist], { dot: true });
+});
+
+gulp.task('copy', ['copy:fonts', 'copy:common']);
+
+gulp.task('copy:fonts', copy.fonts);
+
+gulp.task('copy:common', copy.common);
+
+gulp.task('copy:swagger', copy.swagger);
+
+gulp.task('copy:images', copy.images);
+
+gulp.task('images', function () {
+ return gulp.src(config.app + 'content/images/**')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(changed(config.dist + 'content/images'))
+ .pipe(imagemin({optimizationLevel: 5, progressive: true, interlaced: true}))
+ .pipe(rev())
+ .pipe(gulp.dest(config.dist + 'content/images'))
+ .pipe(rev.manifest(config.revManifest, {
+ base: config.dist,
+ merge: true
+ }))
+ .pipe(gulp.dest(config.dist))
+ .pipe(browserSync.reload({stream: true}));
+});
+
+
+gulp.task('styles', [], function () {
+ return gulp.src(config.app + 'content/css')
+ .pipe(browserSync.reload({stream: true}));
+});
+
+gulp.task('inject', function() {
+ runSequence('inject:dep', 'inject:app');
+});
+
+gulp.task('inject:dep', ['inject:test', 'inject:vendor']);
+
+gulp.task('inject:app', inject.app);
+
+gulp.task('inject:vendor', inject.vendor);
+
+gulp.task('inject:test', inject.test);
+
+gulp.task('inject:troubleshoot', inject.troubleshoot);
+
+gulp.task('assets:prod', ['images', 'styles', 'html', 'copy:swagger', 'copy:images'], build);
+
+gulp.task('html', function () {
+ return gulp.src(config.app + 'app/**/*.html')
+ .pipe(htmlmin({collapseWhitespace: true}))
+ .pipe(templateCache({
+ module: 'gatewayApp',
+ root: 'app/',
+ moduleSystem: 'IIFE'
+ }))
+ .pipe(gulp.dest(config.tmp));
+});
+
+gulp.task('ngconstant:dev', function () {
+ return ngConstant({
+ name: 'gatewayApp',
+ constants: {
+ VERSION: util.parseVersion(),
+ DEBUG_INFO_ENABLED: true
+ },
+ template: config.constantTemplate,
+ stream: true
+ })
+ .pipe(rename('app.constants.js'))
+ .pipe(gulp.dest(config.app + 'app/'));
+});
+
+gulp.task('ngconstant:prod', function () {
+ return ngConstant({
+ name: 'gatewayApp',
+ constants: {
+ VERSION: util.parseVersion(),
+ DEBUG_INFO_ENABLED: false
+ },
+ template: config.constantTemplate,
+ stream: true
+ })
+ .pipe(rename('app.constants.js'))
+ .pipe(gulp.dest(config.app + 'app/'));
+});
+
+// check app for eslint errors
+gulp.task('eslint', function () {
+ return gulp.src(['gulpfile.js', config.app + 'app/**/*.js'])
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(eslint())
+ .pipe(eslint.format())
+ .pipe(eslint.failOnError());
+});
+
+// check app for eslint errors anf fix some of them
+gulp.task('eslint:fix', function () {
+ return gulp.src(config.app + 'app/**/*.js')
+ .pipe(plumber({errorHandler: handleErrors}))
+ .pipe(eslint({
+ fix: true
+ }))
+ .pipe(eslint.format())
+ .pipe(gulpIf(util.isLintFixed, gulp.dest(config.app + 'app')));
+});
+
+gulp.task('test', ['inject:test', 'ngconstant:dev'], function (done) {
+ new KarmaServer({
+ configFile: __dirname + '/' + config.test + 'karma.conf.js',
+ singleRun: true
+ }, done).start();
+});
+
+
+gulp.task('watch', function () {
+ gulp.watch('bower.json', ['install']);
+ gulp.watch(['gulpfile.js', 'pom.xml'], ['ngconstant:dev']);
+ gulp.watch(config.app + 'content/css/**/*.css', ['styles']);
+ gulp.watch(config.app + 'content/images/**', ['images']);
+ gulp.watch(config.app + 'app/**/*.js', ['inject:app']);
+ gulp.watch([config.app + '*.html', config.app + 'app/**', config.app + 'i18n/**']).on('change', browserSync.reload);
+});
+
+gulp.task('install', function () {
+ runSequence(['inject:dep', 'ngconstant:dev'], 'inject:app', 'inject:troubleshoot');
+});
+
+gulp.task('serve', ['install'], serve);
+
+gulp.task('build', ['clean'], function (cb) {
+ runSequence(['copy', 'inject:vendor', 'ngconstant:prod'], 'inject:app', 'inject:troubleshoot', 'assets:prod', cb);
+});
+
+gulp.task('default', ['serve']);
diff --git a/jhipster/jhipster-microservice/gateway-app/mvnw b/jhipster/jhipster-microservice/gateway-app/mvnw
new file mode 100755
index 0000000000..a1ba1bf554
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/mvnw
@@ -0,0 +1,233 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ #
+ # Look for the Apple JDKs first to preserve the existing behaviour, and then look
+ # for the new JDKs provided by Oracle.
+ #
+ if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
+ #
+ # Oracle JDKs
+ #
+ export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=`/usr/libexec/java_home`
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Migwn, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ local basedir=$(pwd)
+ local wdir=$(pwd)
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ wdir=$(cd "$wdir/.."; pwd)
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} "$@"
diff --git a/jhipster/jhipster-microservice/gateway-app/mvnw.cmd b/jhipster/jhipster-microservice/gateway-app/mvnw.cmd
new file mode 100644
index 0000000000..2b934e89dd
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/mvnw.cmd
@@ -0,0 +1,145 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+set MAVEN_CMD_LINE_ARGS=%*
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/package.json b/jhipster/jhipster-microservice/gateway-app/package.json
new file mode 100644
index 0000000000..ece70314ab
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/package.json
@@ -0,0 +1,68 @@
+{
+ "name": "gateway",
+ "version": "0.0.0",
+ "description": "Description for gateway",
+ "private": true,
+ "cacheDirectories": [
+ "node_modules",
+ "src/main/webapp/bower_components"
+ ],
+ "devDependencies": {
+ "bower": "1.7.9",
+ "browser-sync": "2.17.0",
+ "del": "2.2.2",
+ "eslint-config-angular": "0.5.0",
+ "eslint-plugin-angular": "1.3.1",
+ "event-stream": "3.3.4",
+ "generator-jhipster": "4.0.8",
+ "gulp": "3.9.1",
+ "gulp-angular-filesort": "1.1.1",
+ "gulp-angular-templatecache": "2.0.0",
+ "gulp-autoprefixer": "3.1.1",
+ "gulp-changed": "1.3.2",
+ "gulp-cssnano": "2.1.2",
+ "gulp-eslint": "3.0.1",
+ "gulp-flatten": "0.3.1",
+ "gulp-footer": "1.0.5",
+ "gulp-htmlmin": "2.0.0",
+ "gulp-if": "2.0.1",
+ "gulp-imagemin": "3.0.3",
+ "gulp-inject": "4.1.0",
+ "gulp-natural-sort": "0.1.1",
+ "gulp-ng-annotate": "2.0.0",
+ "gulp-ng-constant": "1.1.0",
+ "gulp-notify": "2.2.0",
+ "gulp-plumber": "1.1.0",
+ "gulp-rename": "1.2.2",
+ "gulp-replace": "0.5.4",
+ "gulp-rev": "7.1.2",
+ "gulp-rev-replace": "0.4.3",
+ "gulp-sourcemaps": "1.6.0",
+ "gulp-uglify": "2.0.0",
+ "gulp-useref": "3.1.2",
+ "jasmine-core": "2.5.0",
+ "karma": "1.2.0",
+ "karma-chrome-launcher": "2.0.0",
+ "karma-coverage": "1.1.1",
+ "karma-jasmine": "1.0.2",
+ "karma-junit-reporter": "1.1.0",
+ "karma-phantomjs-launcher": "1.0.2",
+ "karma-script-launcher": "1.0.0",
+ "lazypipe": "1.0.1",
+ "lodash": "4.15.0",
+ "main-bower-files": "2.13.1",
+ "map-stream": "0.0.6",
+ "phantomjs-prebuilt": "2.1.12",
+ "proxy-middleware": "0.15.0",
+ "run-sequence": "1.2.2",
+ "xml2js": "0.4.17",
+
+ "yargs": "5.0.0"
+ },
+ "engines": {
+ "node": "^4.3"
+ },
+ "scripts": {
+ "test": "gulp test"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/pom.xml b/jhipster/jhipster-microservice/gateway-app/pom.xml
new file mode 100644
index 0000000000..dc9168d824
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/pom.xml
@@ -0,0 +1,1043 @@
+
+
+ 4.0.0
+
+
+ spring-boot-starter-parent
+ org.springframework.boot
+ 1.5.2.RELEASE
+
+
+
+ com.gateway
+ gateway
+ 0.0.1-SNAPSHOT
+ war
+ Gateway
+
+
+ ${maven.version}
+
+
+
+ -Djava.security.egd=file:/dev/./urandom -Xmx256m
+ 3.6.2
+ 2.0.0
+ 1.10
+ 2.5
+ 3.5
+ 0.4.13
+ 1.3
+ 1.2
+ 5.2.8.Final
+ 2.6.0
+ 0.7.9
+ 1.8
+ 3.21.0-GA
+ 1.0.0
+ 1.1.0
+ 0.7.0
+ 3.6
+ 2.0.0
+ 4.8
+ 1.3.0
+ jdt_apt
+ 1.1.0.Final
+ 3.6.0
+ 1.4.1
+ 3.0.1
+ yyyyMMddHHmmss
+ ${java.version}
+ ${java.version}
+ 3.0.0
+ 3.1.3
+ v6.10.0
+
+
+
+ 0.0.20
+
+ ${project.build.directory}/test-results
+ false
+ 3.2.2
+ 2.12.1
+ 3.2
+
+ src/main/webapp/content/**/*.*, src/main/webapp/bower_components/**/*.*, src/main/webapp/i18n/*.js, target/www/**/*.*
+
+ S3437,UndocumentedApi,BoldAndItalicTagsCheck
+
+
+ src/main/webapp/app/**/*.*
+ Web:BoldAndItalicTagsCheck
+
+ src/main/java/**/*
+ squid:S3437
+
+ src/main/java/**/*
+ squid:UndocumentedApi
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco-it.exec
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+ jacoco
+
+ ${project.testresult.directory}/karma
+
+ ${project.testresult.directory}/coverage/report-lcov/lcov.info
+
+ ${project.testresult.directory}/coverage/report-lcov/lcov.info
+
+ ${project.basedir}/src/main/
+ ${project.testresult.directory}/surefire-reports
+ ${project.basedir}/src/test/
+
+ 2.5.0
+
+ Camden.SR5
+ 2.6.1
+ 1.4.10.Final
+ 1.1.0.Final
+ v0.21.3
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+ io.github.jhipster
+ jhipster
+ ${jhipster.server.version}
+
+
+ io.dropwizard.metrics
+ metrics-core
+
+
+ io.dropwizard.metrics
+ metrics-annotation
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-json
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-jvm
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-servlet
+ ${dropwizard-metrics.version}
+
+
+ io.dropwizard.metrics
+ metrics-servlets
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-hibernate5
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-hppc
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-json-org
+
+
+ com.h2database
+ h2
+
+
+ com.hazelcast
+ hazelcast
+
+
+ com.hazelcast
+ hazelcast-hibernate52
+ ${hazelcast-hibernate52.version}
+
+
+ com.hazelcast
+ hazelcast-spring
+
+
+ org.awaitility
+ awaitility
+ ${awaitility.version}
+ test
+
+
+ com.jayway.jsonpath
+ json-path
+ test
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${springfox.version}
+
+
+ org.mapstruct
+ mapstruct
+
+
+
+
+ io.springfox
+ springfox-bean-validators
+ ${springfox.version}
+
+
+ com.mattbertolini
+ liquibase-slf4j
+ ${liquibase-slf4j.version}
+
+
+ com.ryantenney.metrics
+ metrics-spring
+ ${metrics-spring.version}
+
+
+ com.codahale.metrics
+ metrics-annotation
+
+
+ com.codahale.metrics
+ metrics-core
+
+
+ com.codahale.metrics
+ metrics-healthchecks
+
+
+
+
+ com.zaxxer
+ HikariCP
+
+
+ tools
+ com.sun
+
+
+
+
+
+ commons-codec
+ commons-codec
+ ${commons-codec.version}
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang.version}
+
+
+ javax.cache
+ cache-api
+
+
+ mysql
+ mysql-connector-java
+
+
+ net.jpountz.lz4
+ lz4
+ ${lz4.version}
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.hibernate
+ hibernate-envers
+
+
+ org.hibernate
+ hibernate-validator
+
+
+ org.liquibase
+ liquibase-core
+
+
+ jetty-servlet
+ org.eclipse.jetty
+
+
+
+
+ org.mapstruct
+ mapstruct-jdk8
+ ${mapstruct.version}
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework.boot
+ spring-boot-actuator
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-loader-tools
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+
+
+ io.jsonwebtoken
+ jjwt
+ ${jjwt.version}
+
+
+
+ com.datastax.cassandra
+ cassandra-driver-core
+
+
+ com.codahale.metrics
+ metrics-core
+
+
+
+
+ com.datastax.cassandra
+ cassandra-driver-extras
+ ${cassandra-driver.version}
+
+
+ com.datastax.cassandra
+ cassandra-driver-mapping
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-zuul
+
+
+ org.springframework.cloud
+ spring-cloud-starter
+
+
+ org.springframework.cloud
+ spring-cloud-starter-ribbon
+
+
+
+ io.netty
+ netty-transport-native-epoll
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-hystrix
+
+
+ org.springframework.cloud
+ spring-cloud-starter-spectator
+
+
+ org.springframework.retry
+ spring-retry
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
+
+ org.springframework.cloud
+ spring-cloud-starter-feign
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ ${logstash-logback-encoder.version}
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ ch.qos.logback
+ logback-access
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-cloud-connectors
+
+
+
+ org.springframework.security
+ spring-security-data
+
+
+
+
+ spring-boot:run
+
+
+ com.github.ekryd.sortpom
+ sortpom-maven-plugin
+ ${sortpom-maven-plugin.version}
+
+
+ verify
+
+ sort
+
+
+
+
+ true
+ 4
+ groupId,artifactId
+ groupId,artifactId
+ true
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-eclipse-plugin
+
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ ${maven-enforcer-plugin.version}
+
+
+ enforce-versions
+
+ enforce
+
+
+
+
+
+
+ You are running an older version of Maven. JHipster requires at least Maven ${maven.version}
+ [${maven.version},)
+
+
+ You are running an older version of Java. JHipster requires at least JDK ${java.version}
+ [${java.version}.0,)
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven-resources-plugin.version}
+
+
+ default-resources
+ validate
+
+ copy-resources
+
+
+ target/classes
+ false
+
+ #
+
+
+
+ src/main/resources/
+ true
+
+ **/*.xml
+ **/*.yml
+
+
+
+ src/main/resources/
+ false
+
+ **/*.xml
+ **/*.yml
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ alphabetical
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-maven-plugin.version}
+
+
+ pre-unit-tests
+
+ prepare-agent
+
+
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+
+
+
+
+ post-unit-test
+ test
+
+ report
+
+
+ ${project.testresult.directory}/coverage/jacoco/jacoco.exec
+ ${project.testresult.directory}/coverage/jacoco
+
+
+
+
+
+ org.sonarsource.scanner.maven
+ sonar-maven-plugin
+ ${sonar-maven-plugin.version}
+
+
+ org.liquibase
+ liquibase-maven-plugin
+ ${liquibase.version}
+
+ src/main/resources/config/liquibase/master.xml
+ src/main/resources/config/liquibase/changelog/${maven.build.timestamp}_changelog.xml
+ org.h2.Driver
+ jdbc:h2:file:./target/h2db/db/gateway
+
+ gateway
+
+ hibernate:spring:com.gateway.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ true
+ debug
+
+
+
+ org.javassist
+ javassist
+ ${javassist.version}
+
+
+ org.liquibase.ext
+ liquibase-hibernate5
+ ${liquibase-hibernate5.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ ${project.parent.version}
+
+
+ javax.validation
+ validation-api
+ ${validation-api.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ true
+
+
+
+
+ com.spotify
+ docker-maven-plugin
+ ${docker-maven-plugin.version}
+
+ gateway
+ src/main/docker
+
+
+ /
+ ${project.build.directory}
+ ${project.build.finalName}.war
+
+
+
+
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+ org.jacoco
+
+ jacoco-maven-plugin
+
+
+ ${jacoco-maven-plugin.version}
+
+
+ prepare-agent
+
+
+
+
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+ ${frontend-maven-plugin.version}
+
+ install-node-and-yarn
+ yarn
+ bower
+ gulp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ no-liquibase
+
+ ,no-liquibase
+
+
+
+ swagger
+
+ ,swagger
+
+
+
+ dev
+
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+ src/main/webapp/
+
+
+
+
+
+
+ DEBUG
+
+ dev${profile.no-liquibase}
+
+
+
+ prod
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+
+
+
+ maven-clean-plugin
+
+
+
+ target/www/
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+ target/www/
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ build-info
+
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+ ${frontend-maven-plugin.version}
+
+
+ install node and yarn
+
+ install-node-and-yarn
+
+
+ ${node.version}
+ ${yarn.version}
+
+
+
+ yarn install
+
+ yarn
+
+
+ install
+
+
+
+ bower install
+
+ bower
+
+
+ install --no-color
+
+
+
+ gulp build
+
+ gulp
+
+
+ build --no-notification
+
+
+
+ gulp test
+
+ gulp
+
+ test
+
+ test --no-notification
+
+
+
+
+
+
+
+
+ INFO
+
+ prod${profile.swagger}${profile.no-liquibase}
+
+
+
+
+ cc
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+ src/main/webapp/
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ true
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+ none
+
+
+ default-testCompile
+ none
+
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+ ${scala-maven-plugin.version}
+
+
+ compile
+ compile
+
+ add-source
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ add-source
+ testCompile
+
+
+
+
+ incremental
+ true
+ ${scala.version}
+
+
+
+
+
+
+ DEBUG
+
+ dev,swagger
+
+
+
+
+ graphite
+
+
+ io.dropwizard.metrics
+ metrics-graphite
+
+
+
+
+
+ prometheus
+
+
+ io.prometheus
+ simpleclient
+ ${prometheus-simpleclient.version}
+
+
+ io.prometheus
+ simpleclient_servlet
+ ${prometheus-simpleclient.version}
+
+
+ io.prometheus
+ simpleclient_dropwizard
+ ${prometheus-simpleclient.version}
+
+
+
+
+
+ IDE
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/Dockerfile b/jhipster/jhipster-microservice/gateway-app/src/main/docker/Dockerfile
new file mode 100644
index 0000000000..08c44eb8cf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/Dockerfile
@@ -0,0 +1,13 @@
+FROM openjdk:8-jre-alpine
+
+ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
+ JHIPSTER_SLEEP=0
+
+# add directly the war
+ADD *.war /app.war
+
+VOLUME /tmp
+EXPOSE 8080 5701/udp
+CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \
+ sleep ${JHIPSTER_SLEEP} && \
+ java -Djava.security.egd=file:/dev/./urandom -jar /app.war
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/app.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/app.yml
new file mode 100644
index 0000000000..bb53e40da5
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/app.yml
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------------
+# Note for using the rate-limiting:
+# The comment part won't be copied if you use the subgenerator docker-compose
+# you have to manually copy it
+#-------------------------------------------------------------------------------
+version: '2'
+services:
+ gateway-app:
+ image: gateway
+ environment:
+ - SPRING_PROFILES_ACTIVE=prod,swagger
+ - SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/config
+ - SPRING_DATASOURCE_URL=jdbc:mysql://gateway-mysql:3306/gateway?useUnicode=true&characterEncoding=utf8&useSSL=false
+ - SPRING_DATA_CASSANDRA_CONTACTPOINTS=gateway-cassandra
+ - JHIPSTER_SLEEP=30 # gives time for the Cassandra cluster to start and execute the migration scripts
+ ports:
+ - 8080:8080
+ gateway-mysql:
+ extends:
+ file: mysql.yml
+ service: gateway-mysql
+ # Uncomment to have Cassandra working with the gateway
+ # gateway-cassandra:
+ # extends:
+ # file: cassandra-cluster.yml
+ # service: gateway-cassandra
+ # gateway-cassandra-migration:
+ # extends:
+ # file: cassandra-migration.yml
+ # service: gateway-cassandra-migration
+ # environment:
+ # - CREATE_KEYSPACE_SCRIPT=create-keyspace-prod.cql
+ jhipster-registry:
+ extends:
+ file: jhipster-registry.yml
+ service: jhipster-registry
+ environment:
+ - SPRING_CLOUD_CONFIG_SERVER_NATIVE_SEARCH_LOCATIONS=file:./central-config/docker-config/
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra-cluster.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra-cluster.yml
new file mode 100644
index 0000000000..1329b4d3f3
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra-cluster.yml
@@ -0,0 +1,22 @@
+version: '2'
+services:
+ gateway-cassandra:
+ image: cassandra:3.9
+ # volumes:
+ # - ~/volumes/jhipster/gateway/cassandra/:/var/lib/cassandra/data
+ ports:
+ - 7000:7000
+ - 7001:7001
+ - 7199:7199
+ - 9042:9042
+ - 9160:9160
+ gateway-cassandra-node:
+ image: cassandra:3.9
+ environment:
+ - CASSANDRA_SEEDS=gateway-cassandra
+ gateway-cassandra-migration:
+ extends:
+ file: cassandra-migration.yml
+ service: gateway-cassandra-migration
+ environment:
+ - CREATE_KEYSPACE_SCRIPT=create-keyspace-prod.cql
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra-migration.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra-migration.yml
new file mode 100644
index 0000000000..3e24f7814c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra-migration.yml
@@ -0,0 +1,12 @@
+version: '2'
+services:
+ gateway-cassandra-migration:
+ environment:
+ - CASSANDRA_CONTACT_POINT=gateway-cassandra
+ - USER=docker-cassandra-migration
+ # - DEBUG_LOG=1 # uncomment to show debug logs during the migration process
+ build:
+ context: .
+ dockerfile: cassandra/Cassandra-Migration.Dockerfile
+ volumes:
+ - ../resources/config/cql:/cql:ro
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra.yml
new file mode 100644
index 0000000000..afb20a9732
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra.yml
@@ -0,0 +1,18 @@
+version: '2'
+services:
+ gateway-cassandra:
+ image: cassandra:3.9
+ # volumes:
+ # - ~/volumes/jhipster/gateway/cassandra/:/var/lib/cassandra/data
+ ports:
+ - 7000:7000
+ - 7001:7001
+ - 7199:7199
+ - 9042:9042
+ - 9160:9160
+ gateway-cassandra-migration:
+ extends:
+ file: cassandra-migration.yml
+ service: gateway-cassandra-migration
+ environment:
+ - CREATE_KEYSPACE_SCRIPT=create-keyspace.cql
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/Cassandra-Migration.Dockerfile b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/Cassandra-Migration.Dockerfile
new file mode 100644
index 0000000000..e5ceb1e56a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/Cassandra-Migration.Dockerfile
@@ -0,0 +1,11 @@
+FROM cassandra:3.9
+
+# script to orchestrate the automatic keyspace creation and apply all migration scripts
+ADD cassandra/scripts/autoMigrate.sh /usr/local/bin/autoMigrate
+RUN chmod 755 /usr/local/bin/autoMigrate
+
+# script to run any cql script from src/main/resources/config/cql
+ADD cassandra/scripts/execute-cql.sh /usr/local/bin/execute-cql
+RUN chmod 755 /usr/local/bin/execute-cql
+
+ENTRYPOINT ["autoMigrate"]
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/scripts/autoMigrate.sh b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/scripts/autoMigrate.sh
new file mode 100755
index 0000000000..83ed6347a2
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/scripts/autoMigrate.sh
@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+
+# Orchestrate the automatic execution of all the cql migration scripts when starting the cluster
+
+# Protect from iterating on empty directories
+shopt -s nullglob
+
+function log {
+ echo "[$(date)]: $*"
+}
+
+function logDebug {
+ ((DEBUG_LOG)) && echo "[DEBUG][$(date)]: $*"
+}
+
+function waitForClusterConnection() {
+ log "Waiting for Cassandra connection..."
+ retryCount=0
+ maxRetry=20
+ cqlsh -e "Describe KEYSPACES;" $CASSANDRA_CONTACT_POINT &>/dev/null
+ while [ $? -ne 0 ] && [ "$retryCount" -ne "$maxRetry" ]; do
+ logDebug 'Cassandra not reachable yet. sleep and retry. retryCount =' $retryCount
+ sleep 5
+ ((retryCount+=1))
+ cqlsh -e "Describe KEYSPACES;" $CASSANDRA_CONTACT_POINT &>/dev/null
+ done
+
+ if [ $? -ne 0 ]; then
+ log "Not connected after " $retryCount " retry. Abort the migration."
+ exit 1
+ fi
+
+ log "Connected to Cassandra cluster"
+}
+
+function executeScripts() {
+ local filePattern=$1
+ # loop over migration scripts
+ for cqlFile in $filePattern; do
+ . $EXECUTE_CQL_SCRIPT $cqlFile
+ done
+}
+
+# parse arguments
+if [ "$#" -gt 0 ]; then
+ log "Override for local usage"
+ CQL_FILES_PATH=$1
+ CREATE_KEYSPACE_SCRIPT="create-keyspace.cql" # default create-keyspace script
+ if [ "$#" -eq 2 ]; then
+ CREATE_KEYSPACE_SCRIPT=$2
+ fi
+ CREATE_KEYSPACE_SCRIPT_FOLDER="$(dirname $CQL_FILES_PATH)"
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ EXECUTE_CQL_SCRIPT=$SCRIPT_DIR'/execute-cql.sh'
+else
+ CQL_FILES_PATH="/cql/changelog/"
+ EXECUTE_CQL_SCRIPT="./usr/local/bin/execute-cql"
+ CREATE_KEYSPACE_SCRIPT_FOLDER="/cql"
+fi
+
+log "Start Cassandra migration tool"
+waitForClusterConnection
+log "Use $CREATE_KEYSPACE_SCRIPT_FOLDER/$CREATE_KEYSPACE_SCRIPT script to create the keyspace if necessary"
+cqlsh -f $CREATE_KEYSPACE_SCRIPT_FOLDER'/'$CREATE_KEYSPACE_SCRIPT $CASSANDRA_CONTACT_POINT
+log "Execute all non already executed scripts from $CQL_FILES_PATH"
+executeScripts "$CQL_FILES_PATH*.cql"
+log "Migration done"
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/scripts/execute-cql.sh b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/scripts/execute-cql.sh
new file mode 100755
index 0000000000..39739b56fe
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/cassandra/scripts/execute-cql.sh
@@ -0,0 +1,124 @@
+#!/usr/bin/env bash
+
+KEYSPACE_NAME=gateway
+
+unset scripts
+declare -A scripts
+
+function log {
+ echo "[$(date)]: $*"
+}
+
+function logDebug {
+ ((DEBUG_LOG)) && echo "[DEBUG][$(date)]: $*"
+}
+
+function exitWithError() {
+ echo "ERROR :
+ $*"
+ exit 1
+}
+
+#usage checks
+if [ -z "$1" ]; then
+ echo "usage: ./execute-cql cqlFile.cql"
+ exit 1
+fi
+if [ -z "$CASSANDRA_CONTACT_POINT" ]; then
+ echo "CASSANDRA_CONTACT_POINT environment variable must be defined"
+ exit 1
+fi
+
+cqlFile=$1
+filename=$(basename "$1")
+
+#load already executed scripts in the `scripts` global variable: dictionary[scriptName->checksum]
+function loadExecutedScripts {
+ #allow spaces in cqlsh output
+ IFS=$'\n'
+ local rows=($(cqlsh -k $KEYSPACE_NAME -e "select * from schema_version" $CASSANDRA_CONTACT_POINT | tail -n+4 | sed '$d' |sed '$d'))
+
+ for r in "${rows[@]}"
+ do
+ local scriptName=$(echo "$r" |cut -d '|' -f 1 | sed s'/^[[:space:]]*//' | sed s'/[[:space:]]*$//')
+ local checksum=$(echo "$r" |cut -d '|' -f 2 | sed s'/^[[:space:]]*//' | sed s'/[[:space:]]*$//')
+ scripts+=(["$scriptName"]="$checksum")
+ done
+ unset IFS
+}
+
+function exists {
+ if [ "$2" != in ]; then
+ echo "Incorrect usage."
+ echo "Correct usage: exists {key} in {array}"
+ return 1
+ fi
+
+ eval '[ ${'$3'[$1]+exists} ]'
+}
+
+function checksumEquals {
+ local checksum=$(md5sum $cqlFile | cut -d ' ' -f 1)
+ local foundChecksum=${scripts[${filename}]}
+
+ if [[ "$checksum" == "$foundChecksum" ]]; then
+ logDebug "checksum equals for $cqlFile, checksum=$checksum"
+ return 0
+ else
+ logDebug "different checksum found for $cqlFile
+ checksum=$checksum
+ foundChecksum=$foundChecksum"
+ return 1
+ fi
+}
+
+function isExecuted {
+ if exists $filename in scripts; then
+ if checksumEquals $cqlFile; then
+ return 0
+ else
+ exitWithError "$cqlFile has already been executed but has a different checksum logged in the schema_version table.
+ scripts must not be changed after being executed.
+ to resolve this issue you can:
+ - revert the modified script to its initial state and create a new script
+ OR
+ - delete the script entry from the schema_version table
+ "
+ fi
+ else
+ return 1
+ fi
+}
+
+function executeCqlScript {
+ log "execute: $cqlFile"
+ cqlsh -k $KEYSPACE_NAME -f $cqlFile $CASSANDRA_CONTACT_POINT
+
+ # if execution failed
+ if [ $? -ne 0 ]; then
+ exitWithError "fail to apply script $filename
+ stop applying database changes"
+ fi
+ logDebug "execution of $cqlFile succeeded"
+}
+
+function logExecutedScript {
+ local duration=$1
+ local checksum=$(md5sum $cqlFile | cut -d ' ' -f 1)
+
+ logDebug "save $cqlFile execution in schema_version table"
+ local query="INSERT INTO schema_version (script_name, checksum, executed_by, executed_on, execution_time, status) VALUES ('$filename', '$checksum', '$USER', toTimestamp(now()), $duration, 'success');"
+ cqlsh -k $KEYSPACE_NAME -e "$query" $CASSANDRA_CONTACT_POINT
+}
+
+loadExecutedScripts
+if isExecuted; then
+ logDebug "skipping $cqlFile already executed"
+else
+ _start=$(date +"%s")
+ executeCqlScript
+ _end=$(date +"%s")
+ duration=`expr $_end - $_start`
+ logExecutedScript $duration
+ log "$cqlFile executed with success in $duration seconds"
+fi
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/README.md
new file mode 100644
index 0000000000..022a152863
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/README.md
@@ -0,0 +1,7 @@
+# Central configuration sources details
+
+The JHipster-Registry will use the following directories as its configuration source :
+- localhost-config : when running the registry in docker with the jhipster-registry.yml docker-compose file
+- docker-config : when running the registry and the app both in docker with the app.yml docker-compose file
+
+For more info, refer to http://jhipster.github.io/microservices-architecture/#registry_app_configuration
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/docker-config/application.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/docker-config/application.yml
new file mode 100644
index 0000000000..f11d367241
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/docker-config/application.yml
@@ -0,0 +1,15 @@
+# Common configuration shared between all applications
+configserver:
+ name: Docker JHipster Registry
+ status: Connected to the JHipster Registry running in Docker
+
+jhipster:
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@jhipster-registry:8761/eureka/
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/localhost-config/application.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/localhost-config/application.yml
new file mode 100644
index 0000000000..052a6d0535
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/central-server-config/localhost-config/application.yml
@@ -0,0 +1,15 @@
+# Common configuration shared between all applications
+configserver:
+ name: Docker JHipster Registry
+ status: Connected to the JHipster Registry running in Docker
+
+jhipster:
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/jhipster-registry.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/jhipster-registry.yml
new file mode 100644
index 0000000000..58feb685d4
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/jhipster-registry.yml
@@ -0,0 +1,18 @@
+version: '2'
+services:
+ jhipster-registry:
+ image: jhipster/jhipster-registry:v2.5.8
+ volumes:
+ - ./central-server-config:/central-config
+ # When run with the "dev" Spring profile, the JHipster Registry will
+ # read the config from the local filesystem (central-server-config directory)
+ # When run with the "prod" Spring profile, it will read the config from a git repository
+ # See http://jhipster.github.io/microservices-architecture/#registry_app_configuration
+ environment:
+ - SPRING_PROFILES_ACTIVE=dev
+ - SECURITY_USER_PASSWORD=admin
+ - SPRING_CLOUD_CONFIG_SERVER_NATIVE_SEARCH_LOCATIONS=file:./central-config/localhost-config/
+ # - GIT_URI=https://github.com/jhipster/jhipster-registry/
+ # - GIT_SEARCH_PATHS=central-config
+ ports:
+ - 8761:8761
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/mysql.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/mysql.yml
new file mode 100644
index 0000000000..464243fd14
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/mysql.yml
@@ -0,0 +1,13 @@
+version: '2'
+services:
+ gateway-mysql:
+ image: mysql:5.7.13
+ # volumes:
+ # - ~/volumes/jhipster/gateway/mysql/:/var/lib/mysql/
+ environment:
+ - MYSQL_USER=root
+ - MYSQL_ALLOW_EMPTY_PASSWORD=yes
+ - MYSQL_DATABASE=gateway
+ ports:
+ - 3306:3306
+ command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/docker/sonar.yml b/jhipster/jhipster-microservice/gateway-app/src/main/docker/sonar.yml
new file mode 100644
index 0000000000..2790075014
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/docker/sonar.yml
@@ -0,0 +1,7 @@
+version: '2'
+services:
+ gateway-sonar:
+ image: sonarqube:6.2-alpine
+ ports:
+ - 9000:9000
+ - 9092:9092
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/ApplicationWebXml.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/ApplicationWebXml.java
new file mode 100644
index 0000000000..75c23ce9f5
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/ApplicationWebXml.java
@@ -0,0 +1,21 @@
+package com.gateway;
+
+import com.gateway.config.DefaultProfileUtil;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.support.SpringBootServletInitializer;
+
+/**
+ * This is a helper Java class that provides an alternative to creating a web.xml.
+ * This will be invoked only when the application is deployed to a servlet container like Tomcat, JBoss etc.
+ */
+public class ApplicationWebXml extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ /**
+ * set a default to use when no profile is configured.
+ */
+ DefaultProfileUtil.addDefaultProfile(application.application());
+ return application.sources(GatewayApp.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/GatewayApp.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/GatewayApp.java
new file mode 100644
index 0000000000..1905b055b6
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/GatewayApp.java
@@ -0,0 +1,93 @@
+package com.gateway;
+
+import com.gateway.config.ApplicationProperties;
+import com.gateway.config.DefaultProfileUtil;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.actuate.autoconfigure.*;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.core.env.Environment;
+
+import javax.annotation.PostConstruct;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collection;
+
+@ComponentScan
+@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class, MetricsDropwizardAutoConfiguration.class})
+@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
+@EnableDiscoveryClient
+@EnableZuulProxy
+public class GatewayApp {
+
+ private static final Logger log = LoggerFactory.getLogger(GatewayApp.class);
+
+ private final Environment env;
+
+ public GatewayApp(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Initializes gateway.
+ *
+ * Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
+ *
+ * You can find more information on how profiles work with JHipster on http://jhipster.github.io/profiles/.
+ */
+ @PostConstruct
+ public void initApplication() {
+ Collection activeProfiles = Arrays.asList(env.getActiveProfiles());
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
+ log.error("You have misconfigured your application! It should not run " +
+ "with both the 'dev' and 'prod' profiles at the same time.");
+ }
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
+ log.error("You have misconfigured your application! It should not" +
+ "run with both the 'dev' and 'cloud' profiles at the same time.");
+ }
+ }
+
+ /**
+ * Main method, used to run the application.
+ *
+ * @param args the command line arguments
+ * @throws UnknownHostException if the local host name could not be resolved into an address
+ */
+ public static void main(String[] args) throws UnknownHostException {
+ SpringApplication app = new SpringApplication(GatewayApp.class);
+ DefaultProfileUtil.addDefaultProfile(app);
+ Environment env = app.run(args).getEnvironment();
+ String protocol = "http";
+ if (env.getProperty("server.ssl.key-store") != null) {
+ protocol = "https";
+ }
+ log.info("\n----------------------------------------------------------\n\t" +
+ "Application '{}' is running! Access URLs:\n\t" +
+ "Local: \t\t{}://localhost:{}\n\t" +
+ "External: \t{}://{}:{}\n\t" +
+ "Profile(s): \t{}\n----------------------------------------------------------",
+ env.getProperty("spring.application.name"),
+ protocol,
+ env.getProperty("server.port"),
+ protocol,
+ InetAddress.getLocalHost().getHostAddress(),
+ env.getProperty("server.port"),
+ env.getActiveProfiles());
+
+ String configServerStatus = env.getProperty("configserver.status");
+ log.info("\n----------------------------------------------------------\n\t" +
+ "Config Server: \t{}\n----------------------------------------------------------",
+ configServerStatus == null ? "Not found or not setup for this application" : configServerStatus);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/aop/logging/LoggingAspect.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/aop/logging/LoggingAspect.java
new file mode 100644
index 0000000000..dd2c4464be
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/aop/logging/LoggingAspect.java
@@ -0,0 +1,79 @@
+package com.gateway.aop.logging;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.Environment;
+
+import java.util.Arrays;
+
+/**
+ * Aspect for logging execution of service and repository Spring components.
+ *
+ * By default, it only runs with the "dev" profile.
+ */
+@Aspect
+public class LoggingAspect {
+
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ private final Environment env;
+
+ public LoggingAspect(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Pointcut that matches all repositories, services and Web REST endpoints.
+ */
+ @Pointcut("within(com.gateway.repository..*) || within(com.gateway.service..*) || within(com.gateway.web.rest..*)")
+ public void loggingPointcut() {
+ // Method is empty as this is just a Pointcut, the implementations are in the advices.
+ }
+
+ /**
+ * Advice that logs methods throwing exceptions.
+ */
+ @AfterThrowing(pointcut = "loggingPointcut()", throwing = "e")
+ public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e);
+
+ } else {
+ log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL");
+ }
+ }
+
+ /**
+ * Advice that logs when a method is entered and exited.
+ */
+ @Around("loggingPointcut()")
+ public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ if (log.isDebugEnabled()) {
+ log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
+ }
+ try {
+ Object result = joinPoint.proceed();
+ if (log.isDebugEnabled()) {
+ log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
+ joinPoint.getSignature().getName(), result);
+ }
+ return result;
+ } catch (IllegalArgumentException e) {
+ log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
+ joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
+
+ throw e;
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/ApplicationProperties.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/ApplicationProperties.java
new file mode 100644
index 0000000000..18e87d7966
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/ApplicationProperties.java
@@ -0,0 +1,15 @@
+package com.gateway.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Properties specific to JHipster.
+ *
+ *
+ * Properties are configured in the application.yml file.
+ *
+ */
+@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
+public class ApplicationProperties {
+
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/AsyncConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/AsyncConfiguration.java
new file mode 100644
index 0000000000..92ed89b5e4
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/AsyncConfiguration.java
@@ -0,0 +1,46 @@
+package com.gateway.config;
+
+import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
+import io.github.jhipster.config.JHipsterProperties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.*;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+@Configuration
+@EnableAsync
+@EnableScheduling
+public class AsyncConfiguration implements AsyncConfigurer {
+
+ private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public AsyncConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Override
+ @Bean(name = "taskExecutor")
+ public Executor getAsyncExecutor() {
+ log.debug("Creating Async Task Executor");
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
+ executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
+ executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
+ executor.setThreadNamePrefix("gateway-Executor-");
+ return new ExceptionHandlingAsyncTaskExecutor(executor);
+ }
+
+ @Override
+ public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+ return new SimpleAsyncUncaughtExceptionHandler();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/CacheConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/CacheConfiguration.java
new file mode 100644
index 0000000000..d5f464f490
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/CacheConfiguration.java
@@ -0,0 +1,134 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.Hazelcast;
+import com.hazelcast.config.MapConfig;
+import com.hazelcast.config.EvictionPolicy;
+import com.hazelcast.config.MaxSizeConfig;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.context.annotation.*;
+import org.springframework.core.env.Environment;
+
+import javax.annotation.PreDestroy;
+
+@Configuration
+@EnableCaching
+@AutoConfigureAfter(value = { MetricsConfiguration.class })
+@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class })
+public class CacheConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);
+
+ private final Environment env;
+
+ private final DiscoveryClient discoveryClient;
+
+ private final ServerProperties serverProperties;
+
+ public CacheConfiguration(Environment env, DiscoveryClient discoveryClient, ServerProperties serverProperties) {
+ this.env = env;
+ this.discoveryClient = discoveryClient;
+ this.serverProperties = serverProperties;
+ }
+
+ @PreDestroy
+ public void destroy() {
+ log.info("Closing Cache Manager");
+ Hazelcast.shutdownAll();
+ }
+
+ @Bean
+ public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
+ log.debug("Starting HazelcastCacheManager");
+ CacheManager cacheManager = new com.hazelcast.spring.cache.HazelcastCacheManager(hazelcastInstance);
+ return cacheManager;
+ }
+
+ @Bean
+ public HazelcastInstance hazelcastInstance(JHipsterProperties jHipsterProperties) {
+ log.debug("Configuring Hazelcast");
+ Config config = new Config();
+ config.setInstanceName("gateway");
+ // The serviceId is by default the application's name, see Spring Boot's eureka.instance.appname property
+ String serviceId = discoveryClient.getLocalServiceInstance().getServiceId();
+ log.debug("Configuring Hazelcast clustering for instanceId: {}", serviceId);
+
+ // In development, everything goes through 127.0.0.1, with a different port
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ log.debug("Application is running with the \"dev\" profile, Hazelcast " +
+ "cluster will only work with localhost instances");
+
+ System.setProperty("hazelcast.local.localAddress", "127.0.0.1");
+ config.getNetworkConfig().setPort(serverProperties.getPort() + 5701);
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
+ for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
+ String clusterMember = "127.0.0.1:" + (instance.getPort() + 5701);
+ log.debug("Adding Hazelcast (dev) cluster member " + clusterMember);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
+ }
+ } else { // Production configuration, one host per instance all using port 5701
+ config.getNetworkConfig().setPort(5701);
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
+ for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
+ String clusterMember = instance.getHost() + ":5701";
+ log.debug("Adding Hazelcast (prod) cluster member " + clusterMember);
+ config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
+ }
+ }
+ config.getMapConfigs().put("default", initializeDefaultMapConfig());
+ config.getMapConfigs().put("com.gateway.domain.*", initializeDomainMapConfig(jHipsterProperties));
+ return Hazelcast.newHazelcastInstance(config);
+ }
+
+ private MapConfig initializeDefaultMapConfig() {
+ MapConfig mapConfig = new MapConfig();
+
+ /*
+ Number of backups. If 1 is set as the backup-count for example,
+ then all entries of the map will be copied to another JVM for
+ fail-safety. Valid numbers are 0 (no backup), 1, 2, 3.
+ */
+ mapConfig.setBackupCount(0);
+
+ /*
+ Valid values are:
+ NONE (no eviction),
+ LRU (Least Recently Used),
+ LFU (Least Frequently Used).
+ NONE is the default.
+ */
+ mapConfig.setEvictionPolicy(EvictionPolicy.LRU);
+
+ /*
+ Maximum size of the map. When max size is reached,
+ map is evicted based on the policy defined.
+ Any integer between 0 and Integer.MAX_VALUE. 0 means
+ Integer.MAX_VALUE. Default is 0.
+ */
+ mapConfig.setMaxSizeConfig(new MaxSizeConfig(0, MaxSizeConfig.MaxSizePolicy.USED_HEAP_SIZE));
+
+ return mapConfig;
+ }
+
+ private MapConfig initializeDomainMapConfig(JHipsterProperties jHipsterProperties) {
+ MapConfig mapConfig = new MapConfig();
+ mapConfig.setTimeToLiveSeconds(jHipsterProperties.getCache().getHazelcast().getTimeToLiveSeconds());
+ return mapConfig;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/CloudDatabaseConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/CloudDatabaseConfiguration.java
new file mode 100644
index 0000000000..ad025f4149
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/CloudDatabaseConfiguration.java
@@ -0,0 +1,24 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.CacheManager;
+import org.springframework.cloud.config.java.AbstractCloudConfig;
+import org.springframework.context.annotation.*;
+
+import javax.sql.DataSource;
+
+@Configuration
+@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
+public class CloudDatabaseConfiguration extends AbstractCloudConfig {
+
+ private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
+
+ @Bean
+ public DataSource dataSource(CacheManager cacheManager) {
+ log.info("Configuring JDBC datasource from a cloud provider");
+ return connectionFactory().dataSource();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/Constants.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/Constants.java
new file mode 100644
index 0000000000..aa79156573
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/Constants.java
@@ -0,0 +1,16 @@
+package com.gateway.config;
+
+/**
+ * Application constants.
+ */
+public final class Constants {
+
+ //Regex for acceptable logins
+ public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
+
+ public static final String SYSTEM_ACCOUNT = "system";
+ public static final String ANONYMOUS_USER = "anonymoususer";
+
+ private Constants() {
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DatabaseConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DatabaseConfiguration.java
new file mode 100644
index 0000000000..83bf0db1bc
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DatabaseConfiguration.java
@@ -0,0 +1,75 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;
+
+import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
+import liquibase.integration.spring.SpringLiquibase;
+import org.h2.tools.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+
+@Configuration
+@EnableJpaRepositories("com.gateway.repository")
+@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
+@EnableTransactionManagement
+public class DatabaseConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
+
+ private final Environment env;
+
+ public DatabaseConfiguration(Environment env) {
+ this.env = env;
+ }
+
+ /**
+ * Open the TCP port for the H2 database, so it is available remotely.
+ *
+ * @return the H2 database TCP server
+ * @throws SQLException if the server failed to start
+ */
+ @Bean(initMethod = "start", destroyMethod = "stop")
+ @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
+ public Server h2TCPServer() throws SQLException {
+ return Server.createTcpServer("-tcp","-tcpAllowOthers");
+ }
+
+ @Bean
+ public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor,
+ DataSource dataSource, LiquibaseProperties liquibaseProperties) {
+
+ // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
+ SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env);
+ liquibase.setDataSource(dataSource);
+ liquibase.setChangeLog("classpath:config/liquibase/master.xml");
+ liquibase.setContexts(liquibaseProperties.getContexts());
+ liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
+ liquibase.setDropFirst(liquibaseProperties.isDropFirst());
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) {
+ liquibase.setShouldRun(false);
+ } else {
+ liquibase.setShouldRun(liquibaseProperties.isEnabled());
+ log.debug("Configuring Liquibase");
+ }
+ return liquibase;
+ }
+
+ @Bean
+ public Hibernate5Module hibernate5Module() {
+ return new Hibernate5Module();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DateTimeFormatConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DateTimeFormatConfiguration.java
new file mode 100644
index 0000000000..e9391c5b1d
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DateTimeFormatConfiguration.java
@@ -0,0 +1,17 @@
+package com.gateway.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter {
+
+ @Override
+ public void addFormatters(FormatterRegistry registry) {
+ DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
+ registrar.setUseIsoFormat(true);
+ registrar.registerFormatters(registry);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DefaultProfileUtil.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DefaultProfileUtil.java
new file mode 100644
index 0000000000..379abc93d5
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/DefaultProfileUtil.java
@@ -0,0 +1,48 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.core.env.Environment;
+
+import java.util.*;
+
+/**
+ * Utility class to load a Spring profile to be used as default
+ * when there is no spring.profiles.active set in the environment or as command line argument.
+ * If the value is not available in application.yml then dev profile will be used as default.
+ */
+public final class DefaultProfileUtil {
+
+ private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default";
+
+ private DefaultProfileUtil() {
+ }
+
+ /**
+ * Set a default to use when no profile is configured.
+ *
+ * @param app the Spring application
+ */
+ public static void addDefaultProfile(SpringApplication app) {
+ Map defProperties = new HashMap<>();
+ /*
+ * The default profile to use when no other profiles are defined
+ * This cannot be set in the application.yml file.
+ * See https://github.com/spring-projects/spring-boot/issues/1219
+ */
+ defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT);
+ app.setDefaultProperties(defProperties);
+ }
+
+ /**
+ * Get the profiles that are applied else get default profiles.
+ */
+ public static String[] getActiveProfiles(Environment env) {
+ String[] profiles = env.getActiveProfiles();
+ if (profiles.length == 0) {
+ return env.getDefaultProfiles();
+ }
+ return profiles;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/GatewayConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/GatewayConfiguration.java
new file mode 100644
index 0000000000..301d7304bd
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/GatewayConfiguration.java
@@ -0,0 +1,71 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import com.gateway.gateway.ratelimiting.RateLimitingFilter;
+import com.gateway.gateway.ratelimiting.RateLimitingRepository;
+import com.gateway.gateway.accesscontrol.AccessControlFilter;
+import com.gateway.gateway.responserewriting.SwaggerBasePathRewritingFilter;
+
+import com.datastax.driver.core.*;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class GatewayConfiguration {
+
+ @Configuration
+ public static class SwaggerBasePathRewritingConfiguration {
+
+ @Bean
+ public SwaggerBasePathRewritingFilter swaggerBasePathRewritingFilter(){
+ return new SwaggerBasePathRewritingFilter();
+ }
+ }
+
+ @Configuration
+ public static class AccessControlFilterConfiguration {
+
+ @Bean
+ public AccessControlFilter accessControlFilter(RouteLocator routeLocator, JHipsterProperties jHipsterProperties){
+ return new AccessControlFilter(routeLocator, jHipsterProperties);
+ }
+ }
+
+ /**
+ * Configures the Zuul filter that limits the number of API calls per user.
+ *
+ * For this filter to work, you need to have:
+ *
+ *
A working Cassandra cluster
+ *
A schema with the JHipster rate-limiting tables configured, using the
+ * "create_keyspace.cql" and "create_tables.cql" scripts from the
+ * "src/main/resources/config/cql" directory
+ *
Your cluster configured in your application-*.yml files, using the
+ * "spring.data.cassandra" keys
+ *
+ */
+ @Configuration
+ @ConditionalOnProperty("jhipster.gateway.rate-limiting.enabled")
+ public static class RateLimitingConfiguration {
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public RateLimitingConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Bean
+ public RateLimitingRepository rateLimitingRepository(Session session) {
+ return new RateLimitingRepository(session);
+ }
+
+ @Bean
+ public RateLimitingFilter rateLimitingFilter(RateLimitingRepository rateLimitingRepository) {
+ return new RateLimitingFilter(rateLimitingRepository, jHipsterProperties);
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LocaleConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LocaleConfiguration.java
new file mode 100644
index 0000000000..1affdfe7f4
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LocaleConfiguration.java
@@ -0,0 +1,35 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.locale.AngularCookieLocaleResolver;
+
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+
+@Configuration
+public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware {
+
+ @Override
+ public void setEnvironment(Environment environment) {
+ // unused
+ }
+
+ @Bean(name = "localeResolver")
+ public LocaleResolver localeResolver() {
+ AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver();
+ cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY");
+ return cookieLocaleResolver;
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
+ localeChangeInterceptor.setParamName("language");
+ registry.addInterceptor(localeChangeInterceptor);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LoggingAspectConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LoggingAspectConfiguration.java
new file mode 100644
index 0000000000..bd02cc46cf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LoggingAspectConfiguration.java
@@ -0,0 +1,19 @@
+package com.gateway.config;
+
+import com.gateway.aop.logging.LoggingAspect;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.springframework.context.annotation.*;
+import org.springframework.core.env.Environment;
+
+@Configuration
+@EnableAspectJAutoProxy
+public class LoggingAspectConfiguration {
+
+ @Bean
+ @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
+ public LoggingAspect loggingAspect(Environment env) {
+ return new LoggingAspect(env);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LoggingConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LoggingConfiguration.java
new file mode 100644
index 0000000000..c9c0a06adc
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/LoggingConfiguration.java
@@ -0,0 +1,113 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import ch.qos.logback.classic.AsyncAppender;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.LoggerContextListener;
+import ch.qos.logback.core.spi.ContextAwareBase;
+import net.logstash.logback.appender.LogstashSocketAppender;
+import net.logstash.logback.stacktrace.ShortenedThrowableConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class LoggingConfiguration {
+
+ private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
+
+ private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+
+ @Value("${spring.application.name}")
+ private String appName;
+
+ @Value("${server.port}")
+ private String serverPort;
+
+ @Value("${eureka.instance.instanceId}")
+ private String instanceId;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public LoggingConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
+ addLogstashAppender(context);
+
+ // Add context listener
+ LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener();
+ loggerContextListener.setContext(context);
+ context.addListener(loggerContextListener);
+ }
+ }
+
+ public void addLogstashAppender(LoggerContext context) {
+ log.info("Initializing Logstash logging");
+
+ LogstashSocketAppender logstashAppender = new LogstashSocketAppender();
+ logstashAppender.setName("LOGSTASH");
+ logstashAppender.setContext(context);
+ String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"," +
+ "\"instance_id\":\"" + instanceId + "\"}";
+
+ // Set the Logstash appender config from JHipster properties
+ logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost());
+ logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort());
+ logstashAppender.setCustomFields(customFields);
+
+ // Limit the maximum length of the forwarded stacktrace so that it won't exceed the 8KB UDP limit of logstash
+ ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter();
+ throwableConverter.setMaxLength(7500);
+ throwableConverter.setRootCauseFirst(true);
+ logstashAppender.setThrowableConverter(throwableConverter);
+
+ logstashAppender.start();
+
+ // Wrap the appender in an Async appender for performance
+ AsyncAppender asyncLogstashAppender = new AsyncAppender();
+ asyncLogstashAppender.setContext(context);
+ asyncLogstashAppender.setName("ASYNC_LOGSTASH");
+ asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
+ asyncLogstashAppender.addAppender(logstashAppender);
+ asyncLogstashAppender.start();
+
+ context.getLogger("ROOT").addAppender(asyncLogstashAppender);
+ }
+
+ /**
+ * Logback configuration is achieved by configuration file and API.
+ * When configuration file change is detected, the configuration is reset.
+ * This listener ensures that the programmatic configuration is also re-applied after reset.
+ */
+ class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener {
+
+ @Override
+ public boolean isResetResistant() {
+ return true;
+ }
+
+ @Override
+ public void onStart(LoggerContext context) {
+ addLogstashAppender(context);
+ }
+
+ @Override
+ public void onReset(LoggerContext context) {
+ addLogstashAppender(context);
+ }
+
+ @Override
+ public void onStop(LoggerContext context) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) {
+ // Nothing to do.
+ }
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/MetricsConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/MetricsConfiguration.java
new file mode 100644
index 0000000000..79689196df
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/MetricsConfiguration.java
@@ -0,0 +1,113 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterProperties;
+import io.github.jhipster.config.metrics.SpectatorLogMetricWriter;
+
+import com.netflix.spectator.api.Registry;
+import org.springframework.boot.actuate.autoconfigure.ExportMetricReader;
+import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter;
+import org.springframework.boot.actuate.metrics.writer.MetricWriter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.netflix.metrics.spectator.SpectatorMetricReader;
+
+import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Slf4jReporter;
+import com.codahale.metrics.health.HealthCheckRegistry;
+import com.codahale.metrics.jvm.*;
+import com.ryantenney.metrics.spring.config.annotation.EnableMetrics;
+import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter;
+import com.zaxxer.hikari.HikariDataSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.*;
+
+import javax.annotation.PostConstruct;
+import java.lang.management.ManagementFactory;
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+@EnableMetrics(proxyTargetClass = true)
+public class MetricsConfiguration extends MetricsConfigurerAdapter {
+
+ private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory";
+ private static final String PROP_METRIC_REG_JVM_GARBAGE = "jvm.garbage";
+ private static final String PROP_METRIC_REG_JVM_THREADS = "jvm.threads";
+ private static final String PROP_METRIC_REG_JVM_FILES = "jvm.files";
+ private static final String PROP_METRIC_REG_JVM_BUFFERS = "jvm.buffers";
+ private final Logger log = LoggerFactory.getLogger(MetricsConfiguration.class);
+
+ private MetricRegistry metricRegistry = new MetricRegistry();
+
+ private HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();
+
+ private final JHipsterProperties jHipsterProperties;
+
+ private HikariDataSource hikariDataSource;
+
+ public MetricsConfiguration(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Autowired(required = false)
+ public void setHikariDataSource(HikariDataSource hikariDataSource) {
+ this.hikariDataSource = hikariDataSource;
+ }
+
+ @Override
+ @Bean
+ public MetricRegistry getMetricRegistry() {
+ return metricRegistry;
+ }
+
+ @Override
+ @Bean
+ public HealthCheckRegistry getHealthCheckRegistry() {
+ return healthCheckRegistry;
+ }
+
+ @PostConstruct
+ public void init() {
+ log.debug("Registering JVM gauges");
+ metricRegistry.register(PROP_METRIC_REG_JVM_MEMORY, new MemoryUsageGaugeSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_GARBAGE, new GarbageCollectorMetricSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_THREADS, new ThreadStatesGaugeSet());
+ metricRegistry.register(PROP_METRIC_REG_JVM_FILES, new FileDescriptorRatioGauge());
+ metricRegistry.register(PROP_METRIC_REG_JVM_BUFFERS, new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer()));
+ if (hikariDataSource != null) {
+ log.debug("Monitoring the datasource");
+ hikariDataSource.setMetricRegistry(metricRegistry);
+ }
+ if (jHipsterProperties.getMetrics().getJmx().isEnabled()) {
+ log.debug("Initializing Metrics JMX reporting");
+ JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build();
+ jmxReporter.start();
+ }
+ if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
+ log.info("Initializing Metrics Log reporting");
+ final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
+ .outputTo(LoggerFactory.getLogger("metrics"))
+ .convertRatesTo(TimeUnit.SECONDS)
+ .convertDurationsTo(TimeUnit.MILLISECONDS)
+ .build();
+ reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS);
+ }
+ }
+
+ /* Spectator metrics log reporting */
+ @Bean
+ @ConditionalOnProperty("jhipster.logging.spectator-metrics.enabled")
+ @ExportMetricReader
+ public SpectatorMetricReader SpectatorMetricReader(Registry registry) {
+ log.info("Initializing Spectator Metrics Log reporting");
+ return new SpectatorMetricReader(registry);
+ }
+
+ @Bean
+ @ConditionalOnProperty("jhipster.logging.spectator-metrics.enabled")
+ @ExportMetricWriter
+ MetricWriter metricWriter() {
+ return new SpectatorLogMetricWriter();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/SecurityConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/SecurityConfiguration.java
new file mode 100644
index 0000000000..73963b5b52
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/SecurityConfiguration.java
@@ -0,0 +1,127 @@
+package com.gateway.config;
+
+import com.gateway.security.*;
+import com.gateway.security.jwt.*;
+
+import io.github.jhipster.security.*;
+
+import org.springframework.beans.factory.BeanInitializationException;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.filter.CorsFilter;
+
+import javax.annotation.PostConstruct;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ private final AuthenticationManagerBuilder authenticationManagerBuilder;
+
+ private final UserDetailsService userDetailsService;
+
+ private final TokenProvider tokenProvider;
+
+ private final CorsFilter corsFilter;
+
+ public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,
+ TokenProvider tokenProvider,
+ CorsFilter corsFilter) {
+
+ this.authenticationManagerBuilder = authenticationManagerBuilder;
+ this.userDetailsService = userDetailsService;
+ this.tokenProvider = tokenProvider;
+ this.corsFilter = corsFilter;
+ }
+
+ @PostConstruct
+ public void init() {
+ try {
+ authenticationManagerBuilder
+ .userDetailsService(userDetailsService)
+ .passwordEncoder(passwordEncoder());
+ } catch (Exception e) {
+ throw new BeanInitializationException("Security configuration failed", e);
+ }
+ }
+
+ @Bean
+ public Http401UnauthorizedEntryPoint http401UnauthorizedEntryPoint() {
+ return new Http401UnauthorizedEntryPoint();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+ web.ignoring()
+ .antMatchers(HttpMethod.OPTIONS, "/**")
+ .antMatchers("/app/**/*.{js,html}")
+ .antMatchers("/bower_components/**")
+ .antMatchers("/i18n/**")
+ .antMatchers("/content/**")
+ .antMatchers("/swagger-ui/index.html")
+ .antMatchers("/test/**")
+ .antMatchers("/h2-console/**");
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
+ .exceptionHandling()
+ .authenticationEntryPoint(http401UnauthorizedEntryPoint())
+ .and()
+ .csrf()
+ .disable()
+ .headers()
+ .frameOptions()
+ .disable()
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .authorizeRequests()
+ .antMatchers("/api/register").permitAll()
+ .antMatchers("/api/activate").permitAll()
+ .antMatchers("/api/authenticate").permitAll()
+ .antMatchers("/api/account/reset_password/init").permitAll()
+ .antMatchers("/api/account/reset_password/finish").permitAll()
+ .antMatchers("/api/profile-info").permitAll()
+ .antMatchers("/api/**").authenticated()
+ .antMatchers("/management/health").permitAll()
+ .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
+ .antMatchers("/v2/api-docs/**").permitAll()
+ .antMatchers("/swagger-resources/configuration/ui").permitAll()
+ .antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN)
+ .and()
+ .apply(securityConfigurerAdapter());
+
+ }
+
+ private JWTConfigurer securityConfigurerAdapter() {
+ return new JWTConfigurer(tokenProvider);
+ }
+
+ @Bean
+ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
+ return new SecurityEvaluationContextExtension();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/ThymeleafConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/ThymeleafConfiguration.java
new file mode 100644
index 0000000000..a39a0363c8
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/ThymeleafConfiguration.java
@@ -0,0 +1,26 @@
+package com.gateway.config;
+
+import org.apache.commons.lang3.CharEncoding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.*;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+
+@Configuration
+public class ThymeleafConfiguration {
+
+ @SuppressWarnings("unused")
+ private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class);
+
+ @Bean
+ @Description("Thymeleaf template resolver serving HTML 5 emails")
+ public ClassLoaderTemplateResolver emailTemplateResolver() {
+ ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
+ emailTemplateResolver.setPrefix("mails/");
+ emailTemplateResolver.setSuffix(".html");
+ emailTemplateResolver.setTemplateMode("HTML5");
+ emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
+ emailTemplateResolver.setOrder(1);
+ return emailTemplateResolver;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/WebConfigurer.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/WebConfigurer.java
new file mode 100644
index 0000000000..1b780a8caf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/WebConfigurer.java
@@ -0,0 +1,195 @@
+package com.gateway.config;
+
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
+import io.github.jhipster.web.filter.CachingHttpHeadersFilter;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.servlet.InstrumentedFilter;
+import com.codahale.metrics.servlets.MetricsServlet;
+import com.hazelcast.core.HazelcastInstance;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.embedded.*;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
+import io.undertow.UndertowOptions;
+import org.springframework.boot.web.servlet.ServletContextInitializer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.io.File;
+import java.nio.file.Paths;
+import java.util.*;
+import javax.servlet.*;
+
+/**
+ * Configuration of web application with Servlet 3.0 APIs.
+ */
+@Configuration
+public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
+
+ private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
+
+ private final Environment env;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ private final HazelcastInstance hazelcastInstance;
+
+ private MetricRegistry metricRegistry;
+
+ public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties, HazelcastInstance hazelcastInstance) {
+
+ this.env = env;
+ this.jHipsterProperties = jHipsterProperties;
+ this.hazelcastInstance = hazelcastInstance;
+ }
+
+ @Override
+ public void onStartup(ServletContext servletContext) throws ServletException {
+ if (env.getActiveProfiles().length != 0) {
+ log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
+ }
+ EnumSet disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
+ initMetrics(servletContext, disps);
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
+ initCachingHttpHeadersFilter(servletContext, disps);
+ }
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
+ initH2Console(servletContext);
+ }
+ log.info("Web application fully configured");
+ }
+
+ /**
+ * Customize the Servlet engine: Mime types, the document root, the cache.
+ */
+ @Override
+ public void customize(ConfigurableEmbeddedServletContainer container) {
+ MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
+ // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
+ mappings.add("html", "text/html;charset=utf-8");
+ // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
+ mappings.add("json", "text/html;charset=utf-8");
+ container.setMimeMappings(mappings);
+ // When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets.
+ setLocationForStaticAssets(container);
+
+ /*
+ * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
+ * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
+ * See the JHipsterProperties class and your application-*.yml configuration files
+ * for more information.
+ */
+ if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
+ container instanceof UndertowEmbeddedServletContainerFactory) {
+
+ ((UndertowEmbeddedServletContainerFactory) container)
+ .addBuilderCustomizers(builder ->
+ builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
+ }
+ }
+
+ private void setLocationForStaticAssets(ConfigurableEmbeddedServletContainer container) {
+ File root;
+ String prefixPath = resolvePathPrefix();
+ if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
+ root = new File(prefixPath + "target/www/");
+ } else {
+ root = new File(prefixPath + "src/main/webapp/");
+ }
+ if (root.exists() && root.isDirectory()) {
+ container.setDocumentRoot(root);
+ }
+ }
+
+ /**
+ * Resolve path prefix to static resources.
+ */
+ private String resolvePathPrefix() {
+ String fullExecutablePath = this.getClass().getResource("").getPath();
+ String rootPath = Paths.get(".").toUri().normalize().getPath();
+ String extractedPath = fullExecutablePath.replace(rootPath, "");
+ int extractionEndIndex = extractedPath.indexOf("target/");
+ if(extractionEndIndex <= 0) {
+ return "";
+ }
+ return extractedPath.substring(0, extractionEndIndex);
+ }
+
+ /**
+ * Initializes the caching HTTP Headers Filter.
+ */
+ private void initCachingHttpHeadersFilter(ServletContext servletContext,
+ EnumSet disps) {
+ log.debug("Registering Caching HTTP Headers Filter");
+ FilterRegistration.Dynamic cachingHttpHeadersFilter =
+ servletContext.addFilter("cachingHttpHeadersFilter",
+ new CachingHttpHeadersFilter(jHipsterProperties));
+
+ cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*");
+ cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*");
+ cachingHttpHeadersFilter.setAsyncSupported(true);
+ }
+
+ /**
+ * Initializes Metrics.
+ */
+ private void initMetrics(ServletContext servletContext, EnumSet disps) {
+ log.debug("Initializing Metrics registries");
+ servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
+ metricRegistry);
+ servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
+ metricRegistry);
+
+ log.debug("Registering Metrics Filter");
+ FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
+ new InstrumentedFilter());
+
+ metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
+ metricsFilter.setAsyncSupported(true);
+
+ log.debug("Registering Metrics Servlet");
+ ServletRegistration.Dynamic metricsAdminServlet =
+ servletContext.addServlet("metricsServlet", new MetricsServlet());
+
+ metricsAdminServlet.addMapping("/management/metrics/*");
+ metricsAdminServlet.setAsyncSupported(true);
+ metricsAdminServlet.setLoadOnStartup(2);
+ }
+
+ @Bean
+ public CorsFilter corsFilter() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = jHipsterProperties.getCors();
+ if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
+ log.debug("Registering CORS filter");
+ source.registerCorsConfiguration("/api/**", config);
+ source.registerCorsConfiguration("/v2/api-docs", config);
+ source.registerCorsConfiguration("/*/api/**", config);
+ }
+ return new CorsFilter(source);
+ }
+
+ /**
+ * Initializes H2 console.
+ */
+ private void initH2Console(ServletContext servletContext) {
+ log.debug("Initialize H2 console");
+ ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
+ h2ConsoleServlet.addMapping("/h2-console/*");
+ h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
+ h2ConsoleServlet.setLoadOnStartup(1);
+ }
+
+ @Autowired(required = false)
+ public void setMetricRegistry(MetricRegistry metricRegistry) {
+ this.metricRegistry = metricRegistry;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/apidoc/GatewaySwaggerResourcesProvider.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/apidoc/GatewaySwaggerResourcesProvider.java
new file mode 100644
index 0000000000..1ef6c7e00b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/apidoc/GatewaySwaggerResourcesProvider.java
@@ -0,0 +1,63 @@
+package com.gateway.config.apidoc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.*;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.netflix.zuul.filters.Route;
+import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import springfox.documentation.swagger.web.SwaggerResource;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+
+/**
+ * Retrieves all registered microservices Swagger resources.
+ */
+@Component
+@Primary
+@Profile(JHipsterConstants.SPRING_PROFILE_SWAGGER)
+public class GatewaySwaggerResourcesProvider implements SwaggerResourcesProvider {
+
+ private final Logger log = LoggerFactory.getLogger(GatewaySwaggerResourcesProvider.class);
+
+ private final RouteLocator routeLocator;
+
+ private final DiscoveryClient discoveryClient;
+
+ public GatewaySwaggerResourcesProvider(RouteLocator routeLocator, DiscoveryClient discoveryClient) {
+ this.routeLocator = routeLocator;
+ this.discoveryClient = discoveryClient;
+ }
+
+ @Override
+ public List get() {
+ List resources = new ArrayList<>();
+
+ //Add the default swagger resource that correspond to the gateway's own swagger doc
+ resources.add(swaggerResource("default", "/v2/api-docs"));
+
+ //Add the registered microservices swagger docs as additional swagger resources
+ List routes = routeLocator.getRoutes();
+ routes.forEach(route -> {
+ resources.add(swaggerResource(route.getId(), route.getFullPath().replace("**", "v2/api-docs")));
+ });
+
+ return resources;
+ }
+
+ private SwaggerResource swaggerResource(String name, String location) {
+ SwaggerResource swaggerResource = new SwaggerResource();
+ swaggerResource.setName(name);
+ swaggerResource.setLocation(location);
+ swaggerResource.setSwaggerVersion("2.0");
+ return swaggerResource;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/audit/AuditEventConverter.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/audit/AuditEventConverter.java
new file mode 100644
index 0000000000..60189a42b4
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/audit/AuditEventConverter.java
@@ -0,0 +1,91 @@
+package com.gateway.config.audit;
+
+import com.gateway.domain.PersistentAuditEvent;
+
+import org.springframework.boot.actuate.audit.AuditEvent;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.*;
+
+@Component
+public class AuditEventConverter {
+
+ /**
+ * Convert a list of PersistentAuditEvent to a list of AuditEvent
+ *
+ * @param persistentAuditEvents the list to convert
+ * @return the converted list.
+ */
+ public List convertToAuditEvent(Iterable persistentAuditEvents) {
+ if (persistentAuditEvents == null) {
+ return Collections.emptyList();
+ }
+ List auditEvents = new ArrayList<>();
+ for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) {
+ auditEvents.add(convertToAuditEvent(persistentAuditEvent));
+ }
+ return auditEvents;
+ }
+
+ /**
+ * Convert a PersistentAuditEvent to an AuditEvent
+ *
+ * @param persistentAuditEvent the event to convert
+ * @return the converted list.
+ */
+ public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) {
+ Instant instant = persistentAuditEvent.getAuditEventDate().atZone(ZoneId.systemDefault()).toInstant();
+ return new AuditEvent(Date.from(instant), persistentAuditEvent.getPrincipal(),
+ persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData()));
+ }
+
+ /**
+ * Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface
+ *
+ * @param data the data to convert
+ * @return a map of String, Object
+ */
+ public Map convertDataToObjects(Map data) {
+ Map results = new HashMap<>();
+
+ if (data != null) {
+ for (Map.Entry entry : data.entrySet()) {
+ results.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Internal conversion. This method will allow to save additional data.
+ * By default, it will save the object as string
+ *
+ * @param data the data to convert
+ * @return a map of String, String
+ */
+ public Map convertDataToStrings(Map data) {
+ Map results = new HashMap<>();
+
+ if (data != null) {
+ for (Map.Entry entry : data.entrySet()) {
+ Object object = entry.getValue();
+
+ // Extract the data that will be saved.
+ if (object instanceof WebAuthenticationDetails) {
+ WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) object;
+ results.put("remoteAddress", authenticationDetails.getRemoteAddress());
+ results.put("sessionId", authenticationDetails.getSessionId());
+ } else if (object != null) {
+ results.put(entry.getKey(), object.toString());
+ } else {
+ results.put(entry.getKey(), "null");
+ }
+ }
+ }
+
+ return results;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/audit/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/audit/package-info.java
new file mode 100644
index 0000000000..91e4a657bf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/audit/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Audit specific code.
+ */
+package com.gateway.config.audit;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/CassandraConfiguration.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/CassandraConfiguration.java
new file mode 100644
index 0000000000..b528d8b96b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/CassandraConfiguration.java
@@ -0,0 +1,122 @@
+package com.gateway.config.cassandra;
+
+import io.github.jhipster.config.JHipsterConstants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.cassandra.CassandraProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.util.StringUtils;
+
+import com.codahale.metrics.MetricRegistry;
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.ProtocolVersion;
+import com.datastax.driver.core.QueryOptions;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.SocketOptions;
+import com.datastax.driver.core.policies.LoadBalancingPolicy;
+import com.datastax.driver.core.policies.ReconnectionPolicy;
+import com.datastax.driver.core.policies.RetryPolicy;
+import com.datastax.driver.extras.codecs.jdk8.LocalDateCodec;
+
+@Configuration
+@ConditionalOnProperty("jhipster.gateway.rate-limiting.enabled")
+@EnableConfigurationProperties(CassandraProperties.class)
+@Profile({JHipsterConstants.SPRING_PROFILE_DEVELOPMENT, JHipsterConstants.SPRING_PROFILE_PRODUCTION})
+public class CassandraConfiguration {
+
+ @Value("${spring.data.cassandra.protocolVersion:V4}")
+ private ProtocolVersion protocolVersion;
+
+ @Autowired(required = false)
+ MetricRegistry metricRegistry;
+
+ private final Logger log = LoggerFactory.getLogger(CassandraConfiguration.class);
+
+ @Bean
+ public Cluster cluster(CassandraProperties properties) {
+ Cluster.Builder builder = Cluster.builder()
+ .withClusterName(properties.getClusterName())
+ .withProtocolVersion(protocolVersion)
+ .withPort(getPort(properties));
+
+ if (properties.getUsername() != null) {
+ builder.withCredentials(properties.getUsername(), properties.getPassword());
+ }
+ if (properties.getCompression() != null) {
+ builder.withCompression(properties.getCompression());
+ }
+ if (properties.getLoadBalancingPolicy() != null) {
+ LoadBalancingPolicy policy = instantiate(properties.getLoadBalancingPolicy());
+ builder.withLoadBalancingPolicy(policy);
+ }
+ builder.withQueryOptions(getQueryOptions(properties));
+ if (properties.getReconnectionPolicy() != null) {
+ ReconnectionPolicy policy = instantiate(properties.getReconnectionPolicy());
+ builder.withReconnectionPolicy(policy);
+ }
+ if (properties.getRetryPolicy() != null) {
+ RetryPolicy policy = instantiate(properties.getRetryPolicy());
+ builder.withRetryPolicy(policy);
+ }
+ builder.withSocketOptions(getSocketOptions(properties));
+ if (properties.isSsl()) {
+ builder.withSSL();
+ }
+ String points = properties.getContactPoints();
+ builder.addContactPoints(StringUtils.commaDelimitedListToStringArray(points));
+
+ Cluster cluster = builder.build();
+
+ cluster.getConfiguration().getCodecRegistry()
+ .register(LocalDateCodec.instance)
+ .register(CustomZonedDateTimeCodec.instance);
+
+ if (metricRegistry != null) {
+ cluster.init();
+ metricRegistry.registerAll(cluster.getMetrics().getRegistry());
+ }
+
+ return cluster;
+ }
+
+ protected int getPort(CassandraProperties properties) {
+ return properties.getPort();
+ }
+
+ public static T instantiate(Class type) {
+ return BeanUtils.instantiate(type);
+ }
+
+ private QueryOptions getQueryOptions(CassandraProperties properties) {
+ QueryOptions options = new QueryOptions();
+ if (properties.getConsistencyLevel() != null) {
+ options.setConsistencyLevel(properties.getConsistencyLevel());
+ }
+ if (properties.getSerialConsistencyLevel() != null) {
+ options.setSerialConsistencyLevel(properties.getSerialConsistencyLevel());
+ }
+ options.setFetchSize(properties.getFetchSize());
+ return options;
+ }
+
+ private SocketOptions getSocketOptions(CassandraProperties properties) {
+ SocketOptions options = new SocketOptions();
+ options.setConnectTimeoutMillis(properties.getConnectTimeoutMillis());
+ options.setReadTimeoutMillis(properties.getReadTimeoutMillis());
+ return options;
+ }
+
+ @Bean(destroyMethod = "close")
+ public Session session(CassandraProperties properties, Cluster cluster) {
+ log.debug("Configuring Cassandra session");
+ return StringUtils.hasText(properties.getKeyspaceName()) ? cluster.connect(properties.getKeyspaceName()) : cluster.connect();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/CustomZonedDateTimeCodec.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/CustomZonedDateTimeCodec.java
new file mode 100644
index 0000000000..5cdab7ac5b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/CustomZonedDateTimeCodec.java
@@ -0,0 +1,93 @@
+package com.gateway.config.cassandra;
+
+import com.datastax.driver.core.*;
+import com.datastax.driver.core.exceptions.InvalidTypeException;
+
+import java.nio.ByteBuffer;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+
+import static com.datastax.driver.core.ParseUtils.isLongLiteral;
+import static com.datastax.driver.core.ParseUtils.quote;
+import static java.time.temporal.ChronoField.*;
+
+public class CustomZonedDateTimeCodec extends TypeCodec {
+
+ public static final CustomZonedDateTimeCodec instance = new CustomZonedDateTimeCodec();
+
+ private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
+ .parseCaseSensitive()
+ .parseStrict()
+ .append(DateTimeFormatter.ISO_LOCAL_DATE)
+ .optionalStart()
+ .appendLiteral('T')
+ .appendValue(HOUR_OF_DAY, 2)
+ .appendLiteral(':')
+ .appendValue(MINUTE_OF_HOUR, 2)
+ .optionalEnd()
+ .optionalStart()
+ .appendLiteral(':')
+ .appendValue(SECOND_OF_MINUTE, 2)
+ .optionalEnd()
+ .optionalStart()
+ .appendFraction(NANO_OF_SECOND, 0, 9, true)
+ .optionalEnd()
+ .optionalStart()
+ .appendZoneOrOffsetId()
+ .optionalEnd()
+ .toFormatter()
+ .withZone(ZoneOffset.UTC);
+
+ private CustomZonedDateTimeCodec() {
+ super(DataType.timestamp(), ZonedDateTime.class);
+ }
+
+ @Override
+ public ByteBuffer serialize(ZonedDateTime value, ProtocolVersion protocolVersion) {
+ if (value == null) {
+ return null;
+ }
+ long millis = value.toInstant().toEpochMilli();
+ return bigint().serializeNoBoxing(millis, protocolVersion);
+ }
+
+ @Override
+ public ZonedDateTime deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) {
+ if (bytes == null || bytes.remaining() == 0) {
+ return null;
+ }
+ long millis = bigint().deserializeNoBoxing(bytes, protocolVersion);
+ return Instant.ofEpochMilli(millis).atZone(ZoneOffset.UTC);
+ }
+
+ @Override
+ public String format(ZonedDateTime value) {
+ return quote(FORMATTER.format(value));
+ }
+
+ @Override
+ public ZonedDateTime parse(String value) {
+ // strip enclosing single quotes, if any
+ if (ParseUtils.isQuoted(value)) {
+ value = ParseUtils.unquote(value);
+ }
+ if (isLongLiteral(value)) {
+ try {
+ long millis = Long.parseLong(value);
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.UTC);
+ } catch (NumberFormatException e) {
+ throw new InvalidTypeException(String.format("Cannot parse timestamp value from \"%s\"", value));
+ }
+ }
+ try {
+ return ZonedDateTime.from(FORMATTER.parse(value));
+ } catch (DateTimeParseException e) {
+ throw new InvalidTypeException(String.format("Cannot parse timestamp value from \"%s\"", value));
+ }
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/package-info.java
new file mode 100644
index 0000000000..a77f49c447
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/cassandra/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Cassandra specific configuration.
+ */
+package com.gateway.config.cassandra;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/package-info.java
new file mode 100644
index 0000000000..00f6440bfb
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/config/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring Framework configuration files.
+ */
+package com.gateway.config;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/AbstractAuditingEntity.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/AbstractAuditingEntity.java
new file mode 100644
index 0000000000..1c270ab70e
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/AbstractAuditingEntity.java
@@ -0,0 +1,80 @@
+package com.gateway.domain;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.hibernate.envers.Audited;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+import java.time.ZonedDateTime;
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+
+/**
+ * Base abstract class for entities which will hold definitions for created, last modified by and created,
+ * last modified by date.
+ */
+@MappedSuperclass
+@Audited
+@EntityListeners(AuditingEntityListener.class)
+public abstract class AbstractAuditingEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @CreatedBy
+ @Column(name = "created_by", nullable = false, length = 50, updatable = false)
+ @JsonIgnore
+ private String createdBy;
+
+ @CreatedDate
+ @Column(name = "created_date", nullable = false)
+ @JsonIgnore
+ private ZonedDateTime createdDate = ZonedDateTime.now();
+
+ @LastModifiedBy
+ @Column(name = "last_modified_by", length = 50)
+ @JsonIgnore
+ private String lastModifiedBy;
+
+ @LastModifiedDate
+ @Column(name = "last_modified_date")
+ @JsonIgnore
+ private ZonedDateTime lastModifiedDate = ZonedDateTime.now();
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public ZonedDateTime getCreatedDate() {
+ return createdDate;
+ }
+
+ public void setCreatedDate(ZonedDateTime createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ public String getLastModifiedBy() {
+ return lastModifiedBy;
+ }
+
+ public void setLastModifiedBy(String lastModifiedBy) {
+ this.lastModifiedBy = lastModifiedBy;
+ }
+
+ public ZonedDateTime getLastModifiedDate() {
+ return lastModifiedDate;
+ }
+
+ public void setLastModifiedDate(ZonedDateTime lastModifiedDate) {
+ this.lastModifiedDate = lastModifiedDate;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/Authority.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/Authority.java
new file mode 100644
index 0000000000..ede3ced0d2
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/Authority.java
@@ -0,0 +1,66 @@
+package com.gateway.domain;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Column;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+
+/**
+ * An authority (a security role) used by Spring Security.
+ */
+@Entity
+@Table(name = "jhi_authority")
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+public class Authority implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @NotNull
+ @Size(min = 0, max = 50)
+ @Id
+ @Column(length = 50)
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Authority authority = (Authority) o;
+
+ if (name != null ? !name.equals(authority.name) : authority.name != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name != null ? name.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Authority{" +
+ "name='" + name + '\'' +
+ "}";
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/PersistentAuditEvent.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/PersistentAuditEvent.java
new file mode 100644
index 0000000000..4bc123af1e
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/PersistentAuditEvent.java
@@ -0,0 +1,78 @@
+package com.gateway.domain;
+
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Persist AuditEvent managed by the Spring Boot actuator
+ * @see org.springframework.boot.actuate.audit.AuditEvent
+ */
+@Entity
+@Table(name = "jhi_persistent_audit_event")
+public class PersistentAuditEvent implements Serializable {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "event_id")
+ private Long id;
+
+ @NotNull
+ @Column(nullable = false)
+ private String principal;
+
+ @Column(name = "event_date")
+ private LocalDateTime auditEventDate;
+ @Column(name = "event_type")
+ private String auditEventType;
+
+ @ElementCollection
+ @MapKeyColumn(name = "name")
+ @Column(name = "value")
+ @CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id"))
+ private Map data = new HashMap<>();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getPrincipal() {
+ return principal;
+ }
+
+ public void setPrincipal(String principal) {
+ this.principal = principal;
+ }
+
+ public LocalDateTime getAuditEventDate() {
+ return auditEventDate;
+ }
+
+ public void setAuditEventDate(LocalDateTime auditEventDate) {
+ this.auditEventDate = auditEventDate;
+ }
+
+ public String getAuditEventType() {
+ return auditEventType;
+ }
+
+ public void setAuditEventType(String auditEventType) {
+ this.auditEventType = auditEventType;
+ }
+
+ public Map getData() {
+ return data;
+ }
+
+ public void setData(Map data) {
+ this.data = data;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/User.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/User.java
new file mode 100644
index 0000000000..1dfab5f93c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/User.java
@@ -0,0 +1,235 @@
+package com.gateway.domain;
+
+import com.gateway.config.Constants;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.hibernate.annotations.BatchSize;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.validator.constraints.Email;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.time.ZonedDateTime;
+
+/**
+ * A user.
+ */
+@Entity
+@Table(name = "jhi_user")
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+public class User extends AbstractAuditingEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotNull
+ @Pattern(regexp = Constants.LOGIN_REGEX)
+ @Size(min = 1, max = 50)
+ @Column(length = 50, unique = true, nullable = false)
+ private String login;
+
+ @JsonIgnore
+ @NotNull
+ @Size(min = 60, max = 60)
+ @Column(name = "password_hash",length = 60)
+ private String password;
+
+ @Size(max = 50)
+ @Column(name = "first_name", length = 50)
+ private String firstName;
+
+ @Size(max = 50)
+ @Column(name = "last_name", length = 50)
+ private String lastName;
+
+ @Email
+ @Size(max = 100)
+ @Column(length = 100, unique = true)
+ private String email;
+
+ @NotNull
+ @Column(nullable = false)
+ private boolean activated = false;
+
+ @Size(min = 2, max = 5)
+ @Column(name = "lang_key", length = 5)
+ private String langKey;
+
+ @Size(max = 256)
+ @Column(name = "image_url", length = 256)
+ private String imageUrl;
+
+ @Size(max = 20)
+ @Column(name = "activation_key", length = 20)
+ @JsonIgnore
+ private String activationKey;
+
+ @Size(max = 20)
+ @Column(name = "reset_key", length = 20)
+ private String resetKey;
+
+ @Column(name = "reset_date")
+ private ZonedDateTime resetDate = null;
+
+ @JsonIgnore
+ @ManyToMany
+ @JoinTable(
+ name = "jhi_user_authority",
+ joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
+ inverseJoinColumns = {@JoinColumn(name = "authority_name", referencedColumnName = "name")})
+ @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+ @BatchSize(size = 20)
+ private Set authorities = new HashSet<>();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ //Lowercase the login before saving it in database
+ public void setLogin(String login) {
+ this.login = login.toLowerCase(Locale.ENGLISH);
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
+ public boolean getActivated() {
+ return activated;
+ }
+
+ public void setActivated(boolean activated) {
+ this.activated = activated;
+ }
+
+ public String getActivationKey() {
+ return activationKey;
+ }
+
+ public void setActivationKey(String activationKey) {
+ this.activationKey = activationKey;
+ }
+
+ public String getResetKey() {
+ return resetKey;
+ }
+
+ public void setResetKey(String resetKey) {
+ this.resetKey = resetKey;
+ }
+
+ public ZonedDateTime getResetDate() {
+ return resetDate;
+ }
+
+ public void setResetDate(ZonedDateTime resetDate) {
+ this.resetDate = resetDate;
+ }
+
+ public String getLangKey() {
+ return langKey;
+ }
+
+ public void setLangKey(String langKey) {
+ this.langKey = langKey;
+ }
+
+ public Set getAuthorities() {
+ return authorities;
+ }
+
+ public void setAuthorities(Set authorities) {
+ this.authorities = authorities;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ User user = (User) o;
+
+ if (!login.equals(user.login)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return login.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "login='" + login + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", email='" + email + '\'' +
+ ", imageUrl='" + imageUrl + '\'' +
+ ", activated='" + activated + '\'' +
+ ", langKey='" + langKey + '\'' +
+ ", activationKey='" + activationKey + '\'' +
+ "}";
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/package-info.java
new file mode 100644
index 0000000000..d56162dc07
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/domain/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * JPA domain objects.
+ */
+package com.gateway.domain;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/TokenRelayFilter.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/TokenRelayFilter.java
new file mode 100644
index 0000000000..ae057b8b00
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/TokenRelayFilter.java
@@ -0,0 +1,36 @@
+package com.gateway.gateway;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+@Component
+public class TokenRelayFilter extends ZuulFilter {
+ @Override
+ public Object run() {
+ RequestContext ctx = RequestContext.getCurrentContext();
+
+ Set headers = (Set) ctx.get("ignoredHeaders");
+ // We need our JWT tokens relayed to resource servers
+ headers.remove("authorization");
+
+ return null;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ return true;
+ }
+
+ @Override
+ public String filterType() {
+ return "pre";
+ }
+
+ @Override
+ public int filterOrder() {
+ return 10000;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/accesscontrol/AccessControlFilter.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/accesscontrol/AccessControlFilter.java
new file mode 100644
index 0000000000..b2d9800c4d
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/accesscontrol/AccessControlFilter.java
@@ -0,0 +1,100 @@
+package com.gateway.gateway.accesscontrol;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.netflix.zuul.filters.Route;
+import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
+import org.springframework.http.HttpStatus;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+
+/**
+ * Zuul filter for restricting access to backend micro-services endpoints.
+ */
+public class AccessControlFilter extends ZuulFilter {
+
+ private final Logger log = LoggerFactory.getLogger(AccessControlFilter.class);
+
+ private final RouteLocator routeLocator;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public AccessControlFilter(RouteLocator routeLocator, JHipsterProperties jHipsterProperties) {
+ this.routeLocator = routeLocator;
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @Override
+ public String filterType() {
+ return "pre";
+ }
+
+ @Override
+ public int filterOrder() {
+ return 0;
+ }
+
+ /**
+ * Filter requests on endpoints that are not in the list of authorized microservices endpoints.
+ */
+ @Override
+ public boolean shouldFilter() {
+ String requestUri = RequestContext.getCurrentContext().getRequest().getRequestURI();
+
+ // If the request Uri does not start with the path of the authorized endpoints, we block the request
+ for (Route route : routeLocator.getRoutes()) {
+ String serviceUrl = route.getFullPath();
+ String serviceName = route.getId();
+
+ // If this route correspond to the current request URI
+ // We do a substring to remove the "**" at the end of the route URL
+ if (requestUri.startsWith(serviceUrl.substring(0, serviceUrl.length() - 2))) {
+ return !isAuthorizedRequest(serviceUrl, serviceName, requestUri);
+ }
+ }
+ return true;
+ }
+
+ private boolean isAuthorizedRequest(String serviceUrl, String serviceName, String requestUri) {
+ Map> authorizedMicroservicesEndpoints = jHipsterProperties.getGateway()
+ .getAuthorizedMicroservicesEndpoints();
+
+ // If the authorized endpoints list was left empty for this route, all access are allowed
+ if (authorizedMicroservicesEndpoints.get(serviceName) == null) {
+ log.debug("Access Control: allowing access for {}, as no access control policy has been set up for " +
+ "service: {}", requestUri, serviceName);
+ return true;
+ } else {
+ List authorizedEndpoints = authorizedMicroservicesEndpoints.get(serviceName);
+
+ // Go over the authorized endpoints to control that the request URI matches it
+ for (String endpoint : authorizedEndpoints) {
+ // We do a substring to remove the "**/" at the end of the route URL
+ String gatewayEndpoint = serviceUrl.substring(0, serviceUrl.length() - 3) + endpoint;
+ if (requestUri.startsWith(gatewayEndpoint)) {
+ log.debug("Access Control: allowing access for {}, as it matches the following authorized " +
+ "microservice endpoint: {}", requestUri, gatewayEndpoint);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Object run() {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ ctx.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
+ if (ctx.getResponseBody() == null && !ctx.getResponseGZipped()) {
+ ctx.setSendZuulResponse(false);
+ }
+ log.debug("Access Control: filtered unauthorized access on endpoint {}", ctx.getRequest().getRequestURI());
+ return null;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/ratelimiting/RateLimitingFilter.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/ratelimiting/RateLimitingFilter.java
new file mode 100644
index 0000000000..e065491b07
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/ratelimiting/RateLimitingFilter.java
@@ -0,0 +1,103 @@
+package com.gateway.gateway.ratelimiting;
+
+import com.gateway.security.SecurityUtils;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import java.util.Calendar;
+import java.util.Date;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+
+/**
+ * Zuul filter for limiting the number of HTTP calls per client.
+ */
+public class RateLimitingFilter extends ZuulFilter {
+
+ private final Logger log = LoggerFactory.getLogger(RateLimitingFilter.class);
+
+ private static final String TIME_PERIOD = "hour";
+
+ private long rateLimit = 100000L;
+
+ private final RateLimitingRepository rateLimitingRepository;
+
+ public RateLimitingFilter(RateLimitingRepository rateLimitingRepository, JHipsterProperties jHipsterProperties) {
+ this.rateLimitingRepository = rateLimitingRepository;
+ this.rateLimit = jHipsterProperties.getGateway().getRateLimiting().getLimit();
+ }
+
+ @Override
+ public String filterType() {
+ return "pre";
+ }
+
+ @Override
+ public int filterOrder() {
+ return 10;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ // specific APIs can be filtered out using
+ // if (RequestContext.getCurrentContext().getRequest().getRequestURI().startsWith("/api")) { ... }
+ return true;
+ }
+
+ @Override
+ public Object run() {
+ String id = getId(RequestContext.getCurrentContext().getRequest());
+ Date date = getPeriod();
+
+ // check current rate limit
+ // default limit per user is 100,000 API calls per hour
+ Long count = rateLimitingRepository.getCounter(id, TIME_PERIOD, date);
+ log.debug("Rate limiting for user {} at {} - {}", id, date, count);
+ if (count > rateLimit) {
+ apiLimitExceeded();
+ } else {
+ // count calls per hour
+ rateLimitingRepository.incrementCounter(id, TIME_PERIOD, date);
+ }
+ return null;
+ }
+
+ private void apiLimitExceeded() {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ ctx.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
+ if (ctx.getResponseBody() == null) {
+ ctx.setResponseBody("API rate limit exceeded");
+ ctx.setSendZuulResponse(false);
+ }
+ }
+
+ /**
+ * The ID that will identify the limit: the user login or the user IP address.
+ */
+ private String getId(HttpServletRequest httpServletRequest) {
+ String login = SecurityUtils.getCurrentUserLogin();
+ if (login != null) {
+ return login;
+ } else {
+ return httpServletRequest.getRemoteAddr();
+ }
+ }
+
+ /**
+ * The period for which the rate is calculated.
+ */
+ private Date getPeriod() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear(Calendar.MILLISECOND);
+ calendar.clear(Calendar.SECOND);
+ calendar.clear(Calendar.MINUTE);
+ return calendar.getTime();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/ratelimiting/RateLimitingRepository.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/ratelimiting/RateLimitingRepository.java
new file mode 100644
index 0000000000..9c710f1b46
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/ratelimiting/RateLimitingRepository.java
@@ -0,0 +1,51 @@
+package com.gateway.gateway.ratelimiting;
+
+import java.util.Date;
+
+import com.datastax.driver.core.*;
+
+/**
+ * Repository storing data used by the gateway's rate limiting filter.
+ */
+public class RateLimitingRepository {
+
+ private final Session session;
+
+ private PreparedStatement rateLimitingIncrement;
+
+ private PreparedStatement rateLimitingCount;
+
+ public RateLimitingRepository(Session session) {
+ this.session = session;
+ this.rateLimitingIncrement = session.prepare(
+ "UPDATE gateway_ratelimiting\n" +
+ " SET value = value + 1\n" +
+ " WHERE id = :id AND time_unit = :time_unit AND time = :time");
+
+ this.rateLimitingCount = session.prepare(
+ "SELECT value\n" +
+ " FROM gateway_ratelimiting\n" +
+ " WHERE id = :id AND time_unit = :time_unit AND time = :time"
+ );
+ }
+
+ public void incrementCounter(String id, String timeUnit, Date time) {
+ BoundStatement stmt = rateLimitingIncrement.bind();
+ stmt.setString("id", id);
+ stmt.setString("time_unit", timeUnit);
+ stmt.setTimestamp("time", time);
+ session.executeAsync(stmt);
+ }
+
+ public long getCounter(String id, String timeUnit, Date time) {
+ BoundStatement stmt = rateLimitingCount.bind();
+ stmt.setString("id", id);
+ stmt.setString("time_unit", timeUnit);
+ stmt.setTimestamp("time", time);
+ ResultSet rs = session.execute(stmt);
+ if (rs.isExhausted()) {
+ return 0;
+ }
+ return rs.one().getLong(0);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/responserewriting/SwaggerBasePathRewritingFilter.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/responserewriting/SwaggerBasePathRewritingFilter.java
new file mode 100644
index 0000000000..43b1ec4f5a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/gateway/responserewriting/SwaggerBasePathRewritingFilter.java
@@ -0,0 +1,78 @@
+package com.gateway.gateway.responserewriting;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.netflix.zuul.context.RequestContext;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter;
+import springfox.documentation.swagger2.web.Swagger2Controller;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Zuul filter to rewrite micro-services Swagger URL Base Path.
+ */
+public class SwaggerBasePathRewritingFilter extends SendResponseFilter {
+
+ private final Logger log = LoggerFactory.getLogger(SwaggerBasePathRewritingFilter.class);
+
+ private ObjectMapper mapper = new ObjectMapper();
+
+ @Override
+ public String filterType() {
+ return "post";
+ }
+
+ @Override
+ public int filterOrder() {
+ return 100;
+ }
+
+ /**
+ * Filter requests to micro-services Swagger docs.
+ */
+ @Override
+ public boolean shouldFilter() {
+ return RequestContext.getCurrentContext().getRequest().getRequestURI().endsWith(Swagger2Controller.DEFAULT_URL);
+ }
+
+ @Override
+ public Object run() {
+ RequestContext context = RequestContext.getCurrentContext();
+
+ if (!context.getResponseGZipped()) {
+ context.getResponse().setCharacterEncoding("UTF-8");
+ }
+
+ String rewrittenResponse = rewriteBasePath(context);
+ context.setResponseBody(rewrittenResponse);
+ return null;
+ }
+
+ private String rewriteBasePath(RequestContext context) {
+ InputStream responseDataStream = context.getResponseDataStream();
+ String requestUri = RequestContext.getCurrentContext().getRequest().getRequestURI();
+ try {
+ if (context.getResponseGZipped()) {
+ responseDataStream = new GZIPInputStream(context.getResponseDataStream());
+ }
+ String response = IOUtils.toString(responseDataStream, StandardCharsets.UTF_8);
+ if (response != null) {
+ LinkedHashMap map = this.mapper.readValue(response, LinkedHashMap.class);
+
+ String basePath = requestUri.replace(Swagger2Controller.DEFAULT_URL,"");
+ map.put("basePath",basePath);
+ log.debug("Swagger-docs: rewritten Base URL with correct micro-service route: {}", basePath);
+ return mapper.writeValueAsString(map);
+ }
+ } catch (IOException e) {
+ log.error("Swagger-docs filter error", e);
+ }
+ return null;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/AuthorityRepository.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/AuthorityRepository.java
new file mode 100644
index 0000000000..553c8913b6
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/AuthorityRepository.java
@@ -0,0 +1,11 @@
+package com.gateway.repository;
+
+import com.gateway.domain.Authority;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * Spring Data JPA repository for the Authority entity.
+ */
+public interface AuthorityRepository extends JpaRepository {
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/CustomAuditEventRepository.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/CustomAuditEventRepository.java
new file mode 100644
index 0000000000..6eb893f095
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/CustomAuditEventRepository.java
@@ -0,0 +1,81 @@
+package com.gateway.repository;
+
+import com.gateway.config.Constants;
+import com.gateway.config.audit.AuditEventConverter;
+import com.gateway.domain.PersistentAuditEvent;
+
+import org.springframework.boot.actuate.audit.AuditEvent;
+import org.springframework.boot.actuate.audit.AuditEventRepository;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * An implementation of Spring Boot's AuditEventRepository.
+ */
+@Repository
+public class CustomAuditEventRepository implements AuditEventRepository {
+
+ private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
+
+ private final PersistenceAuditEventRepository persistenceAuditEventRepository;
+
+ private final AuditEventConverter auditEventConverter;
+
+ public CustomAuditEventRepository(PersistenceAuditEventRepository persistenceAuditEventRepository,
+ AuditEventConverter auditEventConverter) {
+
+ this.persistenceAuditEventRepository = persistenceAuditEventRepository;
+ this.auditEventConverter = auditEventConverter;
+ }
+
+ @Override
+ public List find(Date after) {
+ Iterable persistentAuditEvents =
+ persistenceAuditEventRepository.findByAuditEventDateAfter(LocalDateTime.from(after.toInstant()));
+ return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
+ }
+
+ @Override
+ public List find(String principal, Date after) {
+ Iterable persistentAuditEvents;
+ if (principal == null && after == null) {
+ persistentAuditEvents = persistenceAuditEventRepository.findAll();
+ } else if (after == null) {
+ persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal);
+ } else {
+ persistentAuditEvents =
+ persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant()));
+ }
+ return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
+ }
+
+ @Override
+ public List find(String principal, Date after, String type) {
+ Iterable persistentAuditEvents =
+ persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfterAndAuditEventType(principal, LocalDateTime.from(after.toInstant()), type);
+ return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
+ }
+
+ @Override
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
+ public void add(AuditEvent event) {
+ if (!AUTHORIZATION_FAILURE.equals(event.getType()) &&
+ !Constants.ANONYMOUS_USER.equals(event.getPrincipal())) {
+
+ PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
+ persistentAuditEvent.setPrincipal(event.getPrincipal());
+ persistentAuditEvent.setAuditEventType(event.getType());
+ Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime());
+ persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
+ persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData()));
+ persistenceAuditEventRepository.save(persistentAuditEvent);
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/PersistenceAuditEventRepository.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/PersistenceAuditEventRepository.java
new file mode 100644
index 0000000000..7c6ba05d54
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/PersistenceAuditEventRepository.java
@@ -0,0 +1,26 @@
+package com.gateway.repository;
+
+import com.gateway.domain.PersistentAuditEvent;
+
+import java.time.LocalDateTime;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+
+/**
+ * Spring Data JPA repository for the PersistentAuditEvent entity.
+ */
+public interface PersistenceAuditEventRepository extends JpaRepository {
+
+ List findByPrincipal(String principal);
+
+ List findByAuditEventDateAfter(LocalDateTime after);
+
+ List findByPrincipalAndAuditEventDateAfter(String principal, LocalDateTime after);
+
+ List findByPrincipalAndAuditEventDateAfterAndAuditEventType(String principle, LocalDateTime after, String type);
+
+ Page findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable);
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/UserRepository.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/UserRepository.java
new file mode 100644
index 0000000000..41b10f2ecf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/UserRepository.java
@@ -0,0 +1,37 @@
+package com.gateway.repository;
+
+import com.gateway.domain.User;
+
+import java.time.ZonedDateTime;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Spring Data JPA repository for the User entity.
+ */
+public interface UserRepository extends JpaRepository {
+
+ Optional findOneByActivationKey(String activationKey);
+
+ List findAllByActivatedIsFalseAndCreatedDateBefore(ZonedDateTime dateTime);
+
+ Optional findOneByResetKey(String resetKey);
+
+ Optional findOneByEmail(String email);
+
+ Optional findOneByLogin(String login);
+
+ @EntityGraph(attributePaths = "authorities")
+ User findOneWithAuthoritiesById(Long id);
+
+ @EntityGraph(attributePaths = "authorities")
+ Optional findOneWithAuthoritiesByLogin(String login);
+
+ Page findAllByLoginNot(Pageable pageable, String login);
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/package-info.java
new file mode 100644
index 0000000000..f15a3e98c6
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/repository/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring Data JPA repositories.
+ */
+package com.gateway.repository;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/AuthoritiesConstants.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/AuthoritiesConstants.java
new file mode 100644
index 0000000000..bdf81f928b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/AuthoritiesConstants.java
@@ -0,0 +1,16 @@
+package com.gateway.security;
+
+/**
+ * Constants for Spring Security authorities.
+ */
+public final class AuthoritiesConstants {
+
+ public static final String ADMIN = "ROLE_ADMIN";
+
+ public static final String USER = "ROLE_USER";
+
+ public static final String ANONYMOUS = "ROLE_ANONYMOUS";
+
+ private AuthoritiesConstants() {
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/DomainUserDetailsService.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/DomainUserDetailsService.java
new file mode 100644
index 0000000000..e175d83cb9
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/DomainUserDetailsService.java
@@ -0,0 +1,51 @@
+package com.gateway.security;
+
+import com.gateway.domain.User;
+import com.gateway.repository.UserRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Authenticate a user from the database.
+ */
+@Component("userDetailsService")
+public class DomainUserDetailsService implements UserDetailsService {
+
+ private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);
+
+ private final UserRepository userRepository;
+
+ public DomainUserDetailsService(UserRepository userRepository) {
+ this.userRepository = userRepository;
+ }
+
+ @Override
+ @Transactional
+ public UserDetails loadUserByUsername(final String login) {
+ log.debug("Authenticating {}", login);
+ String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
+ Optional userFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
+ return userFromDatabase.map(user -> {
+ if (!user.getActivated()) {
+ throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
+ }
+ List grantedAuthorities = user.getAuthorities().stream()
+ .map(authority -> new SimpleGrantedAuthority(authority.getName()))
+ .collect(Collectors.toList());
+ return new org.springframework.security.core.userdetails.User(lowercaseLogin,
+ user.getPassword(),
+ grantedAuthorities);
+ }).orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " +
+ "database"));
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/SecurityUtils.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/SecurityUtils.java
new file mode 100644
index 0000000000..5fed368fe3
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/SecurityUtils.java
@@ -0,0 +1,68 @@
+package com.gateway.security;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * Utility class for Spring Security.
+ */
+public final class SecurityUtils {
+
+ private SecurityUtils() {
+ }
+
+ /**
+ * Get the login of the current user.
+ *
+ * @return the login of the current user
+ */
+ public static String getCurrentUserLogin() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ String userName = null;
+ if (authentication != null) {
+ if (authentication.getPrincipal() instanceof UserDetails) {
+ UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
+ userName = springSecurityUser.getUsername();
+ } else if (authentication.getPrincipal() instanceof String) {
+ userName = (String) authentication.getPrincipal();
+ }
+ }
+ return userName;
+ }
+
+ /**
+ * Check if a user is authenticated.
+ *
+ * @return true if the user is authenticated, false otherwise
+ */
+ public static boolean isAuthenticated() {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ if (authentication != null) {
+ return authentication.getAuthorities().stream()
+ .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS));
+ }
+ return false;
+ }
+
+ /**
+ * If the current user has a specific authority (security role).
+ *
+ *
The name of this method comes from the isUserInRole() method in the Servlet API
+ *
+ * @param authority the authority to check
+ * @return true if the current user has the authority, false otherwise
+ */
+ public static boolean isCurrentUserInRole(String authority) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ if (authentication != null) {
+ return authentication.getAuthorities().stream()
+ .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
+ }
+ return false;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/SpringSecurityAuditorAware.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/SpringSecurityAuditorAware.java
new file mode 100644
index 0000000000..f1dd81f129
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/SpringSecurityAuditorAware.java
@@ -0,0 +1,19 @@
+package com.gateway.security;
+
+import com.gateway.config.Constants;
+
+import org.springframework.data.domain.AuditorAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Implementation of AuditorAware based on Spring Security.
+ */
+@Component
+public class SpringSecurityAuditorAware implements AuditorAware {
+
+ @Override
+ public String getCurrentAuditor() {
+ String userName = SecurityUtils.getCurrentUserLogin();
+ return userName != null ? userName : Constants.SYSTEM_ACCOUNT;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/UserNotActivatedException.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/UserNotActivatedException.java
new file mode 100644
index 0000000000..d8bd57ccfe
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/UserNotActivatedException.java
@@ -0,0 +1,19 @@
+package com.gateway.security;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * This exception is thrown in case of a not activated user trying to authenticate.
+ */
+public class UserNotActivatedException extends AuthenticationException {
+
+ private static final long serialVersionUID = 1L;
+
+ public UserNotActivatedException(String message) {
+ super(message);
+ }
+
+ public UserNotActivatedException(String message, Throwable t) {
+ super(message, t);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/JWTConfigurer.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/JWTConfigurer.java
new file mode 100644
index 0000000000..a673e98b3e
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/JWTConfigurer.java
@@ -0,0 +1,23 @@
+package com.gateway.security.jwt;
+
+import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+public class JWTConfigurer extends SecurityConfigurerAdapter {
+
+ public static final String AUTHORIZATION_HEADER = "Authorization";
+
+ private TokenProvider tokenProvider;
+
+ public JWTConfigurer(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+ JWTFilter customFilter = new JWTFilter(tokenProvider);
+ http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/JWTFilter.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/JWTFilter.java
new file mode 100644
index 0000000000..58462ce4a8
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/JWTFilter.java
@@ -0,0 +1,58 @@
+package com.gateway.security.jwt;
+
+import java.io.IOException;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.GenericFilterBean;
+
+import io.jsonwebtoken.ExpiredJwtException;
+
+/**
+ * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is
+ * found.
+ */
+public class JWTFilter extends GenericFilterBean {
+
+ private final Logger log = LoggerFactory.getLogger(JWTFilter.class);
+
+ private TokenProvider tokenProvider;
+
+ public JWTFilter(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+ try {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+ String jwt = resolveToken(httpServletRequest);
+ if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
+ Authentication authentication = this.tokenProvider.getAuthentication(jwt);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ } catch (ExpiredJwtException eje) {
+ log.info("Security exception for user {} - {}",
+ eje.getClaims().getSubject(), eje.getMessage());
+
+ log.trace("Security exception trace: {}", eje);
+ ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+
+ private String resolveToken(HttpServletRequest request){
+ String bearerToken = request.getHeader(JWTConfigurer.AUTHORIZATION_HEADER);
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
+ return bearerToken.substring(7, bearerToken.length());
+ }
+ return null;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/TokenProvider.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/TokenProvider.java
new file mode 100644
index 0000000000..5ffb55f33e
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/jwt/TokenProvider.java
@@ -0,0 +1,109 @@
+package com.gateway.security.jwt;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import javax.annotation.PostConstruct;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.stereotype.Component;
+
+import io.jsonwebtoken.*;
+
+@Component
+public class TokenProvider {
+
+ private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
+
+ private static final String AUTHORITIES_KEY = "auth";
+
+ private String secretKey;
+
+ private long tokenValidityInMilliseconds;
+
+ private long tokenValidityInMillisecondsForRememberMe;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public TokenProvider(JHipsterProperties jHipsterProperties) {
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @PostConstruct
+ public void init() {
+ this.secretKey =
+ jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret();
+
+ this.tokenValidityInMilliseconds =
+ 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds();
+ this.tokenValidityInMillisecondsForRememberMe =
+ 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe();
+ }
+
+ public String createToken(Authentication authentication, Boolean rememberMe) {
+ String authorities = authentication.getAuthorities().stream()
+ .map(GrantedAuthority::getAuthority)
+ .collect(Collectors.joining(","));
+
+ long now = (new Date()).getTime();
+ Date validity;
+ if (rememberMe) {
+ validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
+ } else {
+ validity = new Date(now + this.tokenValidityInMilliseconds);
+ }
+
+ return Jwts.builder()
+ .setSubject(authentication.getName())
+ .claim(AUTHORITIES_KEY, authorities)
+ .signWith(SignatureAlgorithm.HS512, secretKey)
+ .setExpiration(validity)
+ .compact();
+ }
+
+ public Authentication getAuthentication(String token) {
+ Claims claims = Jwts.parser()
+ .setSigningKey(secretKey)
+ .parseClaimsJws(token)
+ .getBody();
+
+ Collection extends GrantedAuthority> authorities =
+ Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
+ .map(SimpleGrantedAuthority::new)
+ .collect(Collectors.toList());
+
+ User principal = new User(claims.getSubject(), "", authorities);
+
+ return new UsernamePasswordAuthenticationToken(principal, "", authorities);
+ }
+
+ public boolean validateToken(String authToken) {
+ try {
+ Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
+ return true;
+ } catch (SignatureException e) {
+ log.info("Invalid JWT signature.");
+ log.trace("Invalid JWT signature trace: {}", e);
+ } catch (MalformedJwtException e) {
+ log.info("Invalid JWT token.");
+ log.trace("Invalid JWT token trace: {}", e);
+ } catch (ExpiredJwtException e) {
+ log.info("Expired JWT token.");
+ log.trace("Expired JWT token trace: {}", e);
+ } catch (UnsupportedJwtException e) {
+ log.info("Unsupported JWT token.");
+ log.trace("Unsupported JWT token trace: {}", e);
+ } catch (IllegalArgumentException e) {
+ log.info("JWT token compact of handler are invalid.");
+ log.trace("JWT token compact of handler are invalid trace: {}", e);
+ }
+ return false;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/package-info.java
new file mode 100644
index 0000000000..d834be18bf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/security/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring Security configuration.
+ */
+package com.gateway.security;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/AuditEventService.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/AuditEventService.java
new file mode 100644
index 0000000000..64c63a7938
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/AuditEventService.java
@@ -0,0 +1,50 @@
+package com.gateway.service;
+
+import com.gateway.config.audit.AuditEventConverter;
+import com.gateway.repository.PersistenceAuditEventRepository;
+import java.time.LocalDateTime;
+import org.springframework.boot.actuate.audit.AuditEvent;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Optional;
+
+/**
+ * Service for managing audit events.
+ *
+ * This is the default implementation to support SpringBoot Actuator AuditEventRepository
+ *
+ * We use the @Async annotation to send e-mails asynchronously.
+ *
+ */
+@Service
+public class MailService {
+
+ private final Logger log = LoggerFactory.getLogger(MailService.class);
+
+ private static final String USER = "user";
+
+ private static final String BASE_URL = "baseUrl";
+
+ private final JHipsterProperties jHipsterProperties;
+
+ private final JavaMailSender javaMailSender;
+
+ private final MessageSource messageSource;
+
+ private final SpringTemplateEngine templateEngine;
+
+ public MailService(JHipsterProperties jHipsterProperties, JavaMailSender javaMailSender,
+ MessageSource messageSource, SpringTemplateEngine templateEngine) {
+
+ this.jHipsterProperties = jHipsterProperties;
+ this.javaMailSender = javaMailSender;
+ this.messageSource = messageSource;
+ this.templateEngine = templateEngine;
+ }
+
+ @Async
+ public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) {
+ log.debug("Send e-mail[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}",
+ isMultipart, isHtml, to, subject, content);
+
+ // Prepare message using a Spring helper
+ MimeMessage mimeMessage = javaMailSender.createMimeMessage();
+ try {
+ MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, CharEncoding.UTF_8);
+ message.setTo(to);
+ message.setFrom(jHipsterProperties.getMail().getFrom());
+ message.setSubject(subject);
+ message.setText(content, isHtml);
+ javaMailSender.send(mimeMessage);
+ log.debug("Sent e-mail to User '{}'", to);
+ } catch (Exception e) {
+ log.warn("E-mail could not be sent to user '{}'", to, e);
+ }
+ }
+
+ @Async
+ public void sendActivationEmail(User user) {
+ log.debug("Sending activation e-mail to '{}'", user.getEmail());
+ Locale locale = Locale.forLanguageTag(user.getLangKey());
+ Context context = new Context(locale);
+ context.setVariable(USER, user);
+ context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
+ String content = templateEngine.process("activationEmail", context);
+ String subject = messageSource.getMessage("email.activation.title", null, locale);
+ sendEmail(user.getEmail(), subject, content, false, true);
+ }
+
+ @Async
+ public void sendCreationEmail(User user) {
+ log.debug("Sending creation e-mail to '{}'", user.getEmail());
+ Locale locale = Locale.forLanguageTag(user.getLangKey());
+ Context context = new Context(locale);
+ context.setVariable(USER, user);
+ context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
+ String content = templateEngine.process("creationEmail", context);
+ String subject = messageSource.getMessage("email.activation.title", null, locale);
+ sendEmail(user.getEmail(), subject, content, false, true);
+ }
+
+ @Async
+ public void sendPasswordResetMail(User user) {
+ log.debug("Sending password reset e-mail to '{}'", user.getEmail());
+ Locale locale = Locale.forLanguageTag(user.getLangKey());
+ Context context = new Context(locale);
+ context.setVariable(USER, user);
+ context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
+ String content = templateEngine.process("passwordResetEmail", context);
+ String subject = messageSource.getMessage("email.reset.title", null, locale);
+ sendEmail(user.getEmail(), subject, content, false, true);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/UserService.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/UserService.java
new file mode 100644
index 0000000000..a95136bca4
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/UserService.java
@@ -0,0 +1,228 @@
+package com.gateway.service;
+
+import com.gateway.domain.Authority;
+import com.gateway.domain.User;
+import com.gateway.repository.AuthorityRepository;
+import com.gateway.config.Constants;
+import com.gateway.repository.UserRepository;
+import com.gateway.security.AuthoritiesConstants;
+import com.gateway.security.SecurityUtils;
+import com.gateway.service.util.RandomUtil;
+import com.gateway.service.dto.UserDTO;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.ZonedDateTime;
+import java.util.*;
+
+/**
+ * Service class for managing users.
+ */
+@Service
+@Transactional
+public class UserService {
+
+ private final Logger log = LoggerFactory.getLogger(UserService.class);
+
+ private final UserRepository userRepository;
+
+ private final PasswordEncoder passwordEncoder;
+
+ private final AuthorityRepository authorityRepository;
+
+ public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository) {
+ this.userRepository = userRepository;
+ this.passwordEncoder = passwordEncoder;
+ this.authorityRepository = authorityRepository;
+ }
+
+ public Optional activateRegistration(String key) {
+ log.debug("Activating user for activation key {}", key);
+ return userRepository.findOneByActivationKey(key)
+ .map(user -> {
+ // activate given user for the registration key.
+ user.setActivated(true);
+ user.setActivationKey(null);
+ log.debug("Activated user: {}", user);
+ return user;
+ });
+ }
+
+ public Optional completePasswordReset(String newPassword, String key) {
+ log.debug("Reset user password for reset key {}", key);
+
+ return userRepository.findOneByResetKey(key)
+ .filter(user -> {
+ ZonedDateTime oneDayAgo = ZonedDateTime.now().minusHours(24);
+ return user.getResetDate().isAfter(oneDayAgo);
+ })
+ .map(user -> {
+ user.setPassword(passwordEncoder.encode(newPassword));
+ user.setResetKey(null);
+ user.setResetDate(null);
+ return user;
+ });
+ }
+
+ public Optional requestPasswordReset(String mail) {
+ return userRepository.findOneByEmail(mail)
+ .filter(User::getActivated)
+ .map(user -> {
+ user.setResetKey(RandomUtil.generateResetKey());
+ user.setResetDate(ZonedDateTime.now());
+ return user;
+ });
+ }
+
+ public User createUser(String login, String password, String firstName, String lastName, String email,
+ String imageUrl, String langKey) {
+
+ User newUser = new User();
+ Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER);
+ Set authorities = new HashSet<>();
+ String encryptedPassword = passwordEncoder.encode(password);
+ newUser.setLogin(login);
+ // new user gets initially a generated password
+ newUser.setPassword(encryptedPassword);
+ newUser.setFirstName(firstName);
+ newUser.setLastName(lastName);
+ newUser.setEmail(email);
+ newUser.setImageUrl(imageUrl);
+ newUser.setLangKey(langKey);
+ // new user is not active
+ newUser.setActivated(false);
+ // new user gets registration key
+ newUser.setActivationKey(RandomUtil.generateActivationKey());
+ authorities.add(authority);
+ newUser.setAuthorities(authorities);
+ userRepository.save(newUser);
+ log.debug("Created Information for User: {}", newUser);
+ return newUser;
+ }
+
+ public User createUser(UserDTO userDTO) {
+ User user = new User();
+ user.setLogin(userDTO.getLogin());
+ user.setFirstName(userDTO.getFirstName());
+ user.setLastName(userDTO.getLastName());
+ user.setEmail(userDTO.getEmail());
+ user.setImageUrl(userDTO.getImageUrl());
+ if (userDTO.getLangKey() == null) {
+ user.setLangKey("en"); // default language
+ } else {
+ user.setLangKey(userDTO.getLangKey());
+ }
+ if (userDTO.getAuthorities() != null) {
+ Set authorities = new HashSet<>();
+ userDTO.getAuthorities().forEach(
+ authority -> authorities.add(authorityRepository.findOne(authority))
+ );
+ user.setAuthorities(authorities);
+ }
+ String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
+ user.setPassword(encryptedPassword);
+ user.setResetKey(RandomUtil.generateResetKey());
+ user.setResetDate(ZonedDateTime.now());
+ user.setActivated(true);
+ userRepository.save(user);
+ log.debug("Created Information for User: {}", user);
+ return user;
+ }
+
+ /**
+ * Update basic information (first name, last name, email, language) for the current user.
+ */
+ public void updateUser(String firstName, String lastName, String email, String langKey) {
+ userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> {
+ user.setFirstName(firstName);
+ user.setLastName(lastName);
+ user.setEmail(email);
+ user.setLangKey(langKey);
+ log.debug("Changed Information for User: {}", user);
+ });
+ }
+
+ /**
+ * Update all information for a specific user, and return the modified user.
+ */
+ public Optional updateUser(UserDTO userDTO) {
+ return Optional.of(userRepository
+ .findOne(userDTO.getId()))
+ .map(user -> {
+ user.setLogin(userDTO.getLogin());
+ user.setFirstName(userDTO.getFirstName());
+ user.setLastName(userDTO.getLastName());
+ user.setEmail(userDTO.getEmail());
+ user.setImageUrl(userDTO.getImageUrl());
+ user.setActivated(userDTO.isActivated());
+ user.setLangKey(userDTO.getLangKey());
+ Set managedAuthorities = user.getAuthorities();
+ managedAuthorities.clear();
+ userDTO.getAuthorities().stream()
+ .map(authorityRepository::findOne)
+ .forEach(managedAuthorities::add);
+ log.debug("Changed Information for User: {}", user);
+ return user;
+ })
+ .map(UserDTO::new);
+ }
+
+ public void deleteUser(String login) {
+ userRepository.findOneByLogin(login).ifPresent(user -> {
+ userRepository.delete(user);
+ log.debug("Deleted User: {}", user);
+ });
+ }
+
+ public void changePassword(String password) {
+ userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> {
+ String encryptedPassword = passwordEncoder.encode(password);
+ user.setPassword(encryptedPassword);
+ log.debug("Changed password for User: {}", user);
+ });
+ }
+
+ @Transactional(readOnly = true)
+ public Page getAllManagedUsers(Pageable pageable) {
+ return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(UserDTO::new);
+ }
+
+ @Transactional(readOnly = true)
+ public Optional getUserWithAuthoritiesByLogin(String login) {
+ return userRepository.findOneWithAuthoritiesByLogin(login);
+ }
+
+ @Transactional(readOnly = true)
+ public User getUserWithAuthorities(Long id) {
+ return userRepository.findOneWithAuthoritiesById(id);
+ }
+
+ @Transactional(readOnly = true)
+ public User getUserWithAuthorities() {
+ return userRepository.findOneWithAuthoritiesByLogin(SecurityUtils.getCurrentUserLogin()).orElse(null);
+ }
+
+
+ /**
+ * Not activated users should be automatically deleted after 3 days.
+ *
+ * This is scheduled to get fired everyday, at 01:00 (am).
+ *
+ */
+ @Scheduled(cron = "0 0 1 * * ?")
+ public void removeNotActivatedUsers() {
+ ZonedDateTime now = ZonedDateTime.now();
+ List users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minusDays(3));
+ for (User user : users) {
+ log.debug("Deleting not activated user {}", user.getLogin());
+ userRepository.delete(user);
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/dto/UserDTO.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/dto/UserDTO.java
new file mode 100644
index 0000000000..7acf62b299
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/dto/UserDTO.java
@@ -0,0 +1,167 @@
+package com.gateway.service.dto;
+
+import com.gateway.config.Constants;
+
+import com.gateway.domain.Authority;
+import com.gateway.domain.User;
+
+import org.hibernate.validator.constraints.Email;
+
+import javax.validation.constraints.*;
+import java.time.ZonedDateTime;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * A DTO representing a user, with his authorities.
+ */
+public class UserDTO {
+
+ private Long id;
+
+ @Pattern(regexp = Constants.LOGIN_REGEX)
+ @Size(min = 1, max = 50)
+ private String login;
+
+ @Size(max = 50)
+ private String firstName;
+
+ @Size(max = 50)
+ private String lastName;
+
+ @Email
+ @Size(min = 5, max = 100)
+ private String email;
+
+ @Size(max = 256)
+ private String imageUrl;
+
+ private boolean activated = false;
+
+ @Size(min = 2, max = 5)
+ private String langKey;
+
+ private String createdBy;
+
+ private ZonedDateTime createdDate;
+
+ private String lastModifiedBy;
+
+ private ZonedDateTime lastModifiedDate;
+
+ private Set authorities;
+
+ public UserDTO() {
+ // Empty constructor needed for MapStruct.
+ }
+
+ public UserDTO(User user) {
+ this(user.getId(), user.getLogin(), user.getFirstName(), user.getLastName(),
+ user.getEmail(), user.getActivated(), user.getImageUrl(), user.getLangKey(),
+ user.getCreatedBy(), user.getCreatedDate(), user.getLastModifiedBy(), user.getLastModifiedDate(),
+ user.getAuthorities().stream().map(Authority::getName)
+ .collect(Collectors.toSet()));
+ }
+
+ public UserDTO(Long id, String login, String firstName, String lastName,
+ String email, boolean activated, String imageUrl, String langKey,
+ String createdBy, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate,
+ Set authorities) {
+
+ this.id = id;
+ this.login = login;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.email = email;
+ this.activated = activated;
+ this.imageUrl = imageUrl;
+ this.langKey = langKey;
+ this.createdBy = createdBy;
+ this.createdDate = createdDate;
+ this.lastModifiedBy = lastModifiedBy;
+ this.lastModifiedDate = lastModifiedDate;
+ this.authorities = authorities;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public boolean isActivated() {
+ return activated;
+ }
+
+ public String getLangKey() {
+ return langKey;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public ZonedDateTime getCreatedDate() {
+ return createdDate;
+ }
+
+ public String getLastModifiedBy() {
+ return lastModifiedBy;
+ }
+
+ public ZonedDateTime getLastModifiedDate() {
+ return lastModifiedDate;
+ }
+
+ public void setLastModifiedDate(ZonedDateTime lastModifiedDate) {
+ this.lastModifiedDate = lastModifiedDate;
+ }
+
+ public Set getAuthorities() {
+ return authorities;
+ }
+
+ @Override
+ public String toString() {
+ return "UserDTO{" +
+ "login='" + login + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", email='" + email + '\'' +
+ ", imageUrl='" + imageUrl + '\'' +
+ ", activated=" + activated +
+ ", langKey='" + langKey + '\'' +
+ ", createdBy=" + createdBy +
+ ", createdDate=" + createdDate +
+ ", lastModifiedBy='" + lastModifiedBy + '\'' +
+ ", lastModifiedDate=" + lastModifiedDate +
+ ", authorities=" + authorities +
+ "}";
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/dto/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/dto/package-info.java
new file mode 100644
index 0000000000..e8c13430ca
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/dto/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Data Transfer Objects.
+ */
+package com.gateway.service.dto;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/mapper/UserMapper.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/mapper/UserMapper.java
new file mode 100644
index 0000000000..d2bf0170c4
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/mapper/UserMapper.java
@@ -0,0 +1,55 @@
+package com.gateway.service.mapper;
+
+import com.gateway.domain.Authority;
+import com.gateway.domain.User;
+import com.gateway.service.dto.UserDTO;
+import org.mapstruct.*;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Mapper for the entity User and its DTO UserDTO.
+ */
+@Mapper(componentModel = "spring", uses = {})
+public interface UserMapper {
+
+ UserDTO userToUserDTO(User user);
+
+ List usersToUserDTOs(List users);
+
+ @Mapping(target = "createdBy", ignore = true)
+ @Mapping(target = "createdDate", ignore = true)
+ @Mapping(target = "lastModifiedBy", ignore = true)
+ @Mapping(target = "lastModifiedDate", ignore = true)
+ @Mapping(target = "activationKey", ignore = true)
+ @Mapping(target = "resetKey", ignore = true)
+ @Mapping(target = "resetDate", ignore = true)
+ @Mapping(target = "password", ignore = true)
+ User userDTOToUser(UserDTO userDTO);
+
+ List userDTOsToUsers(List userDTOs);
+
+ default User userFromId(Long id) {
+ if (id == null) {
+ return null;
+ }
+ User user = new User();
+ user.setId(id);
+ return user;
+ }
+
+ default Set stringsFromAuthorities (Set authorities) {
+ return authorities.stream().map(Authority::getName)
+ .collect(Collectors.toSet());
+ }
+
+ default Set authoritiesFromStrings(Set strings) {
+ return strings.stream().map(string -> {
+ Authority auth = new Authority();
+ auth.setName(string);
+ return auth;
+ }).collect(Collectors.toSet());
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/mapper/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/mapper/package-info.java
new file mode 100644
index 0000000000..7f1db9976c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/mapper/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * MapStruct mappers for mapping domain objects and Data Transfer Objects.
+ */
+package com.gateway.service.mapper;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/package-info.java
new file mode 100644
index 0000000000..05b7074c1b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Service layer beans.
+ */
+package com.gateway.service;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/util/RandomUtil.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/util/RandomUtil.java
new file mode 100644
index 0000000000..ec2db0f6b0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/service/util/RandomUtil.java
@@ -0,0 +1,41 @@
+package com.gateway.service.util;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+/**
+ * Utility class for generating random Strings.
+ */
+public final class RandomUtil {
+
+ private static final int DEF_COUNT = 20;
+
+ private RandomUtil() {
+ }
+
+ /**
+ * Generate a password.
+ *
+ * @return the generated password
+ */
+ public static String generatePassword() {
+ return RandomStringUtils.randomAlphanumeric(DEF_COUNT);
+ }
+
+ /**
+ * Generate an activation key.
+ *
+ * @return the generated activation key
+ */
+ public static String generateActivationKey() {
+ return RandomStringUtils.randomNumeric(DEF_COUNT);
+ }
+
+ /**
+ * Generate a reset key.
+ *
+ * @return the generated reset key
+ */
+ public static String generateResetKey() {
+ return RandomStringUtils.randomNumeric(DEF_COUNT);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/AccountResource.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/AccountResource.java
new file mode 100644
index 0000000000..1e71217a5d
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/AccountResource.java
@@ -0,0 +1,202 @@
+package com.gateway.web.rest;
+
+import com.codahale.metrics.annotation.Timed;
+
+import com.gateway.domain.User;
+import com.gateway.repository.UserRepository;
+import com.gateway.security.SecurityUtils;
+import com.gateway.service.MailService;
+import com.gateway.service.UserService;
+import com.gateway.service.dto.UserDTO;
+import com.gateway.web.rest.vm.KeyAndPasswordVM;
+import com.gateway.web.rest.vm.ManagedUserVM;
+import com.gateway.web.rest.util.HeaderUtil;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import java.util.*;
+
+/**
+ * REST controller for managing the current user's account.
+ */
+@RestController
+@RequestMapping("/api")
+public class AccountResource {
+
+ private final Logger log = LoggerFactory.getLogger(AccountResource.class);
+
+ private final UserRepository userRepository;
+
+ private final UserService userService;
+
+ private final MailService mailService;
+
+ public AccountResource(UserRepository userRepository, UserService userService,
+ MailService mailService) {
+
+ this.userRepository = userRepository;
+ this.userService = userService;
+ this.mailService = mailService;
+ }
+
+ /**
+ * POST /register : register the user.
+ *
+ * @param managedUserVM the managed user View Model
+ * @return the ResponseEntity with status 201 (Created) if the user is registered or 400 (Bad Request) if the login or e-mail is already in use
+ */
+ @PostMapping(path = "/register",
+ produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
+ @Timed
+ public ResponseEntity registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
+
+ HttpHeaders textPlainHeaders = new HttpHeaders();
+ textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
+
+ return userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase())
+ .map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
+ .orElseGet(() -> userRepository.findOneByEmail(managedUserVM.getEmail())
+ .map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
+ .orElseGet(() -> {
+ User user = userService
+ .createUser(managedUserVM.getLogin(), managedUserVM.getPassword(),
+ managedUserVM.getFirstName(), managedUserVM.getLastName(),
+ managedUserVM.getEmail().toLowerCase(), managedUserVM.getImageUrl(), managedUserVM.getLangKey());
+
+ mailService.sendActivationEmail(user);
+ return new ResponseEntity<>(HttpStatus.CREATED);
+ })
+ );
+ }
+
+ /**
+ * GET /activate : activate the registered user.
+ *
+ * @param key the activation key
+ * @return the ResponseEntity with status 200 (OK) and the activated user in body, or status 500 (Internal Server Error) if the user couldn't be activated
+ */
+ @GetMapping("/activate")
+ @Timed
+ public ResponseEntity activateAccount(@RequestParam(value = "key") String key) {
+ return userService.activateRegistration(key)
+ .map(user -> new ResponseEntity(HttpStatus.OK))
+ .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+
+ /**
+ * GET /authenticate : check if the user is authenticated, and return its login.
+ *
+ * @param request the HTTP request
+ * @return the login if the user is authenticated
+ */
+ @GetMapping("/authenticate")
+ @Timed
+ public String isAuthenticated(HttpServletRequest request) {
+ log.debug("REST request to check if the current user is authenticated");
+ return request.getRemoteUser();
+ }
+
+ /**
+ * GET /account : get the current user.
+ *
+ * @return the ResponseEntity with status 200 (OK) and the current user in body, or status 500 (Internal Server Error) if the user couldn't be returned
+ */
+ @GetMapping("/account")
+ @Timed
+ public ResponseEntity getAccount() {
+ return Optional.ofNullable(userService.getUserWithAuthorities())
+ .map(user -> new ResponseEntity<>(new UserDTO(user), HttpStatus.OK))
+ .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+
+ /**
+ * POST /account : update the current user information.
+ *
+ * @param userDTO the current user information
+ * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) or 500 (Internal Server Error) if the user couldn't be updated
+ */
+ @PostMapping("/account")
+ @Timed
+ public ResponseEntity saveAccount(@Valid @RequestBody UserDTO userDTO) {
+ Optional existingUser = userRepository.findOneByEmail(userDTO.getEmail());
+ if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userDTO.getLogin()))) {
+ return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null);
+ }
+ return userRepository
+ .findOneByLogin(SecurityUtils.getCurrentUserLogin())
+ .map(u -> {
+ userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
+ userDTO.getLangKey());
+ return new ResponseEntity(HttpStatus.OK);
+ })
+ .orElseGet(() -> new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+
+ /**
+ * POST /account/change_password : changes the current user's password
+ *
+ * @param password the new password
+ * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) if the new password is not strong enough
+ */
+ @PostMapping(path = "/account/change_password",
+ produces = MediaType.TEXT_PLAIN_VALUE)
+ @Timed
+ public ResponseEntity changePassword(@RequestBody String password) {
+ if (!checkPasswordLength(password)) {
+ return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
+ }
+ userService.changePassword(password);
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ /**
+ * POST /account/reset_password/init : Send an e-mail to reset the password of the user
+ *
+ * @param mail the mail of the user
+ * @return the ResponseEntity with status 200 (OK) if the e-mail was sent, or status 400 (Bad Request) if the e-mail address is not registered
+ */
+ @PostMapping(path = "/account/reset_password/init",
+ produces = MediaType.TEXT_PLAIN_VALUE)
+ @Timed
+ public ResponseEntity requestPasswordReset(@RequestBody String mail) {
+ return userService.requestPasswordReset(mail)
+ .map(user -> {
+ mailService.sendPasswordResetMail(user);
+ return new ResponseEntity<>("e-mail was sent", HttpStatus.OK);
+ }).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST));
+ }
+
+ /**
+ * POST /account/reset_password/finish : Finish to reset the password of the user
+ *
+ * @param keyAndPassword the generated key and the new password
+ * @return the ResponseEntity with status 200 (OK) if the password has been reset,
+ * or status 400 (Bad Request) or 500 (Internal Server Error) if the password could not be reset
+ */
+ @PostMapping(path = "/account/reset_password/finish",
+ produces = MediaType.TEXT_PLAIN_VALUE)
+ @Timed
+ public ResponseEntity finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) {
+ if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
+ return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
+ }
+ return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey())
+ .map(user -> new ResponseEntity(HttpStatus.OK))
+ .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
+
+ private boolean checkPasswordLength(String password) {
+ return !StringUtils.isEmpty(password) &&
+ password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH &&
+ password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/AuditResource.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/AuditResource.java
new file mode 100644
index 0000000000..f61d1ebcb9
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/AuditResource.java
@@ -0,0 +1,76 @@
+package com.gateway.web.rest;
+
+import com.gateway.service.AuditEventService;
+import com.gateway.web.rest.util.PaginationUtil;
+
+import io.github.jhipster.web.util.ResponseUtil;
+import io.swagger.annotations.ApiParam;
+import org.springframework.boot.actuate.audit.AuditEvent;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.net.URISyntaxException;
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * REST controller for getting the audit events.
+ */
+@RestController
+@RequestMapping("/management/audits")
+public class AuditResource {
+
+ private final AuditEventService auditEventService;
+
+ public AuditResource(AuditEventService auditEventService) {
+ this.auditEventService = auditEventService;
+ }
+
+ /**
+ * GET /audits : get a page of AuditEvents.
+ *
+ * @param pageable the pagination information
+ * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
+ */
+ @GetMapping
+ public ResponseEntity> getAll(@ApiParam Pageable pageable) {
+ Page page = auditEventService.findAll(pageable);
+ HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits");
+ return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
+ }
+
+ /**
+ * GET /audits : get a page of AuditEvents between the fromDate and toDate.
+ *
+ * @param fromDate the start of the time period of AuditEvents to get
+ * @param toDate the end of the time period of AuditEvents to get
+ * @param pageable the pagination information
+ * @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
+ */
+
+ @GetMapping(params = {"fromDate", "toDate"})
+ public ResponseEntity> getByDates(
+ @RequestParam(value = "fromDate") LocalDate fromDate,
+ @RequestParam(value = "toDate") LocalDate toDate,
+ @ApiParam Pageable pageable) {
+
+ Page page = auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59), pageable);
+ HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits");
+ return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
+ }
+
+ /**
+ * GET /audits/:id : get an AuditEvent by id.
+ *
+ * @param id the id of the entity to get
+ * @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found)
+ */
+ @GetMapping("/{id:.+}")
+ public ResponseEntity get(@PathVariable Long id) {
+ return ResponseUtil.wrapOrNotFound(auditEventService.find(id));
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/GatewayResource.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/GatewayResource.java
new file mode 100644
index 0000000000..74bf2fd151
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/GatewayResource.java
@@ -0,0 +1,55 @@
+package com.gateway.web.rest;
+
+import com.gateway.web.rest.vm.RouteVM;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.netflix.zuul.filters.Route;
+import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
+import org.springframework.http.*;
+import org.springframework.web.bind.annotation.*;
+
+import com.codahale.metrics.annotation.Timed;
+
+/**
+ * REST controller for managing Gateway configuration.
+ */
+@RestController
+@RequestMapping("/api/gateway")
+public class GatewayResource {
+
+ private final Logger log = LoggerFactory.getLogger(GatewayResource.class);
+
+ private final RouteLocator routeLocator;
+
+ private final DiscoveryClient discoveryClient;
+
+ public GatewayResource(RouteLocator routeLocator, DiscoveryClient discoveryClient) {
+ this.routeLocator = routeLocator;
+ this.discoveryClient = discoveryClient;
+ }
+
+ /**
+ * GET /routes : get the active routes.
+ *
+ * @return the ResponseEntity with status 200 (OK) and with body the list of routes
+ */
+ @GetMapping("/routes")
+ @Timed
+ public ResponseEntity> activeRoutes() {
+ List routes = routeLocator.getRoutes();
+ List routeVMs = new ArrayList<>();
+ routes.forEach(route -> {
+ RouteVM routeVM = new RouteVM();
+ routeVM.setPath(route.getFullPath());
+ routeVM.setServiceId(route.getId());
+ routeVM.setServiceInstances(discoveryClient.getInstances(route.getId()));
+ routeVMs.add(routeVM);
+ });
+ return new ResponseEntity<>(routeVMs, HttpStatus.OK);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/JWTToken.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/JWTToken.java
new file mode 100644
index 0000000000..7305f901dd
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/JWTToken.java
@@ -0,0 +1,24 @@
+package com.gateway.web.rest;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Object to return as body in JWT Authentication.
+ */
+public class JWTToken {
+
+ private String idToken;
+
+ public JWTToken(String idToken) {
+ this.idToken = idToken;
+ }
+
+ @JsonProperty("id_token")
+ public String getIdToken() {
+ return idToken;
+ }
+
+ public void setIdToken(String idToken) {
+ this.idToken = idToken;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/LogsResource.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/LogsResource.java
new file mode 100644
index 0000000000..7197afbec1
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/LogsResource.java
@@ -0,0 +1,39 @@
+package com.gateway.web.rest;
+
+import com.gateway.web.rest.vm.LoggerVM;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import com.codahale.metrics.annotation.Timed;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Controller for view and managing Log Level at runtime.
+ */
+@RestController
+@RequestMapping("/management")
+public class LogsResource {
+
+ @GetMapping("/logs")
+ @Timed
+ public List getList() {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ return context.getLoggerList()
+ .stream()
+ .map(LoggerVM::new)
+ .collect(Collectors.toList());
+ }
+
+ @PutMapping("/logs")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ @Timed
+ public void changeLevel(@RequestBody LoggerVM jsonLogger) {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel()));
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/ProfileInfoResource.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/ProfileInfoResource.java
new file mode 100644
index 0000000000..f077bd07f7
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/ProfileInfoResource.java
@@ -0,0 +1,69 @@
+package com.gateway.web.rest;
+
+import com.gateway.config.DefaultProfileUtil;
+
+import io.github.jhipster.config.JHipsterProperties;
+
+import org.springframework.core.env.Environment;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Resource to return information about the currently running Spring profiles.
+ */
+@RestController
+@RequestMapping("/api")
+public class ProfileInfoResource {
+
+ private final Environment env;
+
+ private final JHipsterProperties jHipsterProperties;
+
+ public ProfileInfoResource(Environment env, JHipsterProperties jHipsterProperties) {
+ this.env = env;
+ this.jHipsterProperties = jHipsterProperties;
+ }
+
+ @GetMapping("/profile-info")
+ public ProfileInfoVM getActiveProfiles() {
+ String[] activeProfiles = DefaultProfileUtil.getActiveProfiles(env);
+ return new ProfileInfoVM(activeProfiles, getRibbonEnv(activeProfiles));
+ }
+
+ private String getRibbonEnv(String[] activeProfiles) {
+ String[] displayOnActiveProfiles = jHipsterProperties.getRibbon().getDisplayOnActiveProfiles();
+ if (displayOnActiveProfiles == null) {
+ return null;
+ }
+ List ribbonProfiles = new ArrayList<>(Arrays.asList(displayOnActiveProfiles));
+ List springBootProfiles = Arrays.asList(activeProfiles);
+ ribbonProfiles.retainAll(springBootProfiles);
+ if (!ribbonProfiles.isEmpty()) {
+ return ribbonProfiles.get(0);
+ }
+ return null;
+ }
+
+ class ProfileInfoVM {
+
+ private String[] activeProfiles;
+
+ private String ribbonEnv;
+
+ ProfileInfoVM(String[] activeProfiles, String ribbonEnv) {
+ this.activeProfiles = activeProfiles;
+ this.ribbonEnv = ribbonEnv;
+ }
+
+ public String[] getActiveProfiles() {
+ return activeProfiles;
+ }
+
+ public String getRibbonEnv() {
+ return ribbonEnv;
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/UserJWTController.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/UserJWTController.java
new file mode 100644
index 0000000000..908e4f096b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/UserJWTController.java
@@ -0,0 +1,60 @@
+package com.gateway.web.rest;
+
+import com.gateway.security.jwt.JWTConfigurer;
+import com.gateway.security.jwt.TokenProvider;
+import com.gateway.web.rest.vm.LoginVM;
+
+import java.util.Collections;
+
+import com.codahale.metrics.annotation.Timed;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+
+@RestController
+@RequestMapping("/api")
+public class UserJWTController {
+
+ private final Logger log = LoggerFactory.getLogger(UserJWTController.class);
+
+ private final TokenProvider tokenProvider;
+
+ private final AuthenticationManager authenticationManager;
+
+ public UserJWTController(TokenProvider tokenProvider, AuthenticationManager authenticationManager) {
+ this.tokenProvider = tokenProvider;
+ this.authenticationManager = authenticationManager;
+ }
+
+ @PostMapping("/authenticate")
+ @Timed
+ public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletResponse response) {
+
+ UsernamePasswordAuthenticationToken authenticationToken =
+ new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
+
+ try {
+ Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
+ String jwt = tokenProvider.createToken(authentication, rememberMe);
+ response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
+ return ResponseEntity.ok(new JWTToken(jwt));
+ } catch (AuthenticationException ae) {
+ log.trace("Authentication exception trace: {}", ae);
+ return new ResponseEntity<>(Collections.singletonMap("AuthenticationException",
+ ae.getLocalizedMessage()), HttpStatus.UNAUTHORIZED);
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/UserResource.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/UserResource.java
new file mode 100644
index 0000000000..80012aacd5
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/UserResource.java
@@ -0,0 +1,187 @@
+package com.gateway.web.rest;
+
+import com.gateway.config.Constants;
+import com.codahale.metrics.annotation.Timed;
+import com.gateway.domain.User;
+import com.gateway.repository.UserRepository;
+import com.gateway.security.AuthoritiesConstants;
+import com.gateway.service.MailService;
+import com.gateway.service.UserService;
+import com.gateway.service.dto.UserDTO;
+import com.gateway.web.rest.vm.ManagedUserVM;
+import com.gateway.web.rest.util.HeaderUtil;
+import com.gateway.web.rest.util.PaginationUtil;
+import io.github.jhipster.web.util.ResponseUtil;
+import io.swagger.annotations.ApiParam;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.web.bind.annotation.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * REST controller for managing users.
+ *
+ *
This class accesses the User entity, and needs to fetch its collection of authorities.
+ *
+ * For a normal use-case, it would be better to have an eager relationship between User and Authority,
+ * and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join
+ * which would be good for performance.
+ *
+ *
+ * We use a View Model and a DTO for 3 reasons:
+ *
+ *
We want to keep a lazy association between the user and the authorities, because people will
+ * quite often do relationships with the user, and we don't want them to get the authorities all
+ * the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users'
+ * application because of this use-case.
+ *
Not having an outer join causes n+1 requests to the database. This is not a real issue as
+ * we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests,
+ * but then all authorities come from the cache, so in fact it's much better than doing an outer join
+ * (which will get lots of data from the database, for each HTTP call).
+ *
As this manages users, for security reasons, we'd rather have a DTO layer.
+ *
+ *
Another option would be to have a specific JPA entity graph to handle this case.
+ */
+@RestController
+@RequestMapping("/api")
+public class UserResource {
+
+ private final Logger log = LoggerFactory.getLogger(UserResource.class);
+
+ private static final String ENTITY_NAME = "userManagement";
+
+ private final UserRepository userRepository;
+
+ private final MailService mailService;
+
+ private final UserService userService;
+
+ public UserResource(UserRepository userRepository, MailService mailService,
+ UserService userService) {
+
+ this.userRepository = userRepository;
+ this.mailService = mailService;
+ this.userService = userService;
+ }
+
+ /**
+ * POST /users : Creates a new user.
+ *
+ * Creates a new user if the login and email are not already used, and sends an
+ * mail with an activation link.
+ * The user needs to be activated on creation.
+ *
+ *
+ * @param managedUserVM the user to create
+ * @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use
+ * @throws URISyntaxException if the Location URI syntax is incorrect
+ */
+ @PostMapping("/users")
+ @Timed
+ @Secured(AuthoritiesConstants.ADMIN)
+ public ResponseEntity createUser(@RequestBody ManagedUserVM managedUserVM) throws URISyntaxException {
+ log.debug("REST request to save User : {}", managedUserVM);
+
+ if (managedUserVM.getId() != null) {
+ return ResponseEntity.badRequest()
+ .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new user cannot already have an ID"))
+ .body(null);
+ // Lowercase the user login before comparing with database
+ } else if (userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()).isPresent()) {
+ return ResponseEntity.badRequest()
+ .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use"))
+ .body(null);
+ } else if (userRepository.findOneByEmail(managedUserVM.getEmail()).isPresent()) {
+ return ResponseEntity.badRequest()
+ .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "Email already in use"))
+ .body(null);
+ } else {
+ User newUser = userService.createUser(managedUserVM);
+ mailService.sendCreationEmail(newUser);
+ return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin()))
+ .headers(HeaderUtil.createAlert( "A user is created with identifier " + newUser.getLogin(), newUser.getLogin()))
+ .body(newUser);
+ }
+ }
+
+ /**
+ * PUT /users : Updates an existing User.
+ *
+ * @param managedUserVM the user to update
+ * @return the ResponseEntity with status 200 (OK) and with body the updated user,
+ * or with status 400 (Bad Request) if the login or email is already in use,
+ * or with status 500 (Internal Server Error) if the user couldn't be updated
+ */
+ @PutMapping("/users")
+ @Timed
+ @Secured(AuthoritiesConstants.ADMIN)
+ public ResponseEntity updateUser(@RequestBody ManagedUserVM managedUserVM) {
+ log.debug("REST request to update User : {}", managedUserVM);
+ Optional existingUser = userRepository.findOneByEmail(managedUserVM.getEmail());
+ if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) {
+ return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "E-mail already in use")).body(null);
+ }
+ existingUser = userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase());
+ if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) {
+ return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use")).body(null);
+ }
+ Optional updatedUser = userService.updateUser(managedUserVM);
+
+ return ResponseUtil.wrapOrNotFound(updatedUser,
+ HeaderUtil.createAlert("A user is updated with identifier " + managedUserVM.getLogin(), managedUserVM.getLogin()));
+ }
+
+ /**
+ * GET /users : get all users.
+ *
+ * @param pageable the pagination information
+ * @return the ResponseEntity with status 200 (OK) and with body all users
+ */
+ @GetMapping("/users")
+ @Timed
+ public ResponseEntity> getAllUsers(@ApiParam Pageable pageable) {
+ final Page page = userService.getAllManagedUsers(pageable);
+ HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users");
+ return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
+ }
+
+ /**
+ * GET /users/:login : get the "login" user.
+ *
+ * @param login the login of the user to find
+ * @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found)
+ */
+ @GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
+ @Timed
+ public ResponseEntity getUser(@PathVariable String login) {
+ log.debug("REST request to get User : {}", login);
+ return ResponseUtil.wrapOrNotFound(
+ userService.getUserWithAuthoritiesByLogin(login)
+ .map(UserDTO::new));
+ }
+
+ /**
+ * DELETE /users/:login : delete the "login" User.
+ *
+ * @param login the login of the user to delete
+ * @return the ResponseEntity with status 200 (OK)
+ */
+ @DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
+ @Timed
+ @Secured(AuthoritiesConstants.ADMIN)
+ public ResponseEntity deleteUser(@PathVariable String login) {
+ log.debug("REST request to delete User: {}", login);
+ userService.deleteUser(login);
+ return ResponseEntity.ok().headers(HeaderUtil.createAlert( "A user is deleted with identifier " + login, login)).build();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/CustomParameterizedException.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/CustomParameterizedException.java
new file mode 100644
index 0000000000..30a341880a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/CustomParameterizedException.java
@@ -0,0 +1,34 @@
+package com.gateway.web.rest.errors;
+
+/**
+ * Custom, parameterized exception, which can be translated on the client side.
+ * For example:
+ *
+ *
+ * throw new CustomParameterizedException("myCustomError", "hello", "world");
+ *
+ *
+ * Can be translated with:
+ *
+ *
+ * "error.myCustomError" : "The server says {{params[0]}} to {{params[1]}}"
+ *
+ */
+public class CustomParameterizedException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+ private final String[] params;
+
+ public CustomParameterizedException(String message, String... params) {
+ super(message);
+ this.message = message;
+ this.params = params;
+ }
+
+ public ParameterizedErrorVM getErrorVM() {
+ return new ParameterizedErrorVM(message, params);
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ErrorConstants.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ErrorConstants.java
new file mode 100644
index 0000000000..2b2b6216e0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ErrorConstants.java
@@ -0,0 +1,14 @@
+package com.gateway.web.rest.errors;
+
+public final class ErrorConstants {
+
+ public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure";
+ public static final String ERR_ACCESS_DENIED = "error.accessDenied";
+ public static final String ERR_VALIDATION = "error.validation";
+ public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported";
+ public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError";
+
+ private ErrorConstants() {
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ErrorVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ErrorVM.java
new file mode 100644
index 0000000000..a7d93c535a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ErrorVM.java
@@ -0,0 +1,52 @@
+package com.gateway.web.rest.errors;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * View Model for transferring error message with a list of field errors.
+ */
+public class ErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+ private final String description;
+
+ private List fieldErrors;
+
+ public ErrorVM(String message) {
+ this(message, null);
+ }
+
+ public ErrorVM(String message, String description) {
+ this.message = message;
+ this.description = description;
+ }
+
+ public ErrorVM(String message, String description, List fieldErrors) {
+ this.message = message;
+ this.description = description;
+ this.fieldErrors = fieldErrors;
+ }
+
+ public void add(String objectName, String field, String message) {
+ if (fieldErrors == null) {
+ fieldErrors = new ArrayList<>();
+ }
+ fieldErrors.add(new FieldErrorVM(objectName, field, message));
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public List getFieldErrors() {
+ return fieldErrors;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ExceptionTranslator.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ExceptionTranslator.java
new file mode 100644
index 0000000000..adff52d310
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ExceptionTranslator.java
@@ -0,0 +1,85 @@
+package com.gateway.web.rest.errors;
+
+import java.util.List;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.dao.ConcurrencyFailureException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.ResponseEntity.BodyBuilder;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * Controller advice to translate the server side exceptions to client-friendly json structures.
+ */
+@ControllerAdvice
+public class ExceptionTranslator {
+
+ @ExceptionHandler(ConcurrencyFailureException.class)
+ @ResponseStatus(HttpStatus.CONFLICT)
+ @ResponseBody
+ public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) {
+ return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE);
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ResponseBody
+ public ErrorVM processValidationError(MethodArgumentNotValidException ex) {
+ BindingResult result = ex.getBindingResult();
+ List fieldErrors = result.getFieldErrors();
+
+ return processFieldErrors(fieldErrors);
+ }
+
+ @ExceptionHandler(CustomParameterizedException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ResponseBody
+ public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) {
+ return ex.getErrorVM();
+ }
+
+ @ExceptionHandler(AccessDeniedException.class)
+ @ResponseStatus(HttpStatus.FORBIDDEN)
+ @ResponseBody
+ public ErrorVM processAccessDeniedException(AccessDeniedException e) {
+ return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage());
+ }
+
+ private ErrorVM processFieldErrors(List fieldErrors) {
+ ErrorVM dto = new ErrorVM(ErrorConstants.ERR_VALIDATION);
+
+ for (FieldError fieldError : fieldErrors) {
+ dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode());
+ }
+
+ return dto;
+ }
+
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+ @ResponseBody
+ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+ public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
+ return new ErrorVM(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage());
+ }
+
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity processRuntimeException(Exception ex) {
+ BodyBuilder builder;
+ ErrorVM errorVM;
+ ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
+ if (responseStatus != null) {
+ builder = ResponseEntity.status(responseStatus.value());
+ errorVM = new ErrorVM("error." + responseStatus.value().value(), responseStatus.reason());
+ } else {
+ builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
+ errorVM = new ErrorVM(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error");
+ }
+ return builder.body(errorVM);
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/FieldErrorVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/FieldErrorVM.java
new file mode 100644
index 0000000000..0b2eef36d3
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/FieldErrorVM.java
@@ -0,0 +1,33 @@
+package com.gateway.web.rest.errors;
+
+import java.io.Serializable;
+
+public class FieldErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String objectName;
+
+ private final String field;
+
+ private final String message;
+
+ public FieldErrorVM(String dto, String field, String message) {
+ this.objectName = dto;
+ this.field = field;
+ this.message = message;
+ }
+
+ public String getObjectName() {
+ return objectName;
+ }
+
+ public String getField() {
+ return field;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ParameterizedErrorVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ParameterizedErrorVM.java
new file mode 100644
index 0000000000..aaac23d165
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/errors/ParameterizedErrorVM.java
@@ -0,0 +1,27 @@
+package com.gateway.web.rest.errors;
+
+import java.io.Serializable;
+
+/**
+ * View Model for sending a parameterized error message.
+ */
+public class ParameterizedErrorVM implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private final String message;
+ private final String[] params;
+
+ public ParameterizedErrorVM(String message, String... params) {
+ this.message = message;
+ this.params = params;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String[] getParams() {
+ return params;
+ }
+
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/package-info.java
new file mode 100644
index 0000000000..14a62c0429
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring MVC REST controllers.
+ */
+package com.gateway.web.rest;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/util/HeaderUtil.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/util/HeaderUtil.java
new file mode 100644
index 0000000000..399d8741dc
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/util/HeaderUtil.java
@@ -0,0 +1,43 @@
+package com.gateway.web.rest.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+
+/**
+ * Utility class for HTTP headers creation.
+ */
+public final class HeaderUtil {
+
+ private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class);
+
+ private HeaderUtil() {
+ }
+
+ public static HttpHeaders createAlert(String message, String param) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-gatewayApp-alert", message);
+ headers.add("X-gatewayApp-params", param);
+ return headers;
+ }
+
+ public static HttpHeaders createEntityCreationAlert(String entityName, String param) {
+ return createAlert("A new " + entityName + " is created with identifier " + param, param);
+ }
+
+ public static HttpHeaders createEntityUpdateAlert(String entityName, String param) {
+ return createAlert("A " + entityName + " is updated with identifier " + param, param);
+ }
+
+ public static HttpHeaders createEntityDeletionAlert(String entityName, String param) {
+ return createAlert("A " + entityName + " is deleted with identifier " + param, param);
+ }
+
+ public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) {
+ log.error("Entity creation failed, {}", defaultMessage);
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-gatewayApp-error", defaultMessage);
+ headers.add("X-gatewayApp-params", entityName);
+ return headers;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/util/PaginationUtil.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/util/PaginationUtil.java
new file mode 100644
index 0000000000..59ca9cb386
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/util/PaginationUtil.java
@@ -0,0 +1,47 @@
+package com.gateway.web.rest.util;
+
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.net.URISyntaxException;
+
+/**
+ * Utility class for handling pagination.
+ *
+ *
+ * Pagination uses the same principles as the Github API,
+ * and follow RFC 5988 (Link header).
+ */
+public final class PaginationUtil {
+
+ private PaginationUtil() {
+ }
+
+ public static HttpHeaders generatePaginationHttpHeaders(Page page, String baseUrl) {
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-Total-Count", "" + Long.toString(page.getTotalElements()));
+ String link = "";
+ if ((page.getNumber() + 1) < page.getTotalPages()) {
+ link = "<" + generateUri(baseUrl, page.getNumber() + 1, page.getSize()) + ">; rel=\"next\",";
+ }
+ // prev link
+ if ((page.getNumber()) > 0) {
+ link += "<" + generateUri(baseUrl, page.getNumber() - 1, page.getSize()) + ">; rel=\"prev\",";
+ }
+ // last and first link
+ int lastPage = 0;
+ if (page.getTotalPages() > 0) {
+ lastPage = page.getTotalPages() - 1;
+ }
+ link += "<" + generateUri(baseUrl, lastPage, page.getSize()) + ">; rel=\"last\",";
+ link += "<" + generateUri(baseUrl, 0, page.getSize()) + ">; rel=\"first\"";
+ headers.add(HttpHeaders.LINK, link);
+ return headers;
+ }
+
+ private static String generateUri(String baseUrl, int page, int size) {
+ return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/KeyAndPasswordVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/KeyAndPasswordVM.java
new file mode 100644
index 0000000000..6c23d40755
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/KeyAndPasswordVM.java
@@ -0,0 +1,27 @@
+package com.gateway.web.rest.vm;
+
+/**
+ * View Model object for storing the user's key and password.
+ */
+public class KeyAndPasswordVM {
+
+ private String key;
+
+ private String newPassword;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getNewPassword() {
+ return newPassword;
+ }
+
+ public void setNewPassword(String newPassword) {
+ this.newPassword = newPassword;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/LoggerVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/LoggerVM.java
new file mode 100644
index 0000000000..6ade2e5e9c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/LoggerVM.java
@@ -0,0 +1,48 @@
+package com.gateway.web.rest.vm;
+
+import ch.qos.logback.classic.Logger;
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+/**
+ * View Model object for storing a Logback logger.
+ */
+public class LoggerVM {
+
+ private String name;
+
+ private String level;
+
+ public LoggerVM(Logger logger) {
+ this.name = logger.getName();
+ this.level = logger.getEffectiveLevel().toString();
+ }
+
+ @JsonCreator
+ public LoggerVM() {
+ // Empty public constructor used by Jackson.
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLevel() {
+ return level;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ @Override
+ public String toString() {
+ return "LoggerVM{" +
+ "name='" + name + '\'' +
+ ", level='" + level + '\'' +
+ '}';
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/LoginVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/LoginVM.java
new file mode 100644
index 0000000000..c49f202378
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/LoginVM.java
@@ -0,0 +1,55 @@
+package com.gateway.web.rest.vm;
+
+import com.gateway.config.Constants;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+/**
+ * View Model object for storing a user's credentials.
+ */
+public class LoginVM {
+
+ @Pattern(regexp = Constants.LOGIN_REGEX)
+ @NotNull
+ @Size(min = 1, max = 50)
+ private String username;
+
+ @NotNull
+ @Size(min = ManagedUserVM.PASSWORD_MIN_LENGTH, max = ManagedUserVM.PASSWORD_MAX_LENGTH)
+ private String password;
+
+ private Boolean rememberMe;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public Boolean isRememberMe() {
+ return rememberMe;
+ }
+
+ public void setRememberMe(Boolean rememberMe) {
+ this.rememberMe = rememberMe;
+ }
+
+ @Override
+ public String toString() {
+ return "LoginVM{" +
+ "username='" + username + '\'' +
+ ", rememberMe=" + rememberMe +
+ '}';
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/ManagedUserVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/ManagedUserVM.java
new file mode 100644
index 0000000000..d40cea8573
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/ManagedUserVM.java
@@ -0,0 +1,45 @@
+package com.gateway.web.rest.vm;
+
+import com.gateway.service.dto.UserDTO;
+import javax.validation.constraints.Size;
+
+import java.time.ZonedDateTime;
+import java.util.Set;
+
+/**
+ * View Model extending the UserDTO, which is meant to be used in the user management UI.
+ */
+public class ManagedUserVM extends UserDTO {
+
+ public static final int PASSWORD_MIN_LENGTH = 4;
+
+ public static final int PASSWORD_MAX_LENGTH = 100;
+
+ @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH)
+ private String password;
+
+ public ManagedUserVM() {
+ // Empty constructor needed for Jackson.
+ }
+
+ public ManagedUserVM(Long id, String login, String password, String firstName, String lastName,
+ String email, boolean activated, String imageUrl, String langKey,
+ String createdBy, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate,
+ Set authorities) {
+
+ super(id, login, firstName, lastName, email, activated, imageUrl, langKey,
+ createdBy, createdDate, lastModifiedBy, lastModifiedDate, authorities);
+
+ this.password = password;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String toString() {
+ return "ManagedUserVM{" +
+ "} " + super.toString();
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/RouteVM.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/RouteVM.java
new file mode 100644
index 0000000000..236924557a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/RouteVM.java
@@ -0,0 +1,41 @@
+package com.gateway.web.rest.vm;
+
+import java.util.List;
+
+import org.springframework.cloud.client.ServiceInstance;
+
+/**
+ * View Model that stores a route managed by the Gateway.
+ */
+public class RouteVM {
+
+ private String path;
+
+ private String serviceId;
+
+ private List serviceInstances;
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ public void setServiceId(String serviceId) {
+ this.serviceId = serviceId;
+ }
+
+ public List getServiceInstances() {
+ return serviceInstances;
+ }
+
+ public void setServiceInstances(List serviceInstances) {
+ this.serviceInstances = serviceInstances;
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/package-info.java b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/package-info.java
new file mode 100644
index 0000000000..c36f362740
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/java/com/gateway/web/rest/vm/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * View Models used by Spring MVC REST controllers.
+ */
+package com.gateway.web.rest.vm;
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/.h2.server.properties b/jhipster/jhipster-microservice/gateway-app/src/main/resources/.h2.server.properties
new file mode 100644
index 0000000000..0982faf6dd
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/.h2.server.properties
@@ -0,0 +1,5 @@
+#H2 Server Properties
+0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/gateway|gateway
+webAllowOthers=true
+webPort=8082
+webSSL=false
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/banner.txt b/jhipster/jhipster-microservice/gateway-app/src/main/resources/banner.txt
new file mode 100644
index 0000000000..c3d8cf725d
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+
+ ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗
+ ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ â•šâ•â•â–ˆâ–ˆâ•”â•â•â• ██╔â•â•â•â–ˆâ–ˆâ•— ██╔â•â•â•â•â• â•šâ•â•â–ˆâ–ˆâ•”â•â•â• ██╔â•â•â•â•â•â• ██╔â•â•â•â–ˆâ–ˆâ•—
+ ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╠╚█████╗ ██║ ██████╗ ███████╔â•
+ ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔â•â•â•â–ˆâ–ˆâ•‘ ██║ ██╔â•â•â•â•â• â•šâ•â•â•â–ˆâ–ˆâ•— ██║ ██╔â•â•â•â• ██╔â•â•â–ˆâ–ˆâ•‘
+ ${AnsiColor.GREEN}╚██████╔â•${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╠██║ ████████╗ ██║ ╚██╗
+ ${AnsiColor.GREEN} â•šâ•â•â•â•â•â• ${AnsiColor.RED} â•šâ•â• â•šâ•â• â•šâ•â•â•â•â•â•â•â• â•šâ•â• â•šâ•â•â•â•â•â• â•šâ•â• â•šâ•â•â•â•â•â•â•â• â•šâ•â• â•šâ•â•
+
+${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} ::
+:: http://jhipster.github.io ::${AnsiColor.DEFAULT}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application-dev.yml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application-dev.yml
new file mode 100644
index 0000000000..2e425a0ebf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application-dev.yml
@@ -0,0 +1,163 @@
+# ===================================================================
+# Spring Boot configuration for the "dev" profile.
+#
+# This configuration overrides the application.yml file.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ prefer-ip-address: true
+ client:
+ enabled: true
+ healthcheck:
+ enabled: true
+ registerWithEureka: true
+ fetchRegistry: true
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
+
+spring:
+ profiles:
+ active: dev
+ include: swagger
+ autoconfigure:
+ exclude: org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
+ devtools:
+ restart:
+ enabled: true
+ livereload:
+ enabled: false # we use gulp + BrowserSync for livereload
+ jackson:
+ serialization.indent_output: true
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:h2:file:./target/h2db/db/gateway;DB_CLOSE_DELAY=-1
+ username: gateway
+ password:
+ h2:
+ console:
+ enabled: false
+ jpa:
+ database-platform: io.github.jhipster.domain.util.FixedH2Dialect
+ database: H2
+ show-sql: true
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: true
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: true
+ hibernate.cache.region.factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
+ hibernate.cache.hazelcast.instance_name: gateway
+ hibernate.cache.use_minimal_puts: true
+ hibernate.cache.hazelcast.use_lite_member: true
+ data:
+ cassandra:
+ contactPoints: localhost
+ protocolVersion: V4
+ compression: LZ4
+ keyspaceName: gateway
+ repositories:
+ enabled: false
+ mail:
+ host: localhost
+ port: 25
+ username:
+ password:
+ messages:
+ cache-seconds: 1
+ thymeleaf:
+ cache: false
+
+liquibase:
+ contexts: dev
+
+# ===================================================================
+# To enable SSL, generate a certificate using:
+# keytool -genkey -alias gateway -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
+#
+# You can also use Let's Encrypt:
+# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
+#
+# Then, modify the server.ssl properties so your "server" configuration looks like:
+#
+# server:
+# port: 8443
+# ssl:
+# key-store: keystore.p12
+# key-store-password:
+# keyStoreType: PKCS12
+# keyAlias: gateway
+# ===================================================================
+server:
+ port: 8080
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ gateway:
+ rate-limiting: # Read the documentation in the GatewayConfiguration class before enabling.
+ enabled: false
+ authorized-microservices-endpoints: # Access Control Policy, if left empty for a route, all endpoints will be accessible
+ app1: /api,/v2/api-docs # recommended dev configuration
+ http:
+ version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
+ cache: # Cache configuration
+ hazelcast: # Hazelcast distributed cache
+ time-to-live-seconds: 3600
+ backup-count: 1
+ security:
+ authentication:
+ jwt:
+ secret: my-secret-token-to-change-in-production
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ token-validity-in-seconds-for-remember-me: 2592000
+ mail: # specific JHipster mail property, for standard properties see MailProperties
+ from: gateway@localhost
+ base-url: http://127.0.0.1:8080
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+ graphite: # Use the "graphite" Maven profile to have the Graphite dependencies
+ enabled: false
+ host: localhost
+ port: 2003
+ prefix: gateway
+ prometheus: # Use the "prometheus" Maven profile to have the Prometheus dependencies
+ enabled: false
+ endpoint: /prometheusMetrics
+ logs: # Reports Dropwizard metrics in the logs
+ enabled: false
+ reportFrequency: 60 # in seconds
+ logging:
+ logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
+ enabled: false
+ host: localhost
+ port: 5000
+ queue-size: 512
+ spectator-metrics: # Reports Spectator Circuit Breaker metrics in the logs
+ enabled: false
+ # edit spring.metrics.export.delay-millis to set report frequency
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application-prod.yml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application-prod.yml
new file mode 100644
index 0000000000..3395ef8f66
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application-prod.yml
@@ -0,0 +1,165 @@
+# ===================================================================
+# Spring Boot configuration for the "prod" profile.
+#
+# This configuration overrides the application.yml file.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ prefer-ip-address: true
+ client:
+ enabled: true
+ healthcheck:
+ enabled: true
+ registerWithEureka: true
+ fetchRegistry: true
+ serviceUrl:
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
+
+spring:
+ autoconfigure:
+ exclude: org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
+ devtools:
+ restart:
+ enabled: false
+ livereload:
+ enabled: false
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ url: jdbc:mysql://localhost:3306/gateway?useUnicode=true&characterEncoding=utf8&useSSL=false
+ username: root
+ password:
+ hikari:
+ data-source-properties:
+ cachePrepStmts: true
+ prepStmtCacheSize: 250
+ prepStmtCacheSqlLimit: 2048
+ useServerPrepStmts: true
+ jpa:
+ database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
+ database: MYSQL
+ show-sql: false
+ properties:
+ hibernate.id.new_generator_mappings: true
+ hibernate.cache.use_second_level_cache: true
+ hibernate.cache.use_query_cache: false
+ hibernate.generate_statistics: false
+ hibernate.cache.region.factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
+ hibernate.cache.hazelcast.instance_name: gateway
+ hibernate.cache.use_minimal_puts: true
+ hibernate.cache.hazelcast.use_lite_member: true
+ data:
+ cassandra:
+ contactPoints: localhost
+ protocolVersion: V4
+ compression: LZ4
+ keyspaceName: gateway
+ repositories:
+ enabled: false
+ mail:
+ host: localhost
+ port: 25
+ username:
+ password:
+ thymeleaf:
+ cache: true
+
+liquibase:
+ contexts: prod
+
+# ===================================================================
+# To enable SSL, generate a certificate using:
+# keytool -genkey -alias gateway -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
+#
+# You can also use Let's Encrypt:
+# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
+#
+# Then, modify the server.ssl properties so your "server" configuration looks like:
+#
+# server:
+# port: 443
+# ssl:
+# key-store: keystore.p12
+# key-store-password:
+# keyStoreType: PKCS12
+# keyAlias: gateway
+# ===================================================================
+server:
+ port: 8080
+ compression:
+ enabled: true
+ mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json
+ min-response-size: 1024
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ gateway:
+ rate-limiting: # Warning! Read the documentation in the GatewayConfiguration class.
+ enabled: false
+ authorized-microservices-endpoints: # Access Control Policy, if left empty for a route, all endpoints will be accessible
+ app1: /api # recommended prod configuration
+ http:
+ version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
+ cache: # Used by the CachingHttpHeadersFilter
+ timeToLiveInDays: 1461
+ cache: # Cache configuration
+ hazelcast: # Hazelcast distributed cache
+ time-to-live-seconds: 3600
+ backup-count: 1
+ security:
+ authentication:
+ jwt:
+ secret: cd875958b5c9a8fa965803fd599da3c13abf1015
+ # Token is valid 24 hours
+ token-validity-in-seconds: 86400
+ token-validity-in-seconds-for-remember-me: 2592000
+ mail: # specific JHipster mail property, for standard properties see MailProperties
+ from: gateway@localhost
+ base-url: http://my-server-url-to-change # Modify according to your server's URL
+ metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
+ jmx.enabled: true
+ graphite:
+ enabled: false
+ host: localhost
+ port: 2003
+ prefix: gateway
+ prometheus:
+ enabled: false
+ endpoint: /prometheusMetrics
+ logs: # Reports Dropwizard metrics in the logs
+ enabled: false
+ reportFrequency: 60 # in seconds
+ logging:
+ logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
+ enabled: false
+ host: localhost
+ port: 5000
+ queue-size: 512
+ spectator-metrics: # Reports Spectator Circuit Breaker metrics in the logs
+ enabled: false
+ # edit spring.metrics.export.delay-millis to set report frequency
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application.yml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application.yml
new file mode 100644
index 0000000000..ef77b24a69
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/application.yml
@@ -0,0 +1,124 @@
+# ===================================================================
+# Spring Boot configuration.
+#
+# This configuration will be overriden by the Spring profile you use,
+# for example application-dev.yml if you use the "dev" profile.
+#
+# More information on profiles: https://jhipster.github.io/profiles/
+# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+eureka:
+ instance:
+ appname: gateway
+ instanceId: gateway:${spring.application.instance_id:${random.value}}
+ statusPageUrlPath: ${management.context-path}/info
+ healthCheckUrlPath: ${management.context-path}/health
+ metadata-map:
+ profile: ${spring.profiles.active}
+ version: ${info.project.version}
+ribbon:
+ eureka:
+ enabled: true
+zuul:
+ host:
+ connect-timeout-millis: 5000
+ socket-timeout-millis: 10000
+
+# See https://github.com/Netflix/Hystrix/wiki/Configuration
+hystrix:
+ command:
+ default:
+ execution:
+ isolation:
+ thread:
+ timeoutInMilliseconds: 10000
+
+management:
+ security:
+ roles: ADMIN
+ context-path: /management
+ health:
+ mail:
+ enabled: false # When using the MailService, configure an SMTP server and set this to true
+spring:
+ application:
+ name: gateway
+ jackson:
+ serialization.write_dates_as_timestamps: false
+ jpa:
+ open-in-view: false
+ hibernate:
+ ddl-auto: none
+ naming:
+ physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
+ implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+ messages:
+ basename: i18n/messages
+ mvc:
+ favicon:
+ enabled: false
+ thymeleaf:
+ mode: XHTML
+
+security:
+ basic:
+ enabled: false
+
+server:
+ session:
+ cookie:
+ http-only: true
+
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+jhipster:
+ async:
+ core-pool-size: 2
+ max-pool-size: 50
+ queue-capacity: 10000
+ # By default CORS is disabled. Uncomment to enable.
+ #cors:
+ #allowed-origins: "*"
+ #allowed-methods: GET, PUT, POST, DELETE, OPTIONS
+ #allowed-headers: "*"
+ #exposed-headers:
+ #allow-credentials: true
+ #max-age: 1800
+ mail:
+ from: gateway@localhost
+ swagger:
+ default-include-pattern: /api/.*
+ title: gateway API
+ description: gateway API documentation
+ version: 0.0.1
+ terms-of-service-url:
+ contact-name:
+ contact-url:
+ contact-email:
+ license:
+ license-url:
+ ribbon:
+ display-on-active-profiles: dev
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://jhipster.github.io/common-application-properties/
+# ===================================================================
+
+application:
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/bootstrap-prod.yml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/bootstrap-prod.yml
new file mode 100644
index 0000000000..a16128e2cb
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/bootstrap-prod.yml
@@ -0,0 +1,22 @@
+# ===================================================================
+# Spring Cloud Config bootstrap configuration for the "prod" profile
+# ===================================================================
+
+spring:
+ cloud:
+ config:
+ fail-fast: true
+ retry:
+ initial-interval: 1000
+ max-interval: 2000
+ max-attempts: 100
+ uri: http://admin:${jhipster.registry.password}@localhost:8761/config
+ # name of the config server's property source (file.yml) that we want to use
+ name: gateway
+ profile: prod # profile(s) of the property source
+ label: master # toggle to switch to a different version of the configuration as stored in git
+ # it can be set to any label, branch or commit of the config source git repository
+
+jhipster:
+ registry:
+ password: admin
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/bootstrap.yml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/bootstrap.yml
new file mode 100644
index 0000000000..1ac86c4aaa
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/bootstrap.yml
@@ -0,0 +1,30 @@
+# ===================================================================
+# Spring Cloud Config bootstrap configuration for the "dev" profile
+# In prod profile, properties will be overwriten by the ones defined in bootstrap-prod.yml
+# ===================================================================
+
+jhipster:
+ registry:
+ password: admin
+
+spring:
+ application:
+ name: gateway
+ profiles:
+ # The commented value for `active` can be replaced with valid Spring profiles to load.
+ # Otherwise, it will be filled in by maven when building the WAR file
+ # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS`
+ active: #spring.profiles.active#
+ cloud:
+ config:
+ fail-fast: true
+ uri: http://admin:${jhipster.registry.password}@localhost:8761/config
+ # name of the config server's property source (file.yml) that we want to use
+ name: gateway
+ profile: dev # profile(s) of the property source
+ label: master # toggle to switch to a different version of the configuration as stored in git
+ # it can be set to any label, branch or commit of the config source git repository
+
+info:
+ project:
+ version: #project.version#
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/changelog/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/changelog/README.md
new file mode 100644
index 0000000000..1dbe25abdd
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/changelog/README.md
@@ -0,0 +1,10 @@
+The changelog folder for cassandra/cql files is similar to Liquibase (for SQL databases), but with a minimal tooling.
+
+- The script name should follow the pattern yyyyMMddHHmmss_{script-name}.cql
+ - eg: 20150805124838_added_entity_BankAccount.cql
+- The scripts will be applied sequentially in alphabetical order
+- The scripts will be applied automatically only in two contexts:
+ - Unit tests
+ - Docker-compose for to start a [cassandra cluster for development](http://jhipster.github.io/docker-compose/#cassandra-in-development)
+
+Unlike Liquibase, the scripts are not currently automatically applied to the database when deployed with a production profile
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/create-keyspace-prod.cql b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/create-keyspace-prod.cql
new file mode 100644
index 0000000000..bbdfed3e38
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/create-keyspace-prod.cql
@@ -0,0 +1,13 @@
+CREATE KEYSPACE IF NOT EXISTS gateway
+ WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 3 }
+ AND DURABLE_WRITES = true;
+
+CREATE TABLE IF NOT EXISTS gateway.schema_version (
+ script_name text,
+ checksum text,
+ executed_by text,
+ executed_on timestamp,
+ execution_time int,
+ status text,
+ PRIMARY KEY(script_name)
+);
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/create-keyspace.cql b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/create-keyspace.cql
new file mode 100644
index 0000000000..4be188bcf8
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/create-keyspace.cql
@@ -0,0 +1,13 @@
+CREATE KEYSPACE IF NOT EXISTS gateway
+ WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }
+ AND DURABLE_WRITES = true;
+
+CREATE TABLE IF NOT EXISTS gateway.schema_version (
+ script_name text,
+ checksum text,
+ executed_by text,
+ executed_on timestamp,
+ execution_time int,
+ status text,
+ PRIMARY KEY(script_name)
+);
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/drop-keyspace.cql b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/drop-keyspace.cql
new file mode 100644
index 0000000000..e45d91ea8e
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/cql/drop-keyspace.cql
@@ -0,0 +1 @@
+DROP KEYSPACE IF EXISTS gateway;
diff --git a/jhipster/src/main/resources/config/liquibase/authorities.csv b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/authorities.csv
similarity index 100%
rename from jhipster/src/main/resources/config/liquibase/authorities.csv
rename to jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/authorities.csv
diff --git a/jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
similarity index 100%
rename from jhipster/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
rename to jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/master.xml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/master.xml
new file mode 100644
index 0000000000..e282fbf8cf
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/master.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/jhipster/src/main/resources/config/liquibase/users.csv b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/users.csv
similarity index 100%
rename from jhipster/src/main/resources/config/liquibase/users.csv
rename to jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/users.csv
diff --git a/jhipster/src/main/resources/config/liquibase/users_authorities.csv b/jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/users_authorities.csv
similarity index 100%
rename from jhipster/src/main/resources/config/liquibase/users_authorities.csv
rename to jhipster/jhipster-microservice/gateway-app/src/main/resources/config/liquibase/users_authorities.csv
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/i18n/messages.properties b/jhipster/jhipster-microservice/gateway-app/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000000..7733f0013c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/i18n/messages.properties
@@ -0,0 +1,22 @@
+# Error page
+error.title=Your request cannot be processed
+error.subtitle=Sorry, an error has occurred.
+error.status=Status:
+error.message=Message:
+
+# Activation e-mail
+email.activation.title=gateway account activation
+email.activation.greeting=Dear {0}
+email.activation.text1=Your gateway account has been created, please click on the URL below to activate it:
+email.activation.text2=Regards,
+email.signature=gateway Team.
+
+# Creation email
+email.creation.text1=Your gateway account has been created, please click on the URL below to access it:
+
+# Reset e-mail
+email.reset.title=gateway password reset
+email.reset.greeting=Dear {0}
+email.reset.text1=For your gateway account a password reset was requested, please click on the URL below to reset it:
+email.reset.text2=Regards,
+
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/logback-spring.xml b/jhipster/jhipster-microservice/gateway-app/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000..1e84075691
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/logback-spring.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
diff --git a/jhipster/src/main/resources/mails/activationEmail.html b/jhipster/jhipster-microservice/gateway-app/src/main/resources/mails/activationEmail.html
similarity index 100%
rename from jhipster/src/main/resources/mails/activationEmail.html
rename to jhipster/jhipster-microservice/gateway-app/src/main/resources/mails/activationEmail.html
diff --git a/jhipster/src/main/resources/mails/creationEmail.html b/jhipster/jhipster-microservice/gateway-app/src/main/resources/mails/creationEmail.html
similarity index 100%
rename from jhipster/src/main/resources/mails/creationEmail.html
rename to jhipster/jhipster-microservice/gateway-app/src/main/resources/mails/creationEmail.html
diff --git a/jhipster/src/main/resources/mails/passwordResetEmail.html b/jhipster/jhipster-microservice/gateway-app/src/main/resources/mails/passwordResetEmail.html
similarity index 100%
rename from jhipster/src/main/resources/mails/passwordResetEmail.html
rename to jhipster/jhipster-microservice/gateway-app/src/main/resources/mails/passwordResetEmail.html
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/resources/templates/error.html b/jhipster/jhipster-microservice/gateway-app/src/main/resources/templates/error.html
new file mode 100644
index 0000000000..774b080d6c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/resources/templates/error.html
@@ -0,0 +1,162 @@
+
+
+
+
+ Your request cannot be processed
+
+
+
+
+ Registration saved! Please check your email for confirmation.
+
+
+
+ Registration failed! Please try again later.
+
+
+
+ Login name already registered! Please choose another one.
+
+
+
+ E-mail is already in use! Please choose another one.
+
+
+
+ The password and its confirmation do not match!
+
+
+
+
+
+
+ If you want to sign in, you can try the default accounts: - Administrator (login="admin" and password="admin") - User (login="user" and password="user").
+
Active requests{{vm.metrics.counters['com.codahale.metrics.servlet.InstrumentedFilter.activeRequests'].count | number:0}} - Total requests{{vm.metrics.timers['com.codahale.metrics.servlet.InstrumentedFilter.requests'].count | number:0}}
+ You are logged in as user "{{vm.account.login}}".
+
+
+
+ If you want to sign in, you can try the default accounts: - Administrator (login="admin" and password="admin") - User (login="user" and password="user").
+
',
+ link : linkFunc
+ };
+
+ return directive;
+
+ function linkFunc(scope, element, attrs) {
+ ProfileService.getProfileInfo().then(function(response) {
+ if (response.ribbonEnv) {
+ scope.ribbonEnv = response.ribbonEnv;
+ element.addClass(response.ribbonEnv);
+ element.removeClass('hidden');
+ }
+ });
+ }
+ }
+})();
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/app/services/profiles/profile.service.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/app/services/profiles/profile.service.js
new file mode 100644
index 0000000000..d6f0d42670
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/app/services/profiles/profile.service.js
@@ -0,0 +1,36 @@
+(function() {
+ 'use strict';
+
+ angular
+ .module('gatewayApp')
+ .factory('ProfileService', ProfileService);
+
+ ProfileService.$inject = ['$http'];
+
+ function ProfileService($http) {
+
+ var dataPromise;
+
+ var service = {
+ getProfileInfo : getProfileInfo
+ };
+
+ return service;
+
+ function getProfileInfo() {
+ if (angular.isUndefined(dataPromise)) {
+ dataPromise = $http.get('api/profile-info').then(function(result) {
+ if (result.data.activeProfiles) {
+ var response = {};
+ response.activeProfiles = result.data.activeProfiles;
+ response.ribbonEnv = result.data.ribbonEnv;
+ response.inProduction = result.data.activeProfiles.indexOf("prod") !== -1;
+ response.swaggerEnabled = result.data.activeProfiles.indexOf("swagger") !== -1;
+ return response;
+ }
+ });
+ }
+ return dataPromise;
+ }
+ }
+})();
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/app/services/user/user.service.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/app/services/user/user.service.js
new file mode 100644
index 0000000000..fd6edb1128
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/app/services/user/user.service.js
@@ -0,0 +1,27 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('gatewayApp')
+ .factory('User', User);
+
+ User.$inject = ['$resource'];
+
+ function User ($resource) {
+ var service = $resource('api/users/:login', {}, {
+ 'query': {method: 'GET', isArray: true},
+ 'get': {
+ method: 'GET',
+ transformResponse: function (data) {
+ data = angular.fromJson(data);
+ return data;
+ }
+ },
+ 'save': { method:'POST' },
+ 'update': { method:'PUT' },
+ 'delete':{ method:'DELETE'}
+ });
+
+ return service;
+ }
+})();
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/.bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/.bower.json
new file mode 100644
index 0000000000..6fc255b3b3
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/.bower.json
@@ -0,0 +1,20 @@
+{
+ "name": "angular-aria",
+ "version": "1.5.8",
+ "license": "MIT",
+ "main": "./angular-aria.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.5.8"
+ },
+ "homepage": "https://github.com/angular/bower-angular-aria",
+ "_release": "1.5.8",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.8",
+ "commit": "91710f3ee26d4e2858af0c91ec462aa43360da47"
+ },
+ "_source": "https://github.com/angular/bower-angular-aria.git",
+ "_target": "1.5.8",
+ "_originalSource": "angular-aria"
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/LICENSE.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/LICENSE.md
new file mode 100644
index 0000000000..2c395eef1b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Angular
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/README.md
new file mode 100644
index 0000000000..04c5a8f949
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/README.md
@@ -0,0 +1,67 @@
+# packaged angular-aria
+
+This repo is for distribution on `npm` and `bower`. The source for this module is in the
+[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngAria).
+Please file issues and pull requests against that repo.
+
+## Install
+
+You can install this package either with `npm` or with `bower`.
+
+### npm
+
+```shell
+npm install angular-aria
+```
+Then add `ngAria` as a dependency for your app:
+
+```javascript
+angular.module('myApp', [require('angular-aria')]);
+```
+
+### bower
+
+```shell
+bower install angular-aria
+```
+
+Add a `
+```
+
+Then add `ngAria` as a dependency for your app:
+
+```javascript
+angular.module('myApp', ['ngAria']);
+```
+
+## Documentation
+
+Documentation is available on the
+[AngularJS docs site](http://docs.angularjs.org/api/ngAria).
+
+## License
+
+The MIT License
+
+Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.js
new file mode 100644
index 0000000000..e9048c638f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.js
@@ -0,0 +1,405 @@
+/**
+ * @license AngularJS v1.5.8
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular) {'use strict';
+
+/**
+ * @ngdoc module
+ * @name ngAria
+ * @description
+ *
+ * The `ngAria` module provides support for common
+ * [ARIA](http://www.w3.org/TR/wai-aria/)
+ * attributes that convey state or semantic information about the application for users
+ * of assistive technologies, such as screen readers.
+ *
+ *
+ *
+ * ## Usage
+ *
+ * For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
+ * directives are supported:
+ * `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
+ * `ngDblClick`, and `ngMessages`.
+ *
+ * Below is a more detailed breakdown of the attributes handled by ngAria:
+ *
+ * | Directive | Supported Attributes |
+ * |---------------------------------------------|----------------------------------------------------------------------------------------|
+ * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
+ * | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
+ * | {@link ng.directive:ngRequired ngRequired} | aria-required
+ * | {@link ng.directive:ngChecked ngChecked} | aria-checked
+ * | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly |
+ * | {@link ng.directive:ngValue ngValue} | aria-checked |
+ * | {@link ng.directive:ngShow ngShow} | aria-hidden |
+ * | {@link ng.directive:ngHide ngHide} | aria-hidden |
+ * | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
+ * | {@link module:ngMessages ngMessages} | aria-live |
+ * | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
+ *
+ * Find out more information about each directive by reading the
+ * {@link guide/accessibility ngAria Developer Guide}.
+ *
+ * ## Example
+ * Using ngDisabled with ngAria:
+ * ```html
+ *
+ * ```
+ * Becomes:
+ * ```html
+ *
+ * ```
+ *
+ * ## Disabling Attributes
+ * It's possible to disable individual attributes added by ngAria with the
+ * {@link ngAria.$ariaProvider#config config} method. For more details, see the
+ * {@link guide/accessibility Developer Guide}.
+ */
+ /* global -ngAriaModule */
+var ngAriaModule = angular.module('ngAria', ['ng']).
+ provider('$aria', $AriaProvider);
+
+/**
+* Internal Utilities
+*/
+var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY'];
+
+var isNodeOneOf = function(elem, nodeTypeArray) {
+ if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
+ return true;
+ }
+};
+/**
+ * @ngdoc provider
+ * @name $ariaProvider
+ *
+ * @description
+ *
+ * Used for configuring the ARIA attributes injected and managed by ngAria.
+ *
+ * ```js
+ * angular.module('myApp', ['ngAria'], function config($ariaProvider) {
+ * $ariaProvider.config({
+ * ariaValue: true,
+ * tabindex: false
+ * });
+ * });
+ *```
+ *
+ * ## Dependencies
+ * Requires the {@link ngAria} module to be installed.
+ *
+ */
+function $AriaProvider() {
+ var config = {
+ ariaHidden: true,
+ ariaChecked: true,
+ ariaReadonly: true,
+ ariaDisabled: true,
+ ariaRequired: true,
+ ariaInvalid: true,
+ ariaValue: true,
+ tabindex: true,
+ bindKeypress: true,
+ bindRoleForClick: true
+ };
+
+ /**
+ * @ngdoc method
+ * @name $ariaProvider#config
+ *
+ * @param {object} config object to enable/disable specific ARIA attributes
+ *
+ * - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
+ * - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
+ * - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags
+ * - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
+ * - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
+ * - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
+ * - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
+ * - **tabindex** – `{boolean}` – Enables/disables tabindex tags
+ * - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and
+ * `li` elements with ng-click
+ * - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
+ * using ng-click, making them more accessible to users of assistive technologies
+ *
+ * @description
+ * Enables/disables various ARIA attributes
+ */
+ this.config = function(newConfig) {
+ config = angular.extend(config, newConfig);
+ };
+
+ function watchExpr(attrName, ariaAttr, nodeBlackList, negate) {
+ return function(scope, elem, attr) {
+ var ariaCamelName = attr.$normalize(ariaAttr);
+ if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) {
+ scope.$watch(attr[attrName], function(boolVal) {
+ // ensure boolean value
+ boolVal = negate ? !boolVal : !!boolVal;
+ elem.attr(ariaAttr, boolVal);
+ });
+ }
+ };
+ }
+ /**
+ * @ngdoc service
+ * @name $aria
+ *
+ * @description
+ * @priority 200
+ *
+ * The $aria service contains helper methods for applying common
+ * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
+ *
+ * ngAria injects common accessibility attributes that tell assistive technologies when HTML
+ * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
+ * let's review a code snippet from ngAria itself:
+ *
+ *```js
+ * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
+ * return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
+ * }])
+ *```
+ * Shown above, the ngAria module creates a directive with the same signature as the
+ * traditional `ng-disabled` directive. But this ngAria version is dedicated to
+ * solely managing accessibility attributes on custom elements. The internal `$aria` service is
+ * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
+ * developer, `aria-disabled` is injected as an attribute with its value synchronized to the
+ * value in `ngDisabled`.
+ *
+ * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
+ * anything to enable this feature. The `aria-disabled` attribute is automatically managed
+ * simply as a silent side-effect of using `ng-disabled` with the ngAria module.
+ *
+ * The full list of directives that interface with ngAria:
+ * * **ngModel**
+ * * **ngChecked**
+ * * **ngReadonly**
+ * * **ngRequired**
+ * * **ngDisabled**
+ * * **ngValue**
+ * * **ngShow**
+ * * **ngHide**
+ * * **ngClick**
+ * * **ngDblclick**
+ * * **ngMessages**
+ *
+ * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
+ * directive.
+ *
+ *
+ * ## Dependencies
+ * Requires the {@link ngAria} module to be installed.
+ */
+ this.$get = function() {
+ return {
+ config: function(key) {
+ return config[key];
+ },
+ $$watchExpr: watchExpr
+ };
+ };
+}
+
+
+ngAriaModule.directive('ngShow', ['$aria', function($aria) {
+ return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true);
+}])
+.directive('ngHide', ['$aria', function($aria) {
+ return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
+}])
+.directive('ngValue', ['$aria', function($aria) {
+ return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
+}])
+.directive('ngChecked', ['$aria', function($aria) {
+ return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
+}])
+.directive('ngReadonly', ['$aria', function($aria) {
+ return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
+}])
+.directive('ngRequired', ['$aria', function($aria) {
+ return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
+}])
+.directive('ngModel', ['$aria', function($aria) {
+
+ function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
+ return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
+ }
+
+ function shouldAttachRole(role, elem) {
+ // if element does not have role attribute
+ // AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
+ // AND element is not INPUT
+ return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
+ }
+
+ function getShape(attr, elem) {
+ var type = attr.type,
+ role = attr.role;
+
+ return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
+ ((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' :
+ (type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : '';
+ }
+
+ return {
+ restrict: 'A',
+ require: 'ngModel',
+ priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
+ compile: function(elem, attr) {
+ var shape = getShape(attr, elem);
+
+ return {
+ pre: function(scope, elem, attr, ngModel) {
+ if (shape === 'checkbox') {
+ //Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
+ ngModel.$isEmpty = function(value) {
+ return value === false;
+ };
+ }
+ },
+ post: function(scope, elem, attr, ngModel) {
+ var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
+
+ function ngAriaWatchModelValue() {
+ return ngModel.$modelValue;
+ }
+
+ function getRadioReaction(newVal) {
+ var boolVal = (attr.value == ngModel.$viewValue);
+ elem.attr('aria-checked', boolVal);
+ }
+
+ function getCheckboxReaction() {
+ elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
+ }
+
+ switch (shape) {
+ case 'radio':
+ case 'checkbox':
+ if (shouldAttachRole(shape, elem)) {
+ elem.attr('role', shape);
+ }
+ if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
+ scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
+ getRadioReaction : getCheckboxReaction);
+ }
+ if (needsTabIndex) {
+ elem.attr('tabindex', 0);
+ }
+ break;
+ case 'range':
+ if (shouldAttachRole(shape, elem)) {
+ elem.attr('role', 'slider');
+ }
+ if ($aria.config('ariaValue')) {
+ var needsAriaValuemin = !elem.attr('aria-valuemin') &&
+ (attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
+ var needsAriaValuemax = !elem.attr('aria-valuemax') &&
+ (attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
+ var needsAriaValuenow = !elem.attr('aria-valuenow');
+
+ if (needsAriaValuemin) {
+ attr.$observe('min', function ngAriaValueMinReaction(newVal) {
+ elem.attr('aria-valuemin', newVal);
+ });
+ }
+ if (needsAriaValuemax) {
+ attr.$observe('max', function ngAriaValueMinReaction(newVal) {
+ elem.attr('aria-valuemax', newVal);
+ });
+ }
+ if (needsAriaValuenow) {
+ scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
+ elem.attr('aria-valuenow', newVal);
+ });
+ }
+ }
+ if (needsTabIndex) {
+ elem.attr('tabindex', 0);
+ }
+ break;
+ }
+
+ if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
+ && shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
+ // ngModel.$error.required is undefined on custom controls
+ attr.$observe('required', function() {
+ elem.attr('aria-required', !!attr['required']);
+ });
+ }
+
+ if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
+ scope.$watch(function ngAriaInvalidWatch() {
+ return ngModel.$invalid;
+ }, function ngAriaInvalidReaction(newVal) {
+ elem.attr('aria-invalid', !!newVal);
+ });
+ }
+ }
+ };
+ }
+ };
+}])
+.directive('ngDisabled', ['$aria', function($aria) {
+ return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
+}])
+.directive('ngMessages', function() {
+ return {
+ restrict: 'A',
+ require: '?ngMessages',
+ link: function(scope, elem, attr, ngMessages) {
+ if (!elem.attr('aria-live')) {
+ elem.attr('aria-live', 'assertive');
+ }
+ }
+ };
+})
+.directive('ngClick',['$aria', '$parse', function($aria, $parse) {
+ return {
+ restrict: 'A',
+ compile: function(elem, attr) {
+ var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
+ return function(scope, elem, attr) {
+
+ if (!isNodeOneOf(elem, nodeBlackList)) {
+
+ if ($aria.config('bindRoleForClick') && !elem.attr('role')) {
+ elem.attr('role', 'button');
+ }
+
+ if ($aria.config('tabindex') && !elem.attr('tabindex')) {
+ elem.attr('tabindex', 0);
+ }
+
+ if ($aria.config('bindKeypress') && !attr.ngKeypress) {
+ elem.on('keypress', function(event) {
+ var keyCode = event.which || event.keyCode;
+ if (keyCode === 32 || keyCode === 13) {
+ scope.$apply(callback);
+ }
+
+ function callback() {
+ fn(scope, { $event: event });
+ }
+ });
+ }
+ }
+ };
+ }
+ };
+}])
+.directive('ngDblclick', ['$aria', function($aria) {
+ return function(scope, elem, attr) {
+ if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) {
+ elem.attr('tabindex', 0);
+ }
+ };
+}]);
+
+
+})(window, window.angular);
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.min.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.min.js
new file mode 100644
index 0000000000..94e202042f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.min.js
@@ -0,0 +1,14 @@
+/*
+ AngularJS v1.5.8
+ (c) 2010-2016 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(t,p){'use strict';var b="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};p.module("ngAria",["ng"]).provider("$aria",function(){function a(a,b,m,h){return function(d,f,e){var q=e.$normalize(b);!c[q]||l(f,m)||e[q]||d.$watch(e[a],function(a){a=h?!a:!!a;f.attr(b,a)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};
+this.config=function(a){c=p.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",b,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",b,!1)}]).directive("ngReadonly",
+["$aria",function(a){return a.$$watchExpr("ngReadonly","aria-readonly",b,!1)}]).directive("ngRequired",["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",b,!1)}]).directive("ngModel",["$aria",function(a){function c(c,h,d,f){return a.config(h)&&!d.attr(c)&&(f||!l(d,b))}function g(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function k(a,c){var d=a.type,f=a.role;return"checkbox"===(d||f)||"menuitemcheckbox"===f?"checkbox":"radio"===(d||f)||"menuitemradio"===
+f?"radio":"range"===d||"progressbar"===f||"slider"===f?"range":""}return{restrict:"A",require:"ngModel",priority:200,compile:function(b,h){var d=k(h,b);return{pre:function(a,e,c,b){"checkbox"===d&&(b.$isEmpty=function(a){return!1===a})},post:function(f,e,b,n){function h(){return n.$modelValue}function k(a){e.attr("aria-checked",b.value==n.$viewValue)}function l(){e.attr("aria-checked",!n.$isEmpty(n.$viewValue))}var m=c("tabindex","tabindex",e,!1);switch(d){case "radio":case "checkbox":g(d,e)&&e.attr("role",
+d);c("aria-checked","ariaChecked",e,!1)&&f.$watch(h,"radio"===d?k:l);m&&e.attr("tabindex",0);break;case "range":g(d,e)&&e.attr("role","slider");if(a.config("ariaValue")){var p=!e.attr("aria-valuemin")&&(b.hasOwnProperty("min")||b.hasOwnProperty("ngMin")),r=!e.attr("aria-valuemax")&&(b.hasOwnProperty("max")||b.hasOwnProperty("ngMax")),s=!e.attr("aria-valuenow");p&&b.$observe("min",function(a){e.attr("aria-valuemin",a)});r&&b.$observe("max",function(a){e.attr("aria-valuemax",a)});s&&f.$watch(h,function(a){e.attr("aria-valuenow",
+a)})}m&&e.attr("tabindex",0)}!b.hasOwnProperty("ngRequired")&&n.$validators.required&&c("aria-required","ariaRequired",e,!1)&&b.$observe("required",function(){e.attr("aria-required",!!b.required)});c("aria-invalid","ariaInvalid",e,!0)&&f.$watch(function(){return n.$invalid},function(a){e.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",b,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",
+link:function(a,b,g,k){b.attr("aria-live")||b.attr("aria-live","assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(g,k){var m=c(k.ngClick,null,!0);return function(c,d,f){if(!l(d,b)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!f.ngKeypress))d.on("keypress",function(a){function b(){m(c,{$event:a})}var d=a.which||a.keyCode;32!==d&&
+13!==d||c.$apply(b)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,g,k){!a.config("tabindex")||g.attr("tabindex")||l(g,b)||g.attr("tabindex",0)}}])})(window,window.angular);
+//# sourceMappingURL=angular-aria.min.js.map
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.min.js.map b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.min.js.map
new file mode 100644
index 0000000000..925779bac7
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/angular-aria.min.js.map
@@ -0,0 +1,8 @@
+{
+"version":3,
+"file":"angular-aria.min.js",
+"lineCount":13,
+"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkB,CA8D3B,IAAIC,EAAgB,gDAAA,MAAA,CAAA,GAAA,CAApB,CAEIC,EAAcA,QAAQ,CAACC,CAAD,CAAOC,CAAP,CAAsB,CAC9C,GAAiD,EAAjD,GAAIA,CAAAC,QAAA,CAAsBF,CAAA,CAAK,CAAL,CAAAG,SAAtB,CAAJ,CACE,MAAO,CAAA,CAFqC,CAR7BN,EAAAO,OAAA,CAAe,QAAf,CAAyB,CAAC,IAAD,CAAzB,CAAAC,SAAAC,CACc,OADdA,CAkCnBC,QAAsB,EAAG,CAwCvBC,QAASA,EAAS,CAACC,CAAD,CAAWC,CAAX,CAAqBZ,CAArB,CAAoCa,CAApC,CAA4C,CAC5D,MAAO,SAAQ,CAACC,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoB,CACjC,IAAIC,EAAgBD,CAAAE,WAAA,CAAgBL,CAAhB,CAChB,EAAAM,CAAA,CAAOF,CAAP,CAAJ,EAA8Bf,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAA9B,EAAmEe,CAAA,CAAKC,CAAL,CAAnE,EACEF,CAAAK,OAAA,CAAaJ,CAAA,CAAKJ,CAAL,CAAb,CAA6B,QAAQ,CAACS,CAAD,CAAU,CAE7CA,CAAA,CAAUP,CAAA,CAAS,CAACO,CAAV,CAAoB,CAAEA,CAAAA,CAChClB,EAAAa,KAAA,CAAUH,CAAV,CAAoBQ,CAApB,CAH6C,CAA/C,CAH+B,CADyB,CAvC9D,IAAIF,EAAS,CACXG,WAAY,CAAA,CADD,CAEXC,YAAa,CAAA,CAFF,CAGXC,aAAc,CAAA,CAHH,CAIXC,aAAc,CAAA,CAJH,CAKXC,aAAc,CAAA,CALH,CAMXC,YAAa,CAAA,CANF,CAOXC,UAAW,CAAA,CAPA,CAQXC,SAAU,CAAA,CARC,CASXC,aAAc,CAAA,CATH,CAUXC,iBAAkB,CAAA,CAVP,CAmCb;IAAAZ,OAAA,CAAca,QAAQ,CAACC,CAAD,CAAY,CAChCd,CAAA,CAASnB,CAAAkC,OAAA,CAAef,CAAf,CAAuBc,CAAvB,CADuB,CAkElC,KAAAE,KAAA,CAAYC,QAAQ,EAAG,CACrB,MAAO,CACLjB,OAAQA,QAAQ,CAACkB,CAAD,CAAM,CACpB,MAAOlB,EAAA,CAAOkB,CAAP,CADa,CADjB,CAILC,YAAa3B,CAJR,CADc,CAtGA,CAlCNF,CAmJnB8B,UAAA,CAAuB,QAAvB,CAAiC,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACzD,MAAOA,EAAAF,YAAA,CAAkB,QAAlB,CAA4B,aAA5B,CAA2C,EAA3C,CAA+C,CAAA,CAA/C,CADkD,CAA1B,CAAjC,CAAAC,UAAA,CAGW,QAHX,CAGqB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAC7C,MAAOA,EAAAF,YAAA,CAAkB,QAAlB,CAA4B,aAA5B,CAA2C,EAA3C,CAA+C,CAAA,CAA/C,CADsC,CAA1B,CAHrB,CAAAC,UAAA,CAMW,SANX,CAMsB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAC9C,MAAOA,EAAAF,YAAA,CAAkB,SAAlB,CAA6B,cAA7B,CAA6CrC,CAA7C,CAA4D,CAAA,CAA5D,CADuC,CAA1B,CANtB,CAAAsC,UAAA,CASW,WATX,CASwB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAChD,MAAOA,EAAAF,YAAA,CAAkB,WAAlB,CAA+B,cAA/B,CAA+CrC,CAA/C,CAA8D,CAAA,CAA9D,CADyC,CAA1B,CATxB,CAAAsC,UAAA,CAYW,YAZX;AAYyB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACjD,MAAOA,EAAAF,YAAA,CAAkB,YAAlB,CAAgC,eAAhC,CAAiDrC,CAAjD,CAAgE,CAAA,CAAhE,CAD0C,CAA1B,CAZzB,CAAAsC,UAAA,CAeW,YAfX,CAeyB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACjD,MAAOA,EAAAF,YAAA,CAAkB,YAAlB,CAAgC,eAAhC,CAAiDrC,CAAjD,CAAgE,CAAA,CAAhE,CAD0C,CAA1B,CAfzB,CAAAsC,UAAA,CAkBW,SAlBX,CAkBsB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CAE9CC,QAASA,EAAgB,CAACzB,CAAD,CAAO0B,CAAP,CAAuBvC,CAAvB,CAA6BwC,CAA7B,CAAgD,CACvE,MAAOH,EAAArB,OAAA,CAAauB,CAAb,CAAP,EAAuC,CAACvC,CAAAa,KAAA,CAAUA,CAAV,CAAxC,GAA4D2B,CAA5D,EAAiF,CAACzC,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAAlF,CADuE,CAIzE2C,QAASA,EAAgB,CAACC,CAAD,CAAO1C,CAAP,CAAa,CAIpC,MAAO,CAACA,CAAAa,KAAA,CAAU,MAAV,CAAR,EAA8Bb,CAAAa,KAAA,CAAU,MAAV,CAA9B,GAAoD6B,CAApD,EAAmF,OAAnF,GAA8D1C,CAAA,CAAK,CAAL,CAAAG,SAJ1B,CAOtCwC,QAASA,EAAQ,CAAC9B,CAAD,CAAOb,CAAP,CAAa,CAAA,IACxB4C,EAAO/B,CAAA+B,KADiB,CAExBF,EAAO7B,CAAA6B,KAEX,OAA2B,UAApB,IAAEE,CAAF,EAAUF,CAAV,GAA2C,kBAA3C,GAAkCA,CAAlC,CAAiE,UAAjE,CACoB,OAApB,IAAEE,CAAF,EAAUF,CAAV,GAA2C,eAA3C;AAAkCA,CAAlC,CAA8D,OAA9D,CACU,OAAV,GAACE,CAAD,EAA2C,aAA3C,GAAkCF,CAAlC,EAAqE,QAArE,GAA4DA,CAA5D,CAAiF,OAAjF,CAA2F,EANtE,CAS9B,MAAO,CACLG,SAAU,GADL,CAELC,QAAS,SAFJ,CAGLC,SAAU,GAHL,CAILC,QAASA,QAAQ,CAAChD,CAAD,CAAOa,CAAP,CAAa,CAC5B,IAAIoC,EAAQN,CAAA,CAAS9B,CAAT,CAAeb,CAAf,CAEZ,OAAO,CACLkD,IAAKA,QAAQ,CAACtC,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoBsC,CAApB,CAA6B,CAC1B,UAAd,GAAIF,CAAJ,GAEEE,CAAAC,SAFF,CAEqBC,QAAQ,CAACC,CAAD,CAAQ,CACjC,MAAiB,CAAA,CAAjB,GAAOA,CAD0B,CAFrC,CADwC,CADrC,CASLC,KAAMA,QAAQ,CAAC3C,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoBsC,CAApB,CAA6B,CAGzCK,QAASA,EAAqB,EAAG,CAC/B,MAAOL,EAAAM,YADwB,CAIjCC,QAASA,EAAgB,CAACC,CAAD,CAAS,CAEhC3D,CAAAa,KAAA,CAAU,cAAV,CADeA,CAAAyC,MACf,EAD6BH,CAAAS,WAC7B,CAFgC,CAKlCC,QAASA,EAAmB,EAAG,CAC7B7D,CAAAa,KAAA,CAAU,cAAV,CAA0B,CAACsC,CAAAC,SAAA,CAAiBD,CAAAS,WAAjB,CAA3B,CAD6B,CAX/B,IAAIE,EAAgBxB,CAAA,CAAiB,UAAjB,CAA6B,UAA7B,CAAyCtC,CAAzC,CAA+C,CAAA,CAA/C,CAepB,QAAQiD,CAAR,EACE,KAAK,OAAL,CACA,KAAK,UAAL,CACMR,CAAA,CAAiBQ,CAAjB,CAAwBjD,CAAxB,CAAJ,EACEA,CAAAa,KAAA,CAAU,MAAV;AAAkBoC,CAAlB,CAEEX,EAAA,CAAiB,cAAjB,CAAiC,aAAjC,CAAgDtC,CAAhD,CAAsD,CAAA,CAAtD,CAAJ,EACEY,CAAAK,OAAA,CAAauC,CAAb,CAA8C,OAAV,GAAAP,CAAA,CAChCS,CADgC,CACbG,CADvB,CAGEC,EAAJ,EACE9D,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CAEF,MACF,MAAK,OAAL,CACM4B,CAAA,CAAiBQ,CAAjB,CAAwBjD,CAAxB,CAAJ,EACEA,CAAAa,KAAA,CAAU,MAAV,CAAkB,QAAlB,CAEF,IAAIwB,CAAArB,OAAA,CAAa,WAAb,CAAJ,CAA+B,CAC7B,IAAI+C,EAAoB,CAAC/D,CAAAa,KAAA,CAAU,eAAV,CAArBkD,GACClD,CAAAmD,eAAA,CAAoB,KAApB,CADDD,EAC+BlD,CAAAmD,eAAA,CAAoB,OAApB,CAD/BD,CAAJ,CAEIE,EAAoB,CAACjE,CAAAa,KAAA,CAAU,eAAV,CAArBoD,GACCpD,CAAAmD,eAAA,CAAoB,KAApB,CADDC,EAC+BpD,CAAAmD,eAAA,CAAoB,OAApB,CAD/BC,CAFJ,CAIIC,EAAoB,CAAClE,CAAAa,KAAA,CAAU,eAAV,CAErBkD,EAAJ,EACElD,CAAAsD,SAAA,CAAc,KAAd,CAAqBC,QAA+B,CAACT,CAAD,CAAS,CAC3D3D,CAAAa,KAAA,CAAU,eAAV,CAA2B8C,CAA3B,CAD2D,CAA7D,CAIEM,EAAJ,EACEpD,CAAAsD,SAAA,CAAc,KAAd,CAAqBC,QAA+B,CAACT,CAAD,CAAS,CAC3D3D,CAAAa,KAAA,CAAU,eAAV,CAA2B8C,CAA3B,CAD2D,CAA7D,CAIEO,EAAJ,EACEtD,CAAAK,OAAA,CAAauC,CAAb,CAAoCa,QAA+B,CAACV,CAAD,CAAS,CAC1E3D,CAAAa,KAAA,CAAU,eAAV;AAA2B8C,CAA3B,CAD0E,CAA5E,CAlB2B,CAuB3BG,CAAJ,EACE9D,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CA1CN,CA+CK,CAAAA,CAAAmD,eAAA,CAAoB,YAApB,CAAL,EAA0Cb,CAAAmB,YAAAC,SAA1C,EACKjC,CAAA,CAAiB,eAAjB,CAAkC,cAAlC,CAAkDtC,CAAlD,CAAwD,CAAA,CAAxD,CADL,EAGEa,CAAAsD,SAAA,CAAc,UAAd,CAA0B,QAAQ,EAAG,CACnCnE,CAAAa,KAAA,CAAU,eAAV,CAA2B,CAAE,CAAAA,CAAA,SAA7B,CADmC,CAArC,CAKEyB,EAAA,CAAiB,cAAjB,CAAiC,aAAjC,CAAgDtC,CAAhD,CAAsD,CAAA,CAAtD,CAAJ,EACEY,CAAAK,OAAA,CAAauD,QAA2B,EAAG,CACzC,MAAOrB,EAAAsB,SADkC,CAA3C,CAEGC,QAA8B,CAACf,CAAD,CAAS,CACxC3D,CAAAa,KAAA,CAAU,cAAV,CAA0B,CAAE8C,CAAAA,CAA5B,CADwC,CAF1C,CAxEuC,CATtC,CAHqB,CAJzB,CAtBuC,CAA1B,CAlBtB,CAAAvB,UAAA,CA2IW,YA3IX,CA2IyB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACjD,MAAOA,EAAAF,YAAA,CAAkB,YAAlB,CAAgC,eAAhC,CAAiDrC,CAAjD,CAAgE,CAAA,CAAhE,CAD0C,CAA1B,CA3IzB,CAAAsC,UAAA,CA8IW,YA9IX,CA8IyB,QAAQ,EAAG,CAClC,MAAO,CACLS,SAAU,GADL,CAELC,QAAS,aAFJ;AAGL6B,KAAMA,QAAQ,CAAC/D,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoB+D,CAApB,CAAgC,CACvC5E,CAAAa,KAAA,CAAU,WAAV,CAAL,EACEb,CAAAa,KAAA,CAAU,WAAV,CAAuB,WAAvB,CAF0C,CAHzC,CAD2B,CA9IpC,CAAAuB,UAAA,CAyJW,SAzJX,CAyJqB,CAAC,OAAD,CAAU,QAAV,CAAoB,QAAQ,CAACC,CAAD,CAAQwC,CAAR,CAAgB,CAC/D,MAAO,CACLhC,SAAU,GADL,CAELG,QAASA,QAAQ,CAAChD,CAAD,CAAOa,CAAP,CAAa,CAC5B,IAAIiE,EAAKD,CAAA,CAAOhE,CAAAkE,QAAP,CAAyC,IAAzC,CAAqE,CAAA,CAArE,CACT,OAAO,SAAQ,CAACnE,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoB,CAEjC,GAAK,CAAAd,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAAL,GAEMuC,CAAArB,OAAA,CAAa,kBAAb,CAQA,EARqC,CAAAhB,CAAAa,KAAA,CAAU,MAAV,CAQrC,EAPFb,CAAAa,KAAA,CAAU,MAAV,CAAkB,QAAlB,CAOE,CAJAwB,CAAArB,OAAA,CAAa,UAAb,CAIA,EAJ6B,CAAAhB,CAAAa,KAAA,CAAU,UAAV,CAI7B,EAHFb,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CAGE,CAAAwB,CAAArB,OAAA,CAAa,cAAb,CAAA,EAAiCgE,CAAAnE,CAAAmE,WAVvC,EAWIhF,CAAAiF,GAAA,CAAQ,UAAR,CAAoB,QAAQ,CAACC,CAAD,CAAQ,CAMlCC,QAASA,EAAQ,EAAG,CAClBL,CAAA,CAAGlE,CAAH,CAAU,CAAEwE,OAAQF,CAAV,CAAV,CADkB,CALpB,IAAIG,EAAUH,CAAAI,MAAVD,EAAyBH,CAAAG,QACb,GAAhB,GAAIA,CAAJ;AAAkC,EAAlC,GAAsBA,CAAtB,EACEzE,CAAA2E,OAAA,CAAaJ,CAAb,CAHgC,CAApC,CAb6B,CAFP,CAFzB,CADwD,CAA5C,CAzJrB,CAAA/C,UAAA,CA2LW,YA3LX,CA2LyB,CAAC,OAAD,CAAU,QAAQ,CAACC,CAAD,CAAQ,CACjD,MAAO,SAAQ,CAACzB,CAAD,CAAQZ,CAAR,CAAca,CAAd,CAAoB,CAC7B,CAAAwB,CAAArB,OAAA,CAAa,UAAb,CAAJ,EAAiChB,CAAAa,KAAA,CAAU,UAAV,CAAjC,EAA2Dd,CAAA,CAAYC,CAAZ,CAAkBF,CAAlB,CAA3D,EACEE,CAAAa,KAAA,CAAU,UAAV,CAAsB,CAAtB,CAF+B,CADc,CAA1B,CA3LzB,CA3M2B,CAA1B,CAAD,CA+YGjB,MA/YH,CA+YWA,MAAAC,QA/YX;",
+"sources":["angular-aria.js"],
+"names":["window","angular","nodeBlackList","isNodeOneOf","elem","nodeTypeArray","indexOf","nodeName","module","provider","ngAriaModule","$AriaProvider","watchExpr","attrName","ariaAttr","negate","scope","attr","ariaCamelName","$normalize","config","$watch","boolVal","ariaHidden","ariaChecked","ariaReadonly","ariaDisabled","ariaRequired","ariaInvalid","ariaValue","tabindex","bindKeypress","bindRoleForClick","this.config","newConfig","extend","$get","this.$get","key","$$watchExpr","directive","$aria","shouldAttachAttr","normalizedAttr","allowBlacklistEls","shouldAttachRole","role","getShape","type","restrict","require","priority","compile","shape","pre","ngModel","$isEmpty","ngModel.$isEmpty","value","post","ngAriaWatchModelValue","$modelValue","getRadioReaction","newVal","$viewValue","getCheckboxReaction","needsTabIndex","needsAriaValuemin","hasOwnProperty","needsAriaValuemax","needsAriaValuenow","$observe","ngAriaValueMinReaction","ngAriaValueNowReaction","$validators","required","ngAriaInvalidWatch","$invalid","ngAriaInvalidReaction","link","ngMessages","$parse","fn","ngClick","ngKeypress","on","event","callback","$event","keyCode","which","$apply"]
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/bower.json
new file mode 100644
index 0000000000..b3414d55fb
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/bower.json
@@ -0,0 +1,10 @@
+{
+ "name": "angular-aria",
+ "version": "1.5.8",
+ "license": "MIT",
+ "main": "./angular-aria.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.5.8"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/index.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/index.js
new file mode 100644
index 0000000000..0a8f0d9b05
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/index.js
@@ -0,0 +1,2 @@
+require('./angular-aria');
+module.exports = 'ngAria';
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/package.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/package.json
new file mode 100644
index 0000000000..476dda58ae
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-aria/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "angular-aria",
+ "version": "1.5.8",
+ "description": "AngularJS module for making accessibility easy",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/angular/angular.js.git"
+ },
+ "keywords": [
+ "angular",
+ "framework",
+ "browser",
+ "accessibility",
+ "a11y",
+ "client-side"
+ ],
+ "author": "Angular Core Team ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/angular/angular.js/issues"
+ },
+ "homepage": "http://angularjs.org",
+ "jspm": {
+ "shim": {
+ "angular-aria": {
+ "deps": ["angular"]
+ }
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.bower.json
new file mode 100644
index 0000000000..3573845cf6
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.bower.json
@@ -0,0 +1,31 @@
+{
+ "author": {
+ "name": "https://github.com/angular-ui/bootstrap/graphs/contributors"
+ },
+ "name": "angular-bootstrap",
+ "keywords": [
+ "angular",
+ "angular-ui",
+ "bootstrap"
+ ],
+ "license": "MIT",
+ "ignore": [],
+ "description": "Native AngularJS (Angular) directives for Bootstrap.",
+ "version": "1.3.3",
+ "main": [
+ "./ui-bootstrap-tpls.js"
+ ],
+ "dependencies": {
+ "angular": ">=1.4.0"
+ },
+ "homepage": "https://github.com/angular-ui/bootstrap-bower",
+ "_release": "1.3.3",
+ "_resolution": {
+ "type": "version",
+ "tag": "1.3.3",
+ "commit": "d45246707f5bf9533e3824861a29abd36757db45"
+ },
+ "_source": "https://github.com/angular-ui/bootstrap-bower.git",
+ "_target": "1.3.3",
+ "_originalSource": "angular-bootstrap"
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.gitignore b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.gitignore
new file mode 100644
index 0000000000..496ee2ca6a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.gitignore
@@ -0,0 +1 @@
+.DS_Store
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.npmignore b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.npmignore
new file mode 100644
index 0000000000..d62f9b6e0f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/.npmignore
@@ -0,0 +1 @@
+bower.json
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/README.md
new file mode 100644
index 0000000000..9607c65f68
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/README.md
@@ -0,0 +1,120 @@
+### UI Bootstrap - [AngularJS](http://angularjs.org/) directives specific to [Bootstrap](http://getbootstrap.com)
+
+[](https://gitter.im/angular-ui/bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](http://travis-ci.org/angular-ui/bootstrap)
+[](https://david-dm.org/angular-ui/bootstrap#info=devDependencies)
+
+### Quick links
+- [Demo](#demo)
+- [Installation](#installation)
+ - [NPM](#install-with-npm)
+ - [Bower](#install-with-bower)
+ - [NuGet](#install-with-nuget)
+ - [Custom](#custom-build)
+ - [Manual](#manual-download)
+- [Support](#support)
+ - [FAQ](#faq)
+ - [Supported browsers](#supported-browsers)
+ - [Need help?](#need-help)
+ - [Found a bug?](#found-a-bug)
+- [Contributing to the project](#contributing-to-the-project)
+- [Development, meeting minutes, roadmap and more.](#development-meeting-minutes-roadmap-and-more)
+
+
+# Demo
+
+Do you want to see directives in action? Visit http://angular-ui.github.io/bootstrap/!
+
+# Installation
+
+Installation is easy as UI Bootstrap has minimal dependencies - only the AngularJS and Twitter Bootstrap's CSS are required.
+Note: Since version 0.13.0, UI Bootstrap depends on [ngAnimate](https://docs.angularjs.org/api/ngAnimate) for transitions and animations, such as the accordion, carousel, etc. Include `ngAnimate` in the module dependencies for your app in order to enable animation.
+
+#### Install with NPM
+
+```sh
+$ npm install angular-ui-bootstrap
+```
+
+This will install AngularJS and Bootstrap NPM packages.
+
+#### Install with Bower
+```sh
+$ bower install angular-bootstrap
+```
+
+Note: do not install 'angular-ui-bootstrap'. A separate repository - [bootstrap-bower](https://github.com/angular-ui/bootstrap-bower) - hosts the compiled javascript file and bower.json.
+
+#### Install with NuGet
+To install AngularJS UI Bootstrap, run the following command in the Package Manager Console
+
+```sh
+PM> Install-Package Angular.UI.Bootstrap
+```
+
+#### Custom build
+
+Head over to http://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it.
+
+#### Manual download
+
+After downloading dependencies (or better yet, referencing them from your favorite CDN) you need to download build version of this project. All the files and their purposes are described here:
+https://github.com/angular-ui/bootstrap/tree/gh-pages#build-files
+Don't worry, if you are not sure which file to take, opt for `ui-bootstrap-tpls-[version].min.js`.
+
+### Adding dependency to your project
+
+When you are done downloading all the dependencies and project files the only remaining part is to add dependencies on the `ui.bootstrap` AngularJS module:
+
+```js
+angular.module('myModule', ['ui.bootstrap']);
+```
+
+If you're a Browserify or Webpack user, you can do:
+
+```js
+var uibs = require('angular-ui-bootstrap');
+
+angular.module('myModule', [uibs]);
+```
+
+# Support
+
+## FAQ
+
+https://github.com/angular-ui/bootstrap/wiki/FAQ
+
+## Supported browsers
+
+Directives from this repository are automatically tested with the following browsers:
+* Chrome (stable and canary channel)
+* Firefox
+* IE 9 and 10
+* Opera
+* Safari
+
+Modern mobile browsers should work without problems.
+
+
+## Need help?
+Need help using UI Bootstrap?
+
+* Live help in the IRC (`#angularjs` channel at the `freenode` network). Use this [webchat](https://webchat.freenode.net/) or your own IRC client.
+* Ask a question in [StackOverflow](http://stackoverflow.com/) under the [angular-ui-bootstrap](http://stackoverflow.com/questions/tagged/angular-ui-bootstrap) tag.
+
+**Please do not create new issues in this repository to ask questions about using UI Bootstrap**
+
+## Found a bug?
+Please take a look at [CONTRIBUTING.md](CONTRIBUTING.md#you-think-youve-found-a-bug) and submit your issue [here](https://github.com/angular-ui/bootstrap/issues/new).
+
+
+----
+
+
+# Contributing to the project
+
+We are always looking for the quality contributions! Please check the [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guidelines.
+
+# Development, meeting minutes, roadmap and more.
+
+Head over to the [Wiki](https://github.com/angular-ui/bootstrap/wiki) for notes on development for UI Bootstrap, meeting minutes from the UI Bootstrap team, roadmap plans, project philosophy and more.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/bower.json
new file mode 100644
index 0000000000..8ee7fd9f1c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/bower.json
@@ -0,0 +1,19 @@
+{
+ "author": {
+ "name": "https://github.com/angular-ui/bootstrap/graphs/contributors"
+ },
+ "name": "angular-bootstrap",
+ "keywords": [
+ "angular",
+ "angular-ui",
+ "bootstrap"
+ ],
+ "license": "MIT",
+ "ignore": [],
+ "description": "Native AngularJS (Angular) directives for Bootstrap.",
+ "version": "1.3.3",
+ "main": ["./ui-bootstrap-tpls.js"],
+ "dependencies": {
+ "angular": ">=1.4.0"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/index.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/index.js
new file mode 100644
index 0000000000..a174f26d42
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/index.js
@@ -0,0 +1,2 @@
+require('./ui-bootstrap-tpls');
+module.exports = 'ui.bootstrap';
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/package.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/package.json
new file mode 100644
index 0000000000..4efc7e8ff9
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "angular-ui-bootstrap",
+ "version": "1.3.3",
+ "description": "Bootstrap widgets for Angular",
+ "main": "index.js",
+ "homepage": "http://angular-ui.github.io/bootstrap/",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/angular-ui/bootstrap.git"
+ },
+ "keywords": [
+ "angular",
+ "bootstrap",
+ "angular-ui",
+ "components",
+ "client-side"
+ ],
+ "author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
+ "peerDependencies": {
+ "angular": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0"
+ },
+ "license": "MIT"
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap-csp.css b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap-csp.css
new file mode 100644
index 0000000000..3b69cf6474
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap-csp.css
@@ -0,0 +1,115 @@
+/* Include this file in your html if you are using the CSP mode. */
+
+.ng-animate.item:not(.left):not(.right) {
+ -webkit-transition: 0s ease-in-out left;
+ transition: 0s ease-in-out left
+}
+.uib-datepicker .uib-title {
+ width: 100%;
+}
+
+.uib-day button, .uib-month button, .uib-year button {
+ min-width: 100%;
+}
+
+.uib-left, .uib-right {
+ width: 100%
+}
+
+.uib-position-measure {
+ display: block !important;
+ visibility: hidden !important;
+ position: absolute !important;
+ top: -9999px !important;
+ left: -9999px !important;
+}
+
+.uib-position-scrollbar-measure {
+ position: absolute !important;
+ top: -9999px !important;
+ width: 50px !important;
+ height: 50px !important;
+ overflow: scroll !important;
+}
+
+.uib-position-body-scrollbar-measure {
+ overflow: scroll !important;
+}
+.uib-datepicker-popup.dropdown-menu {
+ display: block;
+ float: none;
+ margin: 0;
+}
+
+.uib-button-bar {
+ padding: 10px 9px 2px;
+}
+
+[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,
+[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,
+[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,
+[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,
+[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,
+[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,
+[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,
+[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,
+[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,
+[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,
+[uib-popover-popup].popover.top-left > .arrow,
+[uib-popover-popup].popover.top-right > .arrow,
+[uib-popover-popup].popover.bottom-left > .arrow,
+[uib-popover-popup].popover.bottom-right > .arrow,
+[uib-popover-popup].popover.left-top > .arrow,
+[uib-popover-popup].popover.left-bottom > .arrow,
+[uib-popover-popup].popover.right-top > .arrow,
+[uib-popover-popup].popover.right-bottom > .arrow,
+[uib-popover-html-popup].popover.top-left > .arrow,
+[uib-popover-html-popup].popover.top-right > .arrow,
+[uib-popover-html-popup].popover.bottom-left > .arrow,
+[uib-popover-html-popup].popover.bottom-right > .arrow,
+[uib-popover-html-popup].popover.left-top > .arrow,
+[uib-popover-html-popup].popover.left-bottom > .arrow,
+[uib-popover-html-popup].popover.right-top > .arrow,
+[uib-popover-html-popup].popover.right-bottom > .arrow,
+[uib-popover-template-popup].popover.top-left > .arrow,
+[uib-popover-template-popup].popover.top-right > .arrow,
+[uib-popover-template-popup].popover.bottom-left > .arrow,
+[uib-popover-template-popup].popover.bottom-right > .arrow,
+[uib-popover-template-popup].popover.left-top > .arrow,
+[uib-popover-template-popup].popover.left-bottom > .arrow,
+[uib-popover-template-popup].popover.right-top > .arrow,
+[uib-popover-template-popup].popover.right-bottom > .arrow {
+ top: auto;
+ bottom: auto;
+ left: auto;
+ right: auto;
+ margin: 0;
+}
+
+[uib-popover-popup].popover,
+[uib-popover-html-popup].popover,
+[uib-popover-template-popup].popover {
+ display: block !important;
+}
+
+.uib-time input {
+ width: 50px;
+}
+
+[uib-typeahead-popup].dropdown-menu {
+ display: block;
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap-tpls.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap-tpls.js
new file mode 100644
index 0000000000..918ef43632
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap-tpls.js
@@ -0,0 +1,7347 @@
+/*
+ * angular-ui-bootstrap
+ * http://angular-ui.github.io/bootstrap/
+
+ * Version: 1.3.3 - 2016-05-22
+ * License: MIT
+ */angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
+angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/backdrop.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]);
+angular.module('ui.bootstrap.collapse', [])
+
+ .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) {
+ var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
+ return {
+ link: function(scope, element, attrs) {
+ var expandingExpr = $parse(attrs.expanding),
+ expandedExpr = $parse(attrs.expanded),
+ collapsingExpr = $parse(attrs.collapsing),
+ collapsedExpr = $parse(attrs.collapsed);
+
+ if (!scope.$eval(attrs.uibCollapse)) {
+ element.addClass('in')
+ .addClass('collapse')
+ .attr('aria-expanded', true)
+ .attr('aria-hidden', false)
+ .css({height: 'auto'});
+ }
+
+ function expand() {
+ if (element.hasClass('collapse') && element.hasClass('in')) {
+ return;
+ }
+
+ $q.resolve(expandingExpr(scope))
+ .then(function() {
+ element.removeClass('collapse')
+ .addClass('collapsing')
+ .attr('aria-expanded', true)
+ .attr('aria-hidden', false);
+
+ if ($animateCss) {
+ $animateCss(element, {
+ addClass: 'in',
+ easing: 'ease',
+ to: { height: element[0].scrollHeight + 'px' }
+ }).start()['finally'](expandDone);
+ } else {
+ $animate.addClass(element, 'in', {
+ to: { height: element[0].scrollHeight + 'px' }
+ }).then(expandDone);
+ }
+ });
+ }
+
+ function expandDone() {
+ element.removeClass('collapsing')
+ .addClass('collapse')
+ .css({height: 'auto'});
+ expandedExpr(scope);
+ }
+
+ function collapse() {
+ if (!element.hasClass('collapse') && !element.hasClass('in')) {
+ return collapseDone();
+ }
+
+ $q.resolve(collapsingExpr(scope))
+ .then(function() {
+ element
+ // IMPORTANT: The height must be set before adding "collapsing" class.
+ // Otherwise, the browser attempts to animate from height 0 (in
+ // collapsing class) to the given height here.
+ .css({height: element[0].scrollHeight + 'px'})
+ // initially all panel collapse have the collapse class, this removal
+ // prevents the animation from jumping to collapsed state
+ .removeClass('collapse')
+ .addClass('collapsing')
+ .attr('aria-expanded', false)
+ .attr('aria-hidden', true);
+
+ if ($animateCss) {
+ $animateCss(element, {
+ removeClass: 'in',
+ to: {height: '0'}
+ }).start()['finally'](collapseDone);
+ } else {
+ $animate.removeClass(element, 'in', {
+ to: {height: '0'}
+ }).then(collapseDone);
+ }
+ });
+ }
+
+ function collapseDone() {
+ element.css({height: '0'}); // Required so that collapse works when animation is disabled
+ element.removeClass('collapsing')
+ .addClass('collapse');
+ collapsedExpr(scope);
+ }
+
+ scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
+ if (shouldCollapse) {
+ collapse();
+ } else {
+ expand();
+ }
+ });
+ }
+ };
+ }]);
+
+angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
+
+.constant('uibAccordionConfig', {
+ closeOthers: true
+})
+
+.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
+ // This array keeps track of the accordion groups
+ this.groups = [];
+
+ // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
+ this.closeOthers = function(openGroup) {
+ var closeOthers = angular.isDefined($attrs.closeOthers) ?
+ $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
+ if (closeOthers) {
+ angular.forEach(this.groups, function(group) {
+ if (group !== openGroup) {
+ group.isOpen = false;
+ }
+ });
+ }
+ };
+
+ // This is called from the accordion-group directive to add itself to the accordion
+ this.addGroup = function(groupScope) {
+ var that = this;
+ this.groups.push(groupScope);
+
+ groupScope.$on('$destroy', function(event) {
+ that.removeGroup(groupScope);
+ });
+ };
+
+ // This is called from the accordion-group directive when to remove itself
+ this.removeGroup = function(group) {
+ var index = this.groups.indexOf(group);
+ if (index !== -1) {
+ this.groups.splice(index, 1);
+ }
+ };
+}])
+
+// The accordion directive simply sets up the directive controller
+// and adds an accordion CSS class to itself element.
+.directive('uibAccordion', function() {
+ return {
+ controller: 'UibAccordionController',
+ controllerAs: 'accordion',
+ transclude: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/accordion/accordion.html';
+ }
+ };
+})
+
+// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
+.directive('uibAccordionGroup', function() {
+ return {
+ require: '^uibAccordion', // We need this directive to be inside an accordion
+ transclude: true, // It transcludes the contents of the directive into the template
+ replace: true, // The element containing the directive will be replaced with the template
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
+ },
+ scope: {
+ heading: '@', // Interpolate the heading attribute onto this scope
+ panelClass: '@?', // Ditto with panelClass
+ isOpen: '=?',
+ isDisabled: '=?'
+ },
+ controller: function() {
+ this.setHeading = function(element) {
+ this.heading = element;
+ };
+ },
+ link: function(scope, element, attrs, accordionCtrl) {
+ accordionCtrl.addGroup(scope);
+
+ scope.openClass = attrs.openClass || 'panel-open';
+ scope.panelClass = attrs.panelClass || 'panel-default';
+ scope.$watch('isOpen', function(value) {
+ element.toggleClass(scope.openClass, !!value);
+ if (value) {
+ accordionCtrl.closeOthers(scope);
+ }
+ });
+
+ scope.toggleOpen = function($event) {
+ if (!scope.isDisabled) {
+ if (!$event || $event.which === 32) {
+ scope.isOpen = !scope.isOpen;
+ }
+ }
+ };
+
+ var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
+ scope.headingId = id + '-tab';
+ scope.panelId = id + '-panel';
+ }
+ };
+})
+
+// Use accordion-heading below an accordion-group to provide a heading containing HTML
+.directive('uibAccordionHeading', function() {
+ return {
+ transclude: true, // Grab the contents to be used as the heading
+ template: '', // In effect remove this element!
+ replace: true,
+ require: '^uibAccordionGroup',
+ link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
+ // Pass the heading to the accordion-group controller
+ // so that it can be transcluded into the right place in the template
+ // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
+ accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
+ }
+ };
+})
+
+// Use in the accordion-group template to indicate where you want the heading to be transcluded
+// You must provide the property on the accordion-group controller that will hold the transcluded element
+.directive('uibAccordionTransclude', function() {
+ return {
+ require: '^uibAccordionGroup',
+ link: function(scope, element, attrs, controller) {
+ scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
+ if (heading) {
+ var elem = angular.element(element[0].querySelector(getHeaderSelectors()));
+ elem.html('');
+ elem.append(heading);
+ }
+ });
+ }
+ };
+
+ function getHeaderSelectors() {
+ return 'uib-accordion-header,' +
+ 'data-uib-accordion-header,' +
+ 'x-uib-accordion-header,' +
+ 'uib\\:accordion-header,' +
+ '[uib-accordion-header],' +
+ '[data-uib-accordion-header],' +
+ '[x-uib-accordion-header]';
+ }
+});
+
+angular.module('ui.bootstrap.alert', [])
+
+.controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
+ $scope.closeable = !!$attrs.close;
+
+ var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
+ $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
+
+ if (dismissOnTimeout) {
+ $timeout(function() {
+ $scope.close();
+ }, parseInt(dismissOnTimeout, 10));
+ }
+}])
+
+.directive('uibAlert', function() {
+ return {
+ controller: 'UibAlertController',
+ controllerAs: 'alert',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/alert/alert.html';
+ },
+ transclude: true,
+ replace: true,
+ scope: {
+ type: '@',
+ close: '&'
+ }
+ };
+});
+
+angular.module('ui.bootstrap.buttons', [])
+
+.constant('uibButtonConfig', {
+ activeClass: 'active',
+ toggleEvent: 'click'
+})
+
+.controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
+ this.activeClass = buttonConfig.activeClass || 'active';
+ this.toggleEvent = buttonConfig.toggleEvent || 'click';
+}])
+
+.directive('uibBtnRadio', ['$parse', function($parse) {
+ return {
+ require: ['uibBtnRadio', 'ngModel'],
+ controller: 'UibButtonsController',
+ controllerAs: 'buttons',
+ link: function(scope, element, attrs, ctrls) {
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+ var uncheckableExpr = $parse(attrs.uibUncheckable);
+
+ element.find('input').css({display: 'none'});
+
+ //model -> UI
+ ngModelCtrl.$render = function() {
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
+ };
+
+ //ui->model
+ element.on(buttonsCtrl.toggleEvent, function() {
+ if (attrs.disabled) {
+ return;
+ }
+
+ var isActive = element.hasClass(buttonsCtrl.activeClass);
+
+ if (!isActive || angular.isDefined(attrs.uncheckable)) {
+ scope.$apply(function() {
+ ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
+ ngModelCtrl.$render();
+ });
+ }
+ });
+
+ if (attrs.uibUncheckable) {
+ scope.$watch(uncheckableExpr, function(uncheckable) {
+ attrs.$set('uncheckable', uncheckable ? '' : undefined);
+ });
+ }
+ }
+ };
+}])
+
+.directive('uibBtnCheckbox', function() {
+ return {
+ require: ['uibBtnCheckbox', 'ngModel'],
+ controller: 'UibButtonsController',
+ controllerAs: 'button',
+ link: function(scope, element, attrs, ctrls) {
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ element.find('input').css({display: 'none'});
+
+ function getTrueValue() {
+ return getCheckboxValue(attrs.btnCheckboxTrue, true);
+ }
+
+ function getFalseValue() {
+ return getCheckboxValue(attrs.btnCheckboxFalse, false);
+ }
+
+ function getCheckboxValue(attribute, defaultValue) {
+ return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
+ }
+
+ //model -> UI
+ ngModelCtrl.$render = function() {
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
+ };
+
+ //ui->model
+ element.on(buttonsCtrl.toggleEvent, function() {
+ if (attrs.disabled) {
+ return;
+ }
+
+ scope.$apply(function() {
+ ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
+ ngModelCtrl.$render();
+ });
+ });
+ }
+ };
+});
+
+angular.module('ui.bootstrap.carousel', [])
+
+.controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
+ var self = this,
+ slides = self.slides = $scope.slides = [],
+ SLIDE_DIRECTION = 'uib-slideDirection',
+ currentIndex = $scope.active,
+ currentInterval, isPlaying, bufferedTransitions = [];
+
+ var destroyed = false;
+
+ self.addSlide = function(slide, element) {
+ slides.push({
+ slide: slide,
+ element: element
+ });
+ slides.sort(function(a, b) {
+ return +a.slide.index - +b.slide.index;
+ });
+ //if this is the first slide or the slide is set to active, select it
+ if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) {
+ if ($scope.$currentTransition) {
+ $scope.$currentTransition = null;
+ }
+
+ currentIndex = slide.index;
+ $scope.active = slide.index;
+ setActive(currentIndex);
+ self.select(slides[findSlideIndex(slide)]);
+ if (slides.length === 1) {
+ $scope.play();
+ }
+ }
+ };
+
+ self.getCurrentIndex = function() {
+ for (var i = 0; i < slides.length; i++) {
+ if (slides[i].slide.index === currentIndex) {
+ return i;
+ }
+ }
+ };
+
+ self.next = $scope.next = function() {
+ var newIndex = (self.getCurrentIndex() + 1) % slides.length;
+
+ if (newIndex === 0 && $scope.noWrap()) {
+ $scope.pause();
+ return;
+ }
+
+ return self.select(slides[newIndex], 'next');
+ };
+
+ self.prev = $scope.prev = function() {
+ var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
+
+ if ($scope.noWrap() && newIndex === slides.length - 1) {
+ $scope.pause();
+ return;
+ }
+
+ return self.select(slides[newIndex], 'prev');
+ };
+
+ self.removeSlide = function(slide) {
+ var index = findSlideIndex(slide);
+
+ var bufferedIndex = bufferedTransitions.indexOf(slides[index]);
+ if (bufferedIndex !== -1) {
+ bufferedTransitions.splice(bufferedIndex, 1);
+ }
+
+ //get the index of the slide inside the carousel
+ slides.splice(index, 1);
+ if (slides.length > 0 && currentIndex === index) {
+ if (index >= slides.length) {
+ currentIndex = slides.length - 1;
+ $scope.active = currentIndex;
+ setActive(currentIndex);
+ self.select(slides[slides.length - 1]);
+ } else {
+ currentIndex = index;
+ $scope.active = currentIndex;
+ setActive(currentIndex);
+ self.select(slides[index]);
+ }
+ } else if (currentIndex > index) {
+ currentIndex--;
+ $scope.active = currentIndex;
+ }
+
+ //clean the active value when no more slide
+ if (slides.length === 0) {
+ currentIndex = null;
+ $scope.active = null;
+ clearBufferedTransitions();
+ }
+ };
+
+ /* direction: "prev" or "next" */
+ self.select = $scope.select = function(nextSlide, direction) {
+ var nextIndex = findSlideIndex(nextSlide.slide);
+ //Decide direction if it's not given
+ if (direction === undefined) {
+ direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
+ }
+ //Prevent this user-triggered transition from occurring if there is already one in progress
+ if (nextSlide.slide.index !== currentIndex &&
+ !$scope.$currentTransition) {
+ goNext(nextSlide.slide, nextIndex, direction);
+ } else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) {
+ bufferedTransitions.push(slides[nextIndex]);
+ }
+ };
+
+ /* Allow outside people to call indexOf on slides array */
+ $scope.indexOfSlide = function(slide) {
+ return +slide.slide.index;
+ };
+
+ $scope.isActive = function(slide) {
+ return $scope.active === slide.slide.index;
+ };
+
+ $scope.isPrevDisabled = function() {
+ return $scope.active === 0 && $scope.noWrap();
+ };
+
+ $scope.isNextDisabled = function() {
+ return $scope.active === slides.length - 1 && $scope.noWrap();
+ };
+
+ $scope.pause = function() {
+ if (!$scope.noPause) {
+ isPlaying = false;
+ resetTimer();
+ }
+ };
+
+ $scope.play = function() {
+ if (!isPlaying) {
+ isPlaying = true;
+ restartTimer();
+ }
+ };
+
+ $scope.$on('$destroy', function() {
+ destroyed = true;
+ resetTimer();
+ });
+
+ $scope.$watch('noTransition', function(noTransition) {
+ $animate.enabled($element, !noTransition);
+ });
+
+ $scope.$watch('interval', restartTimer);
+
+ $scope.$watchCollection('slides', resetTransition);
+
+ $scope.$watch('active', function(index) {
+ if (angular.isNumber(index) && currentIndex !== index) {
+ for (var i = 0; i < slides.length; i++) {
+ if (slides[i].slide.index === index) {
+ index = i;
+ break;
+ }
+ }
+
+ var slide = slides[index];
+ if (slide) {
+ setActive(index);
+ self.select(slides[index]);
+ currentIndex = index;
+ }
+ }
+ });
+
+ function clearBufferedTransitions() {
+ while (bufferedTransitions.length) {
+ bufferedTransitions.shift();
+ }
+ }
+
+ function getSlideByIndex(index) {
+ for (var i = 0, l = slides.length; i < l; ++i) {
+ if (slides[i].index === index) {
+ return slides[i];
+ }
+ }
+ }
+
+ function setActive(index) {
+ for (var i = 0; i < slides.length; i++) {
+ slides[i].slide.active = i === index;
+ }
+ }
+
+ function goNext(slide, index, direction) {
+ if (destroyed) {
+ return;
+ }
+
+ angular.extend(slide, {direction: direction});
+ angular.extend(slides[currentIndex].slide || {}, {direction: direction});
+ if ($animate.enabled($element) && !$scope.$currentTransition &&
+ slides[index].element && self.slides.length > 1) {
+ slides[index].element.data(SLIDE_DIRECTION, slide.direction);
+ var currentIdx = self.getCurrentIndex();
+
+ if (angular.isNumber(currentIdx) && slides[currentIdx].element) {
+ slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction);
+ }
+
+ $scope.$currentTransition = true;
+ $animate.on('addClass', slides[index].element, function(element, phase) {
+ if (phase === 'close') {
+ $scope.$currentTransition = null;
+ $animate.off('addClass', element);
+ if (bufferedTransitions.length) {
+ var nextSlide = bufferedTransitions.pop().slide;
+ var nextIndex = nextSlide.index;
+ var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
+ clearBufferedTransitions();
+
+ goNext(nextSlide, nextIndex, nextDirection);
+ }
+ }
+ });
+ }
+
+ $scope.active = slide.index;
+ currentIndex = slide.index;
+ setActive(index);
+
+ //every time you change slides, reset the timer
+ restartTimer();
+ }
+
+ function findSlideIndex(slide) {
+ for (var i = 0; i < slides.length; i++) {
+ if (slides[i].slide === slide) {
+ return i;
+ }
+ }
+ }
+
+ function resetTimer() {
+ if (currentInterval) {
+ $interval.cancel(currentInterval);
+ currentInterval = null;
+ }
+ }
+
+ function resetTransition(slides) {
+ if (!slides.length) {
+ $scope.$currentTransition = null;
+ clearBufferedTransitions();
+ }
+ }
+
+ function restartTimer() {
+ resetTimer();
+ var interval = +$scope.interval;
+ if (!isNaN(interval) && interval > 0) {
+ currentInterval = $interval(timerFn, interval);
+ }
+ }
+
+ function timerFn() {
+ var interval = +$scope.interval;
+ if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
+ $scope.next();
+ } else {
+ $scope.pause();
+ }
+ }
+}])
+
+.directive('uibCarousel', function() {
+ return {
+ transclude: true,
+ replace: true,
+ controller: 'UibCarouselController',
+ controllerAs: 'carousel',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/carousel/carousel.html';
+ },
+ scope: {
+ active: '=',
+ interval: '=',
+ noTransition: '=',
+ noPause: '=',
+ noWrap: '&'
+ }
+ };
+})
+
+.directive('uibSlide', function() {
+ return {
+ require: '^uibCarousel',
+ transclude: true,
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/carousel/slide.html';
+ },
+ scope: {
+ actual: '=?',
+ index: '=?'
+ },
+ link: function (scope, element, attrs, carouselCtrl) {
+ carouselCtrl.addSlide(scope, element);
+ //when the scope is destroyed then remove the slide from the current slides array
+ scope.$on('$destroy', function() {
+ carouselCtrl.removeSlide(scope);
+ });
+ }
+ };
+})
+
+.animation('.item', ['$animateCss',
+function($animateCss) {
+ var SLIDE_DIRECTION = 'uib-slideDirection';
+
+ function removeClass(element, className, callback) {
+ element.removeClass(className);
+ if (callback) {
+ callback();
+ }
+ }
+
+ return {
+ beforeAddClass: function(element, className, done) {
+ if (className === 'active') {
+ var stopped = false;
+ var direction = element.data(SLIDE_DIRECTION);
+ var directionClass = direction === 'next' ? 'left' : 'right';
+ var removeClassFn = removeClass.bind(this, element,
+ directionClass + ' ' + direction, done);
+ element.addClass(direction);
+
+ $animateCss(element, {addClass: directionClass})
+ .start()
+ .done(removeClassFn);
+
+ return function() {
+ stopped = true;
+ };
+ }
+ done();
+ },
+ beforeRemoveClass: function (element, className, done) {
+ if (className === 'active') {
+ var stopped = false;
+ var direction = element.data(SLIDE_DIRECTION);
+ var directionClass = direction === 'next' ? 'left' : 'right';
+ var removeClassFn = removeClass.bind(this, element, directionClass, done);
+
+ $animateCss(element, {addClass: directionClass})
+ .start()
+ .done(removeClassFn);
+
+ return function() {
+ stopped = true;
+ };
+ }
+ done();
+ }
+ };
+}]);
+
+angular.module('ui.bootstrap.dateparser', [])
+
+.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) {
+ // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
+ var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
+
+ var localeId;
+ var formatCodeToRegex;
+
+ this.init = function() {
+ localeId = $locale.id;
+
+ this.parsers = {};
+ this.formatters = {};
+
+ formatCodeToRegex = [
+ {
+ key: 'yyyy',
+ regex: '\\d{4}',
+ apply: function(value) { this.year = +value; },
+ formatter: function(date) {
+ var _date = new Date();
+ _date.setFullYear(Math.abs(date.getFullYear()));
+ return dateFilter(_date, 'yyyy');
+ }
+ },
+ {
+ key: 'yy',
+ regex: '\\d{2}',
+ apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; },
+ formatter: function(date) {
+ var _date = new Date();
+ _date.setFullYear(Math.abs(date.getFullYear()));
+ return dateFilter(_date, 'yy');
+ }
+ },
+ {
+ key: 'y',
+ regex: '\\d{1,4}',
+ apply: function(value) { this.year = +value; },
+ formatter: function(date) {
+ var _date = new Date();
+ _date.setFullYear(Math.abs(date.getFullYear()));
+ return dateFilter(_date, 'y');
+ }
+ },
+ {
+ key: 'M!',
+ regex: '0?[1-9]|1[0-2]',
+ apply: function(value) { this.month = value - 1; },
+ formatter: function(date) {
+ var value = date.getMonth();
+ if (/^[0-9]$/.test(value)) {
+ return dateFilter(date, 'MM');
+ }
+
+ return dateFilter(date, 'M');
+ }
+ },
+ {
+ key: 'MMMM',
+ regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
+ apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); },
+ formatter: function(date) { return dateFilter(date, 'MMMM'); }
+ },
+ {
+ key: 'MMM',
+ regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
+ apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); },
+ formatter: function(date) { return dateFilter(date, 'MMM'); }
+ },
+ {
+ key: 'MM',
+ regex: '0[1-9]|1[0-2]',
+ apply: function(value) { this.month = value - 1; },
+ formatter: function(date) { return dateFilter(date, 'MM'); }
+ },
+ {
+ key: 'M',
+ regex: '[1-9]|1[0-2]',
+ apply: function(value) { this.month = value - 1; },
+ formatter: function(date) { return dateFilter(date, 'M'); }
+ },
+ {
+ key: 'd!',
+ regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
+ apply: function(value) { this.date = +value; },
+ formatter: function(date) {
+ var value = date.getDate();
+ if (/^[1-9]$/.test(value)) {
+ return dateFilter(date, 'dd');
+ }
+
+ return dateFilter(date, 'd');
+ }
+ },
+ {
+ key: 'dd',
+ regex: '[0-2][0-9]{1}|3[0-1]{1}',
+ apply: function(value) { this.date = +value; },
+ formatter: function(date) { return dateFilter(date, 'dd'); }
+ },
+ {
+ key: 'd',
+ regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
+ apply: function(value) { this.date = +value; },
+ formatter: function(date) { return dateFilter(date, 'd'); }
+ },
+ {
+ key: 'EEEE',
+ regex: $locale.DATETIME_FORMATS.DAY.join('|'),
+ formatter: function(date) { return dateFilter(date, 'EEEE'); }
+ },
+ {
+ key: 'EEE',
+ regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
+ formatter: function(date) { return dateFilter(date, 'EEE'); }
+ },
+ {
+ key: 'HH',
+ regex: '(?:0|1)[0-9]|2[0-3]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'HH'); }
+ },
+ {
+ key: 'hh',
+ regex: '0[0-9]|1[0-2]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'hh'); }
+ },
+ {
+ key: 'H',
+ regex: '1?[0-9]|2[0-3]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'H'); }
+ },
+ {
+ key: 'h',
+ regex: '[0-9]|1[0-2]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'h'); }
+ },
+ {
+ key: 'mm',
+ regex: '[0-5][0-9]',
+ apply: function(value) { this.minutes = +value; },
+ formatter: function(date) { return dateFilter(date, 'mm'); }
+ },
+ {
+ key: 'm',
+ regex: '[0-9]|[1-5][0-9]',
+ apply: function(value) { this.minutes = +value; },
+ formatter: function(date) { return dateFilter(date, 'm'); }
+ },
+ {
+ key: 'sss',
+ regex: '[0-9][0-9][0-9]',
+ apply: function(value) { this.milliseconds = +value; },
+ formatter: function(date) { return dateFilter(date, 'sss'); }
+ },
+ {
+ key: 'ss',
+ regex: '[0-5][0-9]',
+ apply: function(value) { this.seconds = +value; },
+ formatter: function(date) { return dateFilter(date, 'ss'); }
+ },
+ {
+ key: 's',
+ regex: '[0-9]|[1-5][0-9]',
+ apply: function(value) { this.seconds = +value; },
+ formatter: function(date) { return dateFilter(date, 's'); }
+ },
+ {
+ key: 'a',
+ regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
+ apply: function(value) {
+ if (this.hours === 12) {
+ this.hours = 0;
+ }
+
+ if (value === 'PM') {
+ this.hours += 12;
+ }
+ },
+ formatter: function(date) { return dateFilter(date, 'a'); }
+ },
+ {
+ key: 'Z',
+ regex: '[+-]\\d{4}',
+ apply: function(value) {
+ var matches = value.match(/([+-])(\d{2})(\d{2})/),
+ sign = matches[1],
+ hours = matches[2],
+ minutes = matches[3];
+ this.hours += toInt(sign + hours);
+ this.minutes += toInt(sign + minutes);
+ },
+ formatter: function(date) {
+ return dateFilter(date, 'Z');
+ }
+ },
+ {
+ key: 'ww',
+ regex: '[0-4][0-9]|5[0-3]',
+ formatter: function(date) { return dateFilter(date, 'ww'); }
+ },
+ {
+ key: 'w',
+ regex: '[0-9]|[1-4][0-9]|5[0-3]',
+ formatter: function(date) { return dateFilter(date, 'w'); }
+ },
+ {
+ key: 'GGGG',
+ regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'),
+ formatter: function(date) { return dateFilter(date, 'GGGG'); }
+ },
+ {
+ key: 'GGG',
+ regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
+ formatter: function(date) { return dateFilter(date, 'GGG'); }
+ },
+ {
+ key: 'GG',
+ regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
+ formatter: function(date) { return dateFilter(date, 'GG'); }
+ },
+ {
+ key: 'G',
+ regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
+ formatter: function(date) { return dateFilter(date, 'G'); }
+ }
+ ];
+ };
+
+ this.init();
+
+ function createParser(format, func) {
+ var map = [], regex = format.split('');
+
+ // check for literal values
+ var quoteIndex = format.indexOf('\'');
+ if (quoteIndex > -1) {
+ var inLiteral = false;
+ format = format.split('');
+ for (var i = quoteIndex; i < format.length; i++) {
+ if (inLiteral) {
+ if (format[i] === '\'') {
+ if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
+ format[i+1] = '$';
+ regex[i+1] = '';
+ } else { // end of literal
+ regex[i] = '';
+ inLiteral = false;
+ }
+ }
+ format[i] = '$';
+ } else {
+ if (format[i] === '\'') { // start of literal
+ format[i] = '$';
+ regex[i] = '';
+ inLiteral = true;
+ }
+ }
+ }
+
+ format = format.join('');
+ }
+
+ angular.forEach(formatCodeToRegex, function(data) {
+ var index = format.indexOf(data.key);
+
+ if (index > -1) {
+ format = format.split('');
+
+ regex[index] = '(' + data.regex + ')';
+ format[index] = '$'; // Custom symbol to define consumed part of format
+ for (var i = index + 1, n = index + data.key.length; i < n; i++) {
+ regex[i] = '';
+ format[i] = '$';
+ }
+ format = format.join('');
+
+ map.push({
+ index: index,
+ key: data.key,
+ apply: data[func],
+ matcher: data.regex
+ });
+ }
+ });
+
+ return {
+ regex: new RegExp('^' + regex.join('') + '$'),
+ map: orderByFilter(map, 'index')
+ };
+ }
+
+ this.filter = function(date, format) {
+ if (!angular.isDate(date) || isNaN(date) || !format) {
+ return '';
+ }
+
+ format = $locale.DATETIME_FORMATS[format] || format;
+
+ if ($locale.id !== localeId) {
+ this.init();
+ }
+
+ if (!this.formatters[format]) {
+ this.formatters[format] = createParser(format, 'formatter');
+ }
+
+ var parser = this.formatters[format],
+ map = parser.map;
+
+ var _format = format;
+
+ return map.reduce(function(str, mapper, i) {
+ var match = _format.match(new RegExp('(.*)' + mapper.key));
+ if (match && angular.isString(match[1])) {
+ str += match[1];
+ _format = _format.replace(match[1] + mapper.key, '');
+ }
+
+ var endStr = i === map.length - 1 ? _format : '';
+
+ if (mapper.apply) {
+ return str + mapper.apply.call(null, date) + endStr;
+ }
+
+ return str + endStr;
+ }, '');
+ };
+
+ this.parse = function(input, format, baseDate) {
+ if (!angular.isString(input) || !format) {
+ return input;
+ }
+
+ format = $locale.DATETIME_FORMATS[format] || format;
+ format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
+
+ if ($locale.id !== localeId) {
+ this.init();
+ }
+
+ if (!this.parsers[format]) {
+ this.parsers[format] = createParser(format, 'apply');
+ }
+
+ var parser = this.parsers[format],
+ regex = parser.regex,
+ map = parser.map,
+ results = input.match(regex),
+ tzOffset = false;
+ if (results && results.length) {
+ var fields, dt;
+ if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
+ fields = {
+ year: baseDate.getFullYear(),
+ month: baseDate.getMonth(),
+ date: baseDate.getDate(),
+ hours: baseDate.getHours(),
+ minutes: baseDate.getMinutes(),
+ seconds: baseDate.getSeconds(),
+ milliseconds: baseDate.getMilliseconds()
+ };
+ } else {
+ if (baseDate) {
+ $log.warn('dateparser:', 'baseDate is not a valid date');
+ }
+ fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
+ }
+
+ for (var i = 1, n = results.length; i < n; i++) {
+ var mapper = map[i - 1];
+ if (mapper.matcher === 'Z') {
+ tzOffset = true;
+ }
+
+ if (mapper.apply) {
+ mapper.apply.call(fields, results[i]);
+ }
+ }
+
+ var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
+ Date.prototype.setFullYear;
+ var timesetter = tzOffset ? Date.prototype.setUTCHours :
+ Date.prototype.setHours;
+
+ if (isValid(fields.year, fields.month, fields.date)) {
+ if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
+ dt = new Date(baseDate);
+ datesetter.call(dt, fields.year, fields.month, fields.date);
+ timesetter.call(dt, fields.hours, fields.minutes,
+ fields.seconds, fields.milliseconds);
+ } else {
+ dt = new Date(0);
+ datesetter.call(dt, fields.year, fields.month, fields.date);
+ timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
+ fields.seconds || 0, fields.milliseconds || 0);
+ }
+ }
+
+ return dt;
+ }
+ };
+
+ // Check if date is valid for specific month (and year for February).
+ // Month: 0 = Jan, 1 = Feb, etc
+ function isValid(year, month, date) {
+ if (date < 1) {
+ return false;
+ }
+
+ if (month === 1 && date > 28) {
+ return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
+ }
+
+ if (month === 3 || month === 5 || month === 8 || month === 10) {
+ return date < 31;
+ }
+
+ return true;
+ }
+
+ function toInt(str) {
+ return parseInt(str, 10);
+ }
+
+ this.toTimezone = toTimezone;
+ this.fromTimezone = fromTimezone;
+ this.timezoneToOffset = timezoneToOffset;
+ this.addDateMinutes = addDateMinutes;
+ this.convertTimezoneToLocal = convertTimezoneToLocal;
+
+ function toTimezone(date, timezone) {
+ return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
+ }
+
+ function fromTimezone(date, timezone) {
+ return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
+ }
+
+ //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207
+ function timezoneToOffset(timezone, fallback) {
+ timezone = timezone.replace(/:/g, '');
+ var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
+ return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
+ }
+
+ function addDateMinutes(date, minutes) {
+ date = new Date(date.getTime());
+ date.setMinutes(date.getMinutes() + minutes);
+ return date;
+ }
+
+ function convertTimezoneToLocal(date, timezone, reverse) {
+ reverse = reverse ? -1 : 1;
+ var dateTimezoneOffset = date.getTimezoneOffset();
+ var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
+ return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
+ }
+}]);
+
+// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
+// at most one element.
+angular.module('ui.bootstrap.isClass', [])
+.directive('uibIsClass', [
+ '$animate',
+function ($animate) {
+ // 11111111 22222222
+ var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
+ // 11111111 22222222
+ var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
+
+ var dataPerTracked = {};
+
+ return {
+ restrict: 'A',
+ compile: function(tElement, tAttrs) {
+ var linkedScopes = [];
+ var instances = [];
+ var expToData = {};
+ var lastActivated = null;
+ var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
+ var onExp = onExpMatches[2];
+ var expsStr = onExpMatches[1];
+ var exps = expsStr.split(',');
+
+ return linkFn;
+
+ function linkFn(scope, element, attrs) {
+ linkedScopes.push(scope);
+ instances.push({
+ scope: scope,
+ element: element
+ });
+
+ exps.forEach(function(exp, k) {
+ addForExp(exp, scope);
+ });
+
+ scope.$on('$destroy', removeScope);
+ }
+
+ function addForExp(exp, scope) {
+ var matches = exp.match(IS_REGEXP);
+ var clazz = scope.$eval(matches[1]);
+ var compareWithExp = matches[2];
+ var data = expToData[exp];
+ if (!data) {
+ var watchFn = function(compareWithVal) {
+ var newActivated = null;
+ instances.some(function(instance) {
+ var thisVal = instance.scope.$eval(onExp);
+ if (thisVal === compareWithVal) {
+ newActivated = instance;
+ return true;
+ }
+ });
+ if (data.lastActivated !== newActivated) {
+ if (data.lastActivated) {
+ $animate.removeClass(data.lastActivated.element, clazz);
+ }
+ if (newActivated) {
+ $animate.addClass(newActivated.element, clazz);
+ }
+ data.lastActivated = newActivated;
+ }
+ };
+ expToData[exp] = data = {
+ lastActivated: null,
+ scope: scope,
+ watchFn: watchFn,
+ compareWithExp: compareWithExp,
+ watcher: scope.$watch(compareWithExp, watchFn)
+ };
+ }
+ data.watchFn(scope.$eval(compareWithExp));
+ }
+
+ function removeScope(e) {
+ var removedScope = e.targetScope;
+ var index = linkedScopes.indexOf(removedScope);
+ linkedScopes.splice(index, 1);
+ instances.splice(index, 1);
+ if (linkedScopes.length) {
+ var newWatchScope = linkedScopes[0];
+ angular.forEach(expToData, function(data) {
+ if (data.scope === removedScope) {
+ data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
+ data.scope = newWatchScope;
+ }
+ });
+ } else {
+ expToData = {};
+ }
+ }
+ }
+ };
+}]);
+angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass'])
+
+.value('$datepickerSuppressError', false)
+
+.value('$datepickerLiteralWarning', true)
+
+.constant('uibDatepickerConfig', {
+ datepickerMode: 'day',
+ formatDay: 'dd',
+ formatMonth: 'MMMM',
+ formatYear: 'yyyy',
+ formatDayHeader: 'EEE',
+ formatDayTitle: 'MMMM yyyy',
+ formatMonthTitle: 'yyyy',
+ maxDate: null,
+ maxMode: 'year',
+ minDate: null,
+ minMode: 'day',
+ ngModelOptions: {},
+ shortcutPropagation: false,
+ showWeeks: true,
+ yearColumns: 5,
+ yearRows: 4
+})
+
+.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
+ function($scope, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
+ var self = this,
+ ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
+ ngModelOptions = {},
+ watchListeners = [],
+ optionsUsed = !!$attrs.datepickerOptions;
+
+ if (!$scope.datepickerOptions) {
+ $scope.datepickerOptions = {};
+ }
+
+ // Modes chain
+ this.modes = ['day', 'month', 'year'];
+
+ [
+ 'customClass',
+ 'dateDisabled',
+ 'datepickerMode',
+ 'formatDay',
+ 'formatDayHeader',
+ 'formatDayTitle',
+ 'formatMonth',
+ 'formatMonthTitle',
+ 'formatYear',
+ 'maxDate',
+ 'maxMode',
+ 'minDate',
+ 'minMode',
+ 'showWeeks',
+ 'shortcutPropagation',
+ 'startingDay',
+ 'yearColumns',
+ 'yearRows'
+ ].forEach(function(key) {
+ switch (key) {
+ case 'customClass':
+ case 'dateDisabled':
+ $scope[key] = $scope.datepickerOptions[key] || angular.noop;
+ break;
+ case 'datepickerMode':
+ $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ?
+ $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode;
+ break;
+ case 'formatDay':
+ case 'formatDayHeader':
+ case 'formatDayTitle':
+ case 'formatMonth':
+ case 'formatMonthTitle':
+ case 'formatYear':
+ self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
+ $interpolate($scope.datepickerOptions[key])($scope.$parent) :
+ datepickerConfig[key];
+ break;
+ case 'showWeeks':
+ case 'shortcutPropagation':
+ case 'yearColumns':
+ case 'yearRows':
+ self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
+ $scope.datepickerOptions[key] : datepickerConfig[key];
+ break;
+ case 'startingDay':
+ if (angular.isDefined($scope.datepickerOptions.startingDay)) {
+ self.startingDay = $scope.datepickerOptions.startingDay;
+ } else if (angular.isNumber(datepickerConfig.startingDay)) {
+ self.startingDay = datepickerConfig.startingDay;
+ } else {
+ self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7;
+ }
+
+ break;
+ case 'maxDate':
+ case 'minDate':
+ $scope.$watch('datepickerOptions.' + key, function(value) {
+ if (value) {
+ if (angular.isDate(value)) {
+ self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
+ } else {
+ if ($datepickerLiteralWarning) {
+ $log.warn('Literal date support has been deprecated, please switch to date object usage');
+ }
+
+ self[key] = new Date(dateFilter(value, 'medium'));
+ }
+ } else {
+ self[key] = datepickerConfig[key] ?
+ dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) :
+ null;
+ }
+
+ self.refreshView();
+ });
+
+ break;
+ case 'maxMode':
+ case 'minMode':
+ if ($scope.datepickerOptions[key]) {
+ $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) {
+ self[key] = $scope[key] = angular.isDefined(value) ? value : datepickerOptions[key];
+ if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) ||
+ key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) {
+ $scope.datepickerMode = self[key];
+ $scope.datepickerOptions.datepickerMode = self[key];
+ }
+ });
+ } else {
+ self[key] = $scope[key] = datepickerConfig[key] || null;
+ }
+
+ break;
+ }
+ });
+
+ $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
+
+ $scope.disabled = angular.isDefined($attrs.disabled) || false;
+ if (angular.isDefined($attrs.ngDisabled)) {
+ watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
+ $scope.disabled = disabled;
+ self.refreshView();
+ }));
+ }
+
+ $scope.isActive = function(dateObject) {
+ if (self.compare(dateObject.date, self.activeDate) === 0) {
+ $scope.activeDateId = dateObject.uid;
+ return true;
+ }
+ return false;
+ };
+
+ this.init = function(ngModelCtrl_) {
+ ngModelCtrl = ngModelCtrl_;
+ ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
+ if ($scope.datepickerOptions.initDate) {
+ self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date();
+ $scope.$watch('datepickerOptions.initDate', function(initDate) {
+ if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
+ self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
+ self.refreshView();
+ }
+ });
+ } else {
+ self.activeDate = new Date();
+ }
+
+ var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date();
+ this.activeDate = !isNaN(date) ?
+ dateParser.fromTimezone(date, ngModelOptions.timezone) :
+ dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
+
+ ngModelCtrl.$render = function() {
+ self.render();
+ };
+ };
+
+ this.render = function() {
+ if (ngModelCtrl.$viewValue) {
+ var date = new Date(ngModelCtrl.$viewValue),
+ isValid = !isNaN(date);
+
+ if (isValid) {
+ this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
+ } else if (!$datepickerSuppressError) {
+ $log.error('Datepicker directive: "ng-model" value must be a Date object');
+ }
+ }
+ this.refreshView();
+ };
+
+ this.refreshView = function() {
+ if (this.element) {
+ $scope.selectedDt = null;
+ this._refreshView();
+ if ($scope.activeDt) {
+ $scope.activeDateId = $scope.activeDt.uid;
+ }
+
+ var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
+ date = dateParser.fromTimezone(date, ngModelOptions.timezone);
+ ngModelCtrl.$setValidity('dateDisabled', !date ||
+ this.element && !this.isDisabled(date));
+ }
+ };
+
+ this.createDateObject = function(date, format) {
+ var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
+ model = dateParser.fromTimezone(model, ngModelOptions.timezone);
+ var today = new Date();
+ today = dateParser.fromTimezone(today, ngModelOptions.timezone);
+ var time = this.compare(date, today);
+ var dt = {
+ date: date,
+ label: dateParser.filter(date, format),
+ selected: model && this.compare(date, model) === 0,
+ disabled: this.isDisabled(date),
+ past: time < 0,
+ current: time === 0,
+ future: time > 0,
+ customClass: this.customClass(date) || null
+ };
+
+ if (model && this.compare(date, model) === 0) {
+ $scope.selectedDt = dt;
+ }
+
+ if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
+ $scope.activeDt = dt;
+ }
+
+ return dt;
+ };
+
+ this.isDisabled = function(date) {
+ return $scope.disabled ||
+ this.minDate && this.compare(date, this.minDate) < 0 ||
+ this.maxDate && this.compare(date, this.maxDate) > 0 ||
+ $scope.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
+ };
+
+ this.customClass = function(date) {
+ return $scope.customClass({date: date, mode: $scope.datepickerMode});
+ };
+
+ // Split array into smaller arrays
+ this.split = function(arr, size) {
+ var arrays = [];
+ while (arr.length > 0) {
+ arrays.push(arr.splice(0, size));
+ }
+ return arrays;
+ };
+
+ $scope.select = function(date) {
+ if ($scope.datepickerMode === self.minMode) {
+ var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
+ dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
+ dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
+ ngModelCtrl.$setViewValue(dt);
+ ngModelCtrl.$render();
+ } else {
+ self.activeDate = date;
+ setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]);
+
+ $scope.$emit('uib:datepicker.mode');
+ }
+
+ $scope.$broadcast('uib:datepicker.focus');
+ };
+
+ $scope.move = function(direction) {
+ var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
+ month = self.activeDate.getMonth() + direction * (self.step.months || 0);
+ self.activeDate.setFullYear(year, month, 1);
+ self.refreshView();
+ };
+
+ $scope.toggleMode = function(direction) {
+ direction = direction || 1;
+
+ if ($scope.datepickerMode === self.maxMode && direction === 1 ||
+ $scope.datepickerMode === self.minMode && direction === -1) {
+ return;
+ }
+
+ setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]);
+
+ $scope.$emit('uib:datepicker.mode');
+ };
+
+ // Key event mapper
+ $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
+
+ var focusElement = function() {
+ self.element[0].focus();
+ };
+
+ // Listen for focus requests from popup directive
+ $scope.$on('uib:datepicker.focus', focusElement);
+
+ $scope.keydown = function(evt) {
+ var key = $scope.keys[evt.which];
+
+ if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
+ return;
+ }
+
+ evt.preventDefault();
+ if (!self.shortcutPropagation) {
+ evt.stopPropagation();
+ }
+
+ if (key === 'enter' || key === 'space') {
+ if (self.isDisabled(self.activeDate)) {
+ return; // do nothing
+ }
+ $scope.select(self.activeDate);
+ } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
+ $scope.toggleMode(key === 'up' ? 1 : -1);
+ } else {
+ self.handleKeyDown(key, evt);
+ self.refreshView();
+ }
+ };
+
+ $scope.$on('$destroy', function() {
+ //Clear all watch listeners on destroy
+ while (watchListeners.length) {
+ watchListeners.shift()();
+ }
+ });
+
+ function setMode(mode) {
+ $scope.datepickerMode = mode;
+ $scope.datepickerOptions.datepickerMode = mode;
+ }
+}])
+
+.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+ this.step = { months: 1 };
+ this.element = $element;
+ function getDaysInMonth(year, month) {
+ return month === 1 && year % 4 === 0 &&
+ (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
+ }
+
+ this.init = function(ctrl) {
+ angular.extend(ctrl, this);
+ scope.showWeeks = ctrl.showWeeks;
+ ctrl.refreshView();
+ };
+
+ this.getDates = function(startDate, n) {
+ var dates = new Array(n), current = new Date(startDate), i = 0, date;
+ while (i < n) {
+ date = new Date(current);
+ dates[i++] = date;
+ current.setDate(current.getDate() + 1);
+ }
+ return dates;
+ };
+
+ this._refreshView = function() {
+ var year = this.activeDate.getFullYear(),
+ month = this.activeDate.getMonth(),
+ firstDayOfMonth = new Date(this.activeDate);
+
+ firstDayOfMonth.setFullYear(year, month, 1);
+
+ var difference = this.startingDay - firstDayOfMonth.getDay(),
+ numDisplayedFromPreviousMonth = difference > 0 ?
+ 7 - difference : - difference,
+ firstDate = new Date(firstDayOfMonth);
+
+ if (numDisplayedFromPreviousMonth > 0) {
+ firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
+ }
+
+ // 42 is the number of days on a six-week calendar
+ var days = this.getDates(firstDate, 42);
+ for (var i = 0; i < 42; i ++) {
+ days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
+ secondary: days[i].getMonth() !== month,
+ uid: scope.uniqueId + '-' + i
+ });
+ }
+
+ scope.labels = new Array(7);
+ for (var j = 0; j < 7; j++) {
+ scope.labels[j] = {
+ abbr: dateFilter(days[j].date, this.formatDayHeader),
+ full: dateFilter(days[j].date, 'EEEE')
+ };
+ }
+
+ scope.title = dateFilter(this.activeDate, this.formatDayTitle);
+ scope.rows = this.split(days, 7);
+
+ if (scope.showWeeks) {
+ scope.weekNumbers = [];
+ var thursdayIndex = (4 + 7 - this.startingDay) % 7,
+ numWeeks = scope.rows.length;
+ for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
+ scope.weekNumbers.push(
+ getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
+ }
+ }
+ };
+
+ this.compare = function(date1, date2) {
+ var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
+ var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
+ _date1.setFullYear(date1.getFullYear());
+ _date2.setFullYear(date2.getFullYear());
+ return _date1 - _date2;
+ };
+
+ function getISO8601WeekNumber(date) {
+ var checkDate = new Date(date);
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
+ var time = checkDate.getTime();
+ checkDate.setMonth(0); // Compare with Jan 1
+ checkDate.setDate(1);
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
+ }
+
+ this.handleKeyDown = function(key, evt) {
+ var date = this.activeDate.getDate();
+
+ if (key === 'left') {
+ date = date - 1;
+ } else if (key === 'up') {
+ date = date - 7;
+ } else if (key === 'right') {
+ date = date + 1;
+ } else if (key === 'down') {
+ date = date + 7;
+ } else if (key === 'pageup' || key === 'pagedown') {
+ var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
+ this.activeDate.setMonth(month, 1);
+ date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
+ } else if (key === 'home') {
+ date = 1;
+ } else if (key === 'end') {
+ date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
+ }
+ this.activeDate.setDate(date);
+ };
+}])
+
+.controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
+ this.step = { years: 1 };
+ this.element = $element;
+
+ this.init = function(ctrl) {
+ angular.extend(ctrl, this);
+ ctrl.refreshView();
+ };
+
+ this._refreshView = function() {
+ var months = new Array(12),
+ year = this.activeDate.getFullYear(),
+ date;
+
+ for (var i = 0; i < 12; i++) {
+ date = new Date(this.activeDate);
+ date.setFullYear(year, i, 1);
+ months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
+ uid: scope.uniqueId + '-' + i
+ });
+ }
+
+ scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
+ scope.rows = this.split(months, 3);
+ };
+
+ this.compare = function(date1, date2) {
+ var _date1 = new Date(date1.getFullYear(), date1.getMonth());
+ var _date2 = new Date(date2.getFullYear(), date2.getMonth());
+ _date1.setFullYear(date1.getFullYear());
+ _date2.setFullYear(date2.getFullYear());
+ return _date1 - _date2;
+ };
+
+ this.handleKeyDown = function(key, evt) {
+ var date = this.activeDate.getMonth();
+
+ if (key === 'left') {
+ date = date - 1;
+ } else if (key === 'up') {
+ date = date - 3;
+ } else if (key === 'right') {
+ date = date + 1;
+ } else if (key === 'down') {
+ date = date + 3;
+ } else if (key === 'pageup' || key === 'pagedown') {
+ var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
+ this.activeDate.setFullYear(year);
+ } else if (key === 'home') {
+ date = 0;
+ } else if (key === 'end') {
+ date = 11;
+ }
+ this.activeDate.setMonth(date);
+ };
+}])
+
+.controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
+ var columns, range;
+ this.element = $element;
+
+ function getStartingYear(year) {
+ return parseInt((year - 1) / range, 10) * range + 1;
+ }
+
+ this.yearpickerInit = function() {
+ columns = this.yearColumns;
+ range = this.yearRows * columns;
+ this.step = { years: range };
+ };
+
+ this._refreshView = function() {
+ var years = new Array(range), date;
+
+ for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
+ date = new Date(this.activeDate);
+ date.setFullYear(start + i, 0, 1);
+ years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
+ uid: scope.uniqueId + '-' + i
+ });
+ }
+
+ scope.title = [years[0].label, years[range - 1].label].join(' - ');
+ scope.rows = this.split(years, columns);
+ scope.columns = columns;
+ };
+
+ this.compare = function(date1, date2) {
+ return date1.getFullYear() - date2.getFullYear();
+ };
+
+ this.handleKeyDown = function(key, evt) {
+ var date = this.activeDate.getFullYear();
+
+ if (key === 'left') {
+ date = date - 1;
+ } else if (key === 'up') {
+ date = date - columns;
+ } else if (key === 'right') {
+ date = date + 1;
+ } else if (key === 'down') {
+ date = date + columns;
+ } else if (key === 'pageup' || key === 'pagedown') {
+ date += (key === 'pageup' ? - 1 : 1) * range;
+ } else if (key === 'home') {
+ date = getStartingYear(this.activeDate.getFullYear());
+ } else if (key === 'end') {
+ date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
+ }
+ this.activeDate.setFullYear(date);
+ };
+}])
+
+.directive('uibDatepicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
+ },
+ scope: {
+ datepickerOptions: '=?'
+ },
+ require: ['uibDatepicker', '^ngModel'],
+ controller: 'UibDatepickerController',
+ controllerAs: 'datepicker',
+ link: function(scope, element, attrs, ctrls) {
+ var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ datepickerCtrl.init(ngModelCtrl);
+ }
+ };
+})
+
+.directive('uibDaypicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/day.html';
+ },
+ require: ['^uibDatepicker', 'uibDaypicker'],
+ controller: 'UibDaypickerController',
+ link: function(scope, element, attrs, ctrls) {
+ var datepickerCtrl = ctrls[0],
+ daypickerCtrl = ctrls[1];
+
+ daypickerCtrl.init(datepickerCtrl);
+ }
+ };
+})
+
+.directive('uibMonthpicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/month.html';
+ },
+ require: ['^uibDatepicker', 'uibMonthpicker'],
+ controller: 'UibMonthpickerController',
+ link: function(scope, element, attrs, ctrls) {
+ var datepickerCtrl = ctrls[0],
+ monthpickerCtrl = ctrls[1];
+
+ monthpickerCtrl.init(datepickerCtrl);
+ }
+ };
+})
+
+.directive('uibYearpicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/year.html';
+ },
+ require: ['^uibDatepicker', 'uibYearpicker'],
+ controller: 'UibYearpickerController',
+ link: function(scope, element, attrs, ctrls) {
+ var ctrl = ctrls[0];
+ angular.extend(ctrl, ctrls[1]);
+ ctrl.yearpickerInit();
+
+ ctrl.refreshView();
+ }
+ };
+});
+
+angular.module('ui.bootstrap.position', [])
+
+/**
+ * A set of utility methods for working with the DOM.
+ * It is meant to be used where we need to absolute-position elements in
+ * relation to another element (this is the case for tooltips, popovers,
+ * typeahead suggestions etc.).
+ */
+ .factory('$uibPosition', ['$document', '$window', function($document, $window) {
+ /**
+ * Used by scrollbarWidth() function to cache scrollbar's width.
+ * Do not access this variable directly, use scrollbarWidth() instead.
+ */
+ var SCROLLBAR_WIDTH;
+ /**
+ * scrollbar on body and html element in IE and Edge overlay
+ * content and should be considered 0 width.
+ */
+ var BODY_SCROLLBAR_WIDTH;
+ var OVERFLOW_REGEX = {
+ normal: /(auto|scroll)/,
+ hidden: /(auto|scroll|hidden)/
+ };
+ var PLACEMENT_REGEX = {
+ auto: /\s?auto?\s?/i,
+ primary: /^(top|bottom|left|right)$/,
+ secondary: /^(top|bottom|left|right|center)$/,
+ vertical: /^(top|bottom)$/
+ };
+ var BODY_REGEX = /(HTML|BODY)/;
+
+ return {
+
+ /**
+ * Provides a raw DOM element from a jQuery/jQLite element.
+ *
+ * @param {element} elem - The element to convert.
+ *
+ * @returns {element} A HTML element.
+ */
+ getRawNode: function(elem) {
+ return elem.nodeName ? elem : elem[0] || elem;
+ },
+
+ /**
+ * Provides a parsed number for a style property. Strips
+ * units and casts invalid numbers to 0.
+ *
+ * @param {string} value - The style value to parse.
+ *
+ * @returns {number} A valid number.
+ */
+ parseStyle: function(value) {
+ value = parseFloat(value);
+ return isFinite(value) ? value : 0;
+ },
+
+ /**
+ * Provides the closest positioned ancestor.
+ *
+ * @param {element} element - The element to get the offest parent for.
+ *
+ * @returns {element} The closest positioned ancestor.
+ */
+ offsetParent: function(elem) {
+ elem = this.getRawNode(elem);
+
+ var offsetParent = elem.offsetParent || $document[0].documentElement;
+
+ function isStaticPositioned(el) {
+ return ($window.getComputedStyle(el).position || 'static') === 'static';
+ }
+
+ while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
+ offsetParent = offsetParent.offsetParent;
+ }
+
+ return offsetParent || $document[0].documentElement;
+ },
+
+ /**
+ * Provides the scrollbar width, concept from TWBS measureScrollbar()
+ * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
+ * In IE and Edge, scollbar on body and html element overlay and should
+ * return a width of 0.
+ *
+ * @returns {number} The width of the browser scollbar.
+ */
+ scrollbarWidth: function(isBody) {
+ if (isBody) {
+ if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) {
+ var bodyElem = $document.find('body');
+ bodyElem.addClass('uib-position-body-scrollbar-measure');
+ BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth;
+ BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0;
+ bodyElem.removeClass('uib-position-body-scrollbar-measure');
+ }
+ return BODY_SCROLLBAR_WIDTH;
+ }
+
+ if (angular.isUndefined(SCROLLBAR_WIDTH)) {
+ var scrollElem = angular.element('');
+ $document.find('body').append(scrollElem);
+ SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
+ SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
+ scrollElem.remove();
+ }
+
+ return SCROLLBAR_WIDTH;
+ },
+
+ /**
+ * Provides the padding required on an element to replace the scrollbar.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**scrollbarWidth**: the width of the scrollbar
+ *
**widthOverflow**: whether the the width is overflowing
+ *
**right**: the amount of right padding on the element needed to replace the scrollbar
+ *
**rightOriginal**: the amount of right padding currently on the element
+ *
**heightOverflow**: whether the the height is overflowing
+ *
**bottom**: the amount of bottom padding on the element needed to replace the scrollbar
+ *
**bottomOriginal**: the amount of bottom padding currently on the element
+ *
+ */
+ scrollbarPadding: function(elem) {
+ elem = this.getRawNode(elem);
+
+ var elemStyle = $window.getComputedStyle(elem);
+ var paddingRight = this.parseStyle(elemStyle.paddingRight);
+ var paddingBottom = this.parseStyle(elemStyle.paddingBottom);
+ var scrollParent = this.scrollParent(elem, false, true);
+ var scrollbarWidth = this.scrollbarWidth(scrollParent, BODY_REGEX.test(scrollParent.tagName));
+
+ return {
+ scrollbarWidth: scrollbarWidth,
+ widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth,
+ right: paddingRight + scrollbarWidth,
+ originalRight: paddingRight,
+ heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight,
+ bottom: paddingBottom + scrollbarWidth,
+ originalBottom: paddingBottom
+ };
+ },
+
+ /**
+ * Checks to see if the element is scrollable.
+ *
+ * @param {element} elem - The element to check.
+ * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
+ * default is false.
+ *
+ * @returns {boolean} Whether the element is scrollable.
+ */
+ isScrollable: function(elem, includeHidden) {
+ elem = this.getRawNode(elem);
+
+ var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
+ var elemStyle = $window.getComputedStyle(elem);
+ return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX);
+ },
+
+ /**
+ * Provides the closest scrollable ancestor.
+ * A port of the jQuery UI scrollParent method:
+ * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
+ *
+ * @param {element} elem - The element to find the scroll parent of.
+ * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
+ * default is false.
+ * @param {boolean=} [includeSelf=false] - Should the element being passed be
+ * included in the scrollable llokup.
+ *
+ * @returns {element} A HTML element.
+ */
+ scrollParent: function(elem, includeHidden, includeSelf) {
+ elem = this.getRawNode(elem);
+
+ var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
+ var documentEl = $document[0].documentElement;
+ var elemStyle = $window.getComputedStyle(elem);
+ if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) {
+ return elem;
+ }
+ var excludeStatic = elemStyle.position === 'absolute';
+ var scrollParent = elem.parentElement || documentEl;
+
+ if (scrollParent === documentEl || elemStyle.position === 'fixed') {
+ return documentEl;
+ }
+
+ while (scrollParent.parentElement && scrollParent !== documentEl) {
+ var spStyle = $window.getComputedStyle(scrollParent);
+ if (excludeStatic && spStyle.position !== 'static') {
+ excludeStatic = false;
+ }
+
+ if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
+ break;
+ }
+ scrollParent = scrollParent.parentElement;
+ }
+
+ return scrollParent;
+ },
+
+ /**
+ * Provides read-only equivalent of jQuery's position function:
+ * http://api.jquery.com/position/ - distance to closest positioned
+ * ancestor. Does not account for margins by default like jQuery position.
+ *
+ * @param {element} elem - The element to caclulate the position on.
+ * @param {boolean=} [includeMargins=false] - Should margins be accounted
+ * for, default is false.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**width**: the width of the element
+ *
**height**: the height of the element
+ *
**top**: distance to top edge of offset parent
+ *
**left**: distance to left edge of offset parent
+ *
+ */
+ position: function(elem, includeMagins) {
+ elem = this.getRawNode(elem);
+
+ var elemOffset = this.offset(elem);
+ if (includeMagins) {
+ var elemStyle = $window.getComputedStyle(elem);
+ elemOffset.top -= this.parseStyle(elemStyle.marginTop);
+ elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
+ }
+ var parent = this.offsetParent(elem);
+ var parentOffset = {top: 0, left: 0};
+
+ if (parent !== $document[0].documentElement) {
+ parentOffset = this.offset(parent);
+ parentOffset.top += parent.clientTop - parent.scrollTop;
+ parentOffset.left += parent.clientLeft - parent.scrollLeft;
+ }
+
+ return {
+ width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
+ height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
+ top: Math.round(elemOffset.top - parentOffset.top),
+ left: Math.round(elemOffset.left - parentOffset.left)
+ };
+ },
+
+ /**
+ * Provides read-only equivalent of jQuery's offset function:
+ * http://api.jquery.com/offset/ - distance to viewport. Does
+ * not account for borders, margins, or padding on the body
+ * element.
+ *
+ * @param {element} elem - The element to calculate the offset on.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**width**: the width of the element
+ *
**height**: the height of the element
+ *
**top**: distance to top edge of viewport
+ *
**right**: distance to bottom edge of viewport
+ *
+ */
+ offset: function(elem) {
+ elem = this.getRawNode(elem);
+
+ var elemBCR = elem.getBoundingClientRect();
+ return {
+ width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
+ height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
+ top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
+ left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
+ };
+ },
+
+ /**
+ * Provides offset distance to the closest scrollable ancestor
+ * or viewport. Accounts for border and scrollbar width.
+ *
+ * Right and bottom dimensions represent the distance to the
+ * respective edge of the viewport element. If the element
+ * edge extends beyond the viewport, a negative value will be
+ * reported.
+ *
+ * @param {element} elem - The element to get the viewport offset for.
+ * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
+ * of the first scrollable element, default is false.
+ * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
+ * be accounted for, default is true.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**top**: distance to the top content edge of viewport element
+ *
**bottom**: distance to the bottom content edge of viewport element
+ *
**left**: distance to the left content edge of viewport element
+ *
**right**: distance to the right content edge of viewport element
top: element on top, horizontally centered on host element.
+ *
top-left: element on top, left edge aligned with host element left edge.
+ *
top-right: element on top, lerightft edge aligned with host element right edge.
+ *
bottom: element on bottom, horizontally centered on host element.
+ *
bottom-left: element on bottom, left edge aligned with host element left edge.
+ *
bottom-right: element on bottom, right edge aligned with host element right edge.
+ *
left: element on left, vertically centered on host element.
+ *
left-top: element on left, top edge aligned with host element top edge.
+ *
left-bottom: element on left, bottom edge aligned with host element bottom edge.
+ *
right: element on right, vertically centered on host element.
+ *
right-top: element on right, top edge aligned with host element top edge.
+ *
right-bottom: element on right, bottom edge aligned with host element bottom edge.
+ *
+ * A placement string with an 'auto' indicator is expected to be
+ * space separated from the placement, i.e: 'auto bottom-left' If
+ * the primary and secondary placement values do not match 'top,
+ * bottom, left, right' then 'top' will be the primary placement and
+ * 'center' will be the secondary placement. If 'auto' is passed, true
+ * will be returned as the 3rd value of the array.
+ *
+ * @param {string} placement - The placement string to parse.
+ *
+ * @returns {array} An array with the following values
+ *
+ *
**[0]**: The primary placement.
+ *
**[1]**: The secondary placement.
+ *
**[2]**: If auto is passed: true, else undefined.
+ *
+ */
+ parsePlacement: function(placement) {
+ var autoPlace = PLACEMENT_REGEX.auto.test(placement);
+ if (autoPlace) {
+ placement = placement.replace(PLACEMENT_REGEX.auto, '');
+ }
+
+ placement = placement.split('-');
+
+ placement[0] = placement[0] || 'top';
+ if (!PLACEMENT_REGEX.primary.test(placement[0])) {
+ placement[0] = 'top';
+ }
+
+ placement[1] = placement[1] || 'center';
+ if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
+ placement[1] = 'center';
+ }
+
+ if (autoPlace) {
+ placement[2] = true;
+ } else {
+ placement[2] = false;
+ }
+
+ return placement;
+ },
+
+ /**
+ * Provides coordinates for an element to be positioned relative to
+ * another element. Passing 'auto' as part of the placement parameter
+ * will enable smart placement - where the element fits. i.e:
+ * 'auto left-top' will check to see if there is enough space to the left
+ * of the hostElem to fit the targetElem, if not place right (same for secondary
+ * top placement). Available space is calculated using the viewportOffset
+ * function.
+ *
+ * @param {element} hostElem - The element to position against.
+ * @param {element} targetElem - The element to position.
+ * @param {string=} [placement=top] - The placement for the targetElem,
+ * default is 'top'. 'center' is assumed as secondary placement for
+ * 'top', 'left', 'right', and 'bottom' placements. Available placements are:
+ *
+ *
top
+ *
top-right
+ *
top-left
+ *
bottom
+ *
bottom-left
+ *
bottom-right
+ *
left
+ *
left-top
+ *
left-bottom
+ *
right
+ *
right-top
+ *
right-bottom
+ *
+ * @param {boolean=} [appendToBody=false] - Should the top and left values returned
+ * be calculated from the body element, default is false.
+ *
+ * @returns {object} An object with the following properties:
+ *
\n')}]),angular.module("ui.bootstrap.carousel").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibCarouselCss&&angular.element(document).find("head").prepend(''),angular.$$uibCarouselCss=!0}),angular.module("ui.bootstrap.datepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerCss=!0}),angular.module("ui.bootstrap.position").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibPositionCss&&angular.element(document).find("head").prepend(''),angular.$$uibPositionCss=!0}),angular.module("ui.bootstrap.datepickerPopup").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerpopupCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerpopupCss=!0}),angular.module("ui.bootstrap.tooltip").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTooltipCss&&angular.element(document).find("head").prepend(''),angular.$$uibTooltipCss=!0}),angular.module("ui.bootstrap.timepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTimepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibTimepickerCss=!0}),angular.module("ui.bootstrap.typeahead").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTypeaheadCss&&angular.element(document).find("head").prepend(''),angular.$$uibTypeaheadCss=!0});
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap.js
new file mode 100644
index 0000000000..8ec4bbcb37
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap.js
@@ -0,0 +1,6924 @@
+/*
+ * angular-ui-bootstrap
+ * http://angular-ui.github.io/bootstrap/
+
+ * Version: 1.3.3 - 2016-05-22
+ * License: MIT
+ */angular.module("ui.bootstrap", ["ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
+angular.module('ui.bootstrap.collapse', [])
+
+ .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) {
+ var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
+ return {
+ link: function(scope, element, attrs) {
+ var expandingExpr = $parse(attrs.expanding),
+ expandedExpr = $parse(attrs.expanded),
+ collapsingExpr = $parse(attrs.collapsing),
+ collapsedExpr = $parse(attrs.collapsed);
+
+ if (!scope.$eval(attrs.uibCollapse)) {
+ element.addClass('in')
+ .addClass('collapse')
+ .attr('aria-expanded', true)
+ .attr('aria-hidden', false)
+ .css({height: 'auto'});
+ }
+
+ function expand() {
+ if (element.hasClass('collapse') && element.hasClass('in')) {
+ return;
+ }
+
+ $q.resolve(expandingExpr(scope))
+ .then(function() {
+ element.removeClass('collapse')
+ .addClass('collapsing')
+ .attr('aria-expanded', true)
+ .attr('aria-hidden', false);
+
+ if ($animateCss) {
+ $animateCss(element, {
+ addClass: 'in',
+ easing: 'ease',
+ to: { height: element[0].scrollHeight + 'px' }
+ }).start()['finally'](expandDone);
+ } else {
+ $animate.addClass(element, 'in', {
+ to: { height: element[0].scrollHeight + 'px' }
+ }).then(expandDone);
+ }
+ });
+ }
+
+ function expandDone() {
+ element.removeClass('collapsing')
+ .addClass('collapse')
+ .css({height: 'auto'});
+ expandedExpr(scope);
+ }
+
+ function collapse() {
+ if (!element.hasClass('collapse') && !element.hasClass('in')) {
+ return collapseDone();
+ }
+
+ $q.resolve(collapsingExpr(scope))
+ .then(function() {
+ element
+ // IMPORTANT: The height must be set before adding "collapsing" class.
+ // Otherwise, the browser attempts to animate from height 0 (in
+ // collapsing class) to the given height here.
+ .css({height: element[0].scrollHeight + 'px'})
+ // initially all panel collapse have the collapse class, this removal
+ // prevents the animation from jumping to collapsed state
+ .removeClass('collapse')
+ .addClass('collapsing')
+ .attr('aria-expanded', false)
+ .attr('aria-hidden', true);
+
+ if ($animateCss) {
+ $animateCss(element, {
+ removeClass: 'in',
+ to: {height: '0'}
+ }).start()['finally'](collapseDone);
+ } else {
+ $animate.removeClass(element, 'in', {
+ to: {height: '0'}
+ }).then(collapseDone);
+ }
+ });
+ }
+
+ function collapseDone() {
+ element.css({height: '0'}); // Required so that collapse works when animation is disabled
+ element.removeClass('collapsing')
+ .addClass('collapse');
+ collapsedExpr(scope);
+ }
+
+ scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
+ if (shouldCollapse) {
+ collapse();
+ } else {
+ expand();
+ }
+ });
+ }
+ };
+ }]);
+
+angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
+
+.constant('uibAccordionConfig', {
+ closeOthers: true
+})
+
+.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
+ // This array keeps track of the accordion groups
+ this.groups = [];
+
+ // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
+ this.closeOthers = function(openGroup) {
+ var closeOthers = angular.isDefined($attrs.closeOthers) ?
+ $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
+ if (closeOthers) {
+ angular.forEach(this.groups, function(group) {
+ if (group !== openGroup) {
+ group.isOpen = false;
+ }
+ });
+ }
+ };
+
+ // This is called from the accordion-group directive to add itself to the accordion
+ this.addGroup = function(groupScope) {
+ var that = this;
+ this.groups.push(groupScope);
+
+ groupScope.$on('$destroy', function(event) {
+ that.removeGroup(groupScope);
+ });
+ };
+
+ // This is called from the accordion-group directive when to remove itself
+ this.removeGroup = function(group) {
+ var index = this.groups.indexOf(group);
+ if (index !== -1) {
+ this.groups.splice(index, 1);
+ }
+ };
+}])
+
+// The accordion directive simply sets up the directive controller
+// and adds an accordion CSS class to itself element.
+.directive('uibAccordion', function() {
+ return {
+ controller: 'UibAccordionController',
+ controllerAs: 'accordion',
+ transclude: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/accordion/accordion.html';
+ }
+ };
+})
+
+// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
+.directive('uibAccordionGroup', function() {
+ return {
+ require: '^uibAccordion', // We need this directive to be inside an accordion
+ transclude: true, // It transcludes the contents of the directive into the template
+ replace: true, // The element containing the directive will be replaced with the template
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
+ },
+ scope: {
+ heading: '@', // Interpolate the heading attribute onto this scope
+ panelClass: '@?', // Ditto with panelClass
+ isOpen: '=?',
+ isDisabled: '=?'
+ },
+ controller: function() {
+ this.setHeading = function(element) {
+ this.heading = element;
+ };
+ },
+ link: function(scope, element, attrs, accordionCtrl) {
+ accordionCtrl.addGroup(scope);
+
+ scope.openClass = attrs.openClass || 'panel-open';
+ scope.panelClass = attrs.panelClass || 'panel-default';
+ scope.$watch('isOpen', function(value) {
+ element.toggleClass(scope.openClass, !!value);
+ if (value) {
+ accordionCtrl.closeOthers(scope);
+ }
+ });
+
+ scope.toggleOpen = function($event) {
+ if (!scope.isDisabled) {
+ if (!$event || $event.which === 32) {
+ scope.isOpen = !scope.isOpen;
+ }
+ }
+ };
+
+ var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
+ scope.headingId = id + '-tab';
+ scope.panelId = id + '-panel';
+ }
+ };
+})
+
+// Use accordion-heading below an accordion-group to provide a heading containing HTML
+.directive('uibAccordionHeading', function() {
+ return {
+ transclude: true, // Grab the contents to be used as the heading
+ template: '', // In effect remove this element!
+ replace: true,
+ require: '^uibAccordionGroup',
+ link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
+ // Pass the heading to the accordion-group controller
+ // so that it can be transcluded into the right place in the template
+ // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
+ accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
+ }
+ };
+})
+
+// Use in the accordion-group template to indicate where you want the heading to be transcluded
+// You must provide the property on the accordion-group controller that will hold the transcluded element
+.directive('uibAccordionTransclude', function() {
+ return {
+ require: '^uibAccordionGroup',
+ link: function(scope, element, attrs, controller) {
+ scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
+ if (heading) {
+ var elem = angular.element(element[0].querySelector(getHeaderSelectors()));
+ elem.html('');
+ elem.append(heading);
+ }
+ });
+ }
+ };
+
+ function getHeaderSelectors() {
+ return 'uib-accordion-header,' +
+ 'data-uib-accordion-header,' +
+ 'x-uib-accordion-header,' +
+ 'uib\\:accordion-header,' +
+ '[uib-accordion-header],' +
+ '[data-uib-accordion-header],' +
+ '[x-uib-accordion-header]';
+ }
+});
+
+angular.module('ui.bootstrap.alert', [])
+
+.controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
+ $scope.closeable = !!$attrs.close;
+
+ var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
+ $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
+
+ if (dismissOnTimeout) {
+ $timeout(function() {
+ $scope.close();
+ }, parseInt(dismissOnTimeout, 10));
+ }
+}])
+
+.directive('uibAlert', function() {
+ return {
+ controller: 'UibAlertController',
+ controllerAs: 'alert',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/alert/alert.html';
+ },
+ transclude: true,
+ replace: true,
+ scope: {
+ type: '@',
+ close: '&'
+ }
+ };
+});
+
+angular.module('ui.bootstrap.buttons', [])
+
+.constant('uibButtonConfig', {
+ activeClass: 'active',
+ toggleEvent: 'click'
+})
+
+.controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
+ this.activeClass = buttonConfig.activeClass || 'active';
+ this.toggleEvent = buttonConfig.toggleEvent || 'click';
+}])
+
+.directive('uibBtnRadio', ['$parse', function($parse) {
+ return {
+ require: ['uibBtnRadio', 'ngModel'],
+ controller: 'UibButtonsController',
+ controllerAs: 'buttons',
+ link: function(scope, element, attrs, ctrls) {
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+ var uncheckableExpr = $parse(attrs.uibUncheckable);
+
+ element.find('input').css({display: 'none'});
+
+ //model -> UI
+ ngModelCtrl.$render = function() {
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
+ };
+
+ //ui->model
+ element.on(buttonsCtrl.toggleEvent, function() {
+ if (attrs.disabled) {
+ return;
+ }
+
+ var isActive = element.hasClass(buttonsCtrl.activeClass);
+
+ if (!isActive || angular.isDefined(attrs.uncheckable)) {
+ scope.$apply(function() {
+ ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
+ ngModelCtrl.$render();
+ });
+ }
+ });
+
+ if (attrs.uibUncheckable) {
+ scope.$watch(uncheckableExpr, function(uncheckable) {
+ attrs.$set('uncheckable', uncheckable ? '' : undefined);
+ });
+ }
+ }
+ };
+}])
+
+.directive('uibBtnCheckbox', function() {
+ return {
+ require: ['uibBtnCheckbox', 'ngModel'],
+ controller: 'UibButtonsController',
+ controllerAs: 'button',
+ link: function(scope, element, attrs, ctrls) {
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ element.find('input').css({display: 'none'});
+
+ function getTrueValue() {
+ return getCheckboxValue(attrs.btnCheckboxTrue, true);
+ }
+
+ function getFalseValue() {
+ return getCheckboxValue(attrs.btnCheckboxFalse, false);
+ }
+
+ function getCheckboxValue(attribute, defaultValue) {
+ return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
+ }
+
+ //model -> UI
+ ngModelCtrl.$render = function() {
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
+ };
+
+ //ui->model
+ element.on(buttonsCtrl.toggleEvent, function() {
+ if (attrs.disabled) {
+ return;
+ }
+
+ scope.$apply(function() {
+ ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
+ ngModelCtrl.$render();
+ });
+ });
+ }
+ };
+});
+
+angular.module('ui.bootstrap.carousel', [])
+
+.controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
+ var self = this,
+ slides = self.slides = $scope.slides = [],
+ SLIDE_DIRECTION = 'uib-slideDirection',
+ currentIndex = $scope.active,
+ currentInterval, isPlaying, bufferedTransitions = [];
+
+ var destroyed = false;
+
+ self.addSlide = function(slide, element) {
+ slides.push({
+ slide: slide,
+ element: element
+ });
+ slides.sort(function(a, b) {
+ return +a.slide.index - +b.slide.index;
+ });
+ //if this is the first slide or the slide is set to active, select it
+ if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) {
+ if ($scope.$currentTransition) {
+ $scope.$currentTransition = null;
+ }
+
+ currentIndex = slide.index;
+ $scope.active = slide.index;
+ setActive(currentIndex);
+ self.select(slides[findSlideIndex(slide)]);
+ if (slides.length === 1) {
+ $scope.play();
+ }
+ }
+ };
+
+ self.getCurrentIndex = function() {
+ for (var i = 0; i < slides.length; i++) {
+ if (slides[i].slide.index === currentIndex) {
+ return i;
+ }
+ }
+ };
+
+ self.next = $scope.next = function() {
+ var newIndex = (self.getCurrentIndex() + 1) % slides.length;
+
+ if (newIndex === 0 && $scope.noWrap()) {
+ $scope.pause();
+ return;
+ }
+
+ return self.select(slides[newIndex], 'next');
+ };
+
+ self.prev = $scope.prev = function() {
+ var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
+
+ if ($scope.noWrap() && newIndex === slides.length - 1) {
+ $scope.pause();
+ return;
+ }
+
+ return self.select(slides[newIndex], 'prev');
+ };
+
+ self.removeSlide = function(slide) {
+ var index = findSlideIndex(slide);
+
+ var bufferedIndex = bufferedTransitions.indexOf(slides[index]);
+ if (bufferedIndex !== -1) {
+ bufferedTransitions.splice(bufferedIndex, 1);
+ }
+
+ //get the index of the slide inside the carousel
+ slides.splice(index, 1);
+ if (slides.length > 0 && currentIndex === index) {
+ if (index >= slides.length) {
+ currentIndex = slides.length - 1;
+ $scope.active = currentIndex;
+ setActive(currentIndex);
+ self.select(slides[slides.length - 1]);
+ } else {
+ currentIndex = index;
+ $scope.active = currentIndex;
+ setActive(currentIndex);
+ self.select(slides[index]);
+ }
+ } else if (currentIndex > index) {
+ currentIndex--;
+ $scope.active = currentIndex;
+ }
+
+ //clean the active value when no more slide
+ if (slides.length === 0) {
+ currentIndex = null;
+ $scope.active = null;
+ clearBufferedTransitions();
+ }
+ };
+
+ /* direction: "prev" or "next" */
+ self.select = $scope.select = function(nextSlide, direction) {
+ var nextIndex = findSlideIndex(nextSlide.slide);
+ //Decide direction if it's not given
+ if (direction === undefined) {
+ direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
+ }
+ //Prevent this user-triggered transition from occurring if there is already one in progress
+ if (nextSlide.slide.index !== currentIndex &&
+ !$scope.$currentTransition) {
+ goNext(nextSlide.slide, nextIndex, direction);
+ } else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) {
+ bufferedTransitions.push(slides[nextIndex]);
+ }
+ };
+
+ /* Allow outside people to call indexOf on slides array */
+ $scope.indexOfSlide = function(slide) {
+ return +slide.slide.index;
+ };
+
+ $scope.isActive = function(slide) {
+ return $scope.active === slide.slide.index;
+ };
+
+ $scope.isPrevDisabled = function() {
+ return $scope.active === 0 && $scope.noWrap();
+ };
+
+ $scope.isNextDisabled = function() {
+ return $scope.active === slides.length - 1 && $scope.noWrap();
+ };
+
+ $scope.pause = function() {
+ if (!$scope.noPause) {
+ isPlaying = false;
+ resetTimer();
+ }
+ };
+
+ $scope.play = function() {
+ if (!isPlaying) {
+ isPlaying = true;
+ restartTimer();
+ }
+ };
+
+ $scope.$on('$destroy', function() {
+ destroyed = true;
+ resetTimer();
+ });
+
+ $scope.$watch('noTransition', function(noTransition) {
+ $animate.enabled($element, !noTransition);
+ });
+
+ $scope.$watch('interval', restartTimer);
+
+ $scope.$watchCollection('slides', resetTransition);
+
+ $scope.$watch('active', function(index) {
+ if (angular.isNumber(index) && currentIndex !== index) {
+ for (var i = 0; i < slides.length; i++) {
+ if (slides[i].slide.index === index) {
+ index = i;
+ break;
+ }
+ }
+
+ var slide = slides[index];
+ if (slide) {
+ setActive(index);
+ self.select(slides[index]);
+ currentIndex = index;
+ }
+ }
+ });
+
+ function clearBufferedTransitions() {
+ while (bufferedTransitions.length) {
+ bufferedTransitions.shift();
+ }
+ }
+
+ function getSlideByIndex(index) {
+ for (var i = 0, l = slides.length; i < l; ++i) {
+ if (slides[i].index === index) {
+ return slides[i];
+ }
+ }
+ }
+
+ function setActive(index) {
+ for (var i = 0; i < slides.length; i++) {
+ slides[i].slide.active = i === index;
+ }
+ }
+
+ function goNext(slide, index, direction) {
+ if (destroyed) {
+ return;
+ }
+
+ angular.extend(slide, {direction: direction});
+ angular.extend(slides[currentIndex].slide || {}, {direction: direction});
+ if ($animate.enabled($element) && !$scope.$currentTransition &&
+ slides[index].element && self.slides.length > 1) {
+ slides[index].element.data(SLIDE_DIRECTION, slide.direction);
+ var currentIdx = self.getCurrentIndex();
+
+ if (angular.isNumber(currentIdx) && slides[currentIdx].element) {
+ slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction);
+ }
+
+ $scope.$currentTransition = true;
+ $animate.on('addClass', slides[index].element, function(element, phase) {
+ if (phase === 'close') {
+ $scope.$currentTransition = null;
+ $animate.off('addClass', element);
+ if (bufferedTransitions.length) {
+ var nextSlide = bufferedTransitions.pop().slide;
+ var nextIndex = nextSlide.index;
+ var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
+ clearBufferedTransitions();
+
+ goNext(nextSlide, nextIndex, nextDirection);
+ }
+ }
+ });
+ }
+
+ $scope.active = slide.index;
+ currentIndex = slide.index;
+ setActive(index);
+
+ //every time you change slides, reset the timer
+ restartTimer();
+ }
+
+ function findSlideIndex(slide) {
+ for (var i = 0; i < slides.length; i++) {
+ if (slides[i].slide === slide) {
+ return i;
+ }
+ }
+ }
+
+ function resetTimer() {
+ if (currentInterval) {
+ $interval.cancel(currentInterval);
+ currentInterval = null;
+ }
+ }
+
+ function resetTransition(slides) {
+ if (!slides.length) {
+ $scope.$currentTransition = null;
+ clearBufferedTransitions();
+ }
+ }
+
+ function restartTimer() {
+ resetTimer();
+ var interval = +$scope.interval;
+ if (!isNaN(interval) && interval > 0) {
+ currentInterval = $interval(timerFn, interval);
+ }
+ }
+
+ function timerFn() {
+ var interval = +$scope.interval;
+ if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
+ $scope.next();
+ } else {
+ $scope.pause();
+ }
+ }
+}])
+
+.directive('uibCarousel', function() {
+ return {
+ transclude: true,
+ replace: true,
+ controller: 'UibCarouselController',
+ controllerAs: 'carousel',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/carousel/carousel.html';
+ },
+ scope: {
+ active: '=',
+ interval: '=',
+ noTransition: '=',
+ noPause: '=',
+ noWrap: '&'
+ }
+ };
+})
+
+.directive('uibSlide', function() {
+ return {
+ require: '^uibCarousel',
+ transclude: true,
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/carousel/slide.html';
+ },
+ scope: {
+ actual: '=?',
+ index: '=?'
+ },
+ link: function (scope, element, attrs, carouselCtrl) {
+ carouselCtrl.addSlide(scope, element);
+ //when the scope is destroyed then remove the slide from the current slides array
+ scope.$on('$destroy', function() {
+ carouselCtrl.removeSlide(scope);
+ });
+ }
+ };
+})
+
+.animation('.item', ['$animateCss',
+function($animateCss) {
+ var SLIDE_DIRECTION = 'uib-slideDirection';
+
+ function removeClass(element, className, callback) {
+ element.removeClass(className);
+ if (callback) {
+ callback();
+ }
+ }
+
+ return {
+ beforeAddClass: function(element, className, done) {
+ if (className === 'active') {
+ var stopped = false;
+ var direction = element.data(SLIDE_DIRECTION);
+ var directionClass = direction === 'next' ? 'left' : 'right';
+ var removeClassFn = removeClass.bind(this, element,
+ directionClass + ' ' + direction, done);
+ element.addClass(direction);
+
+ $animateCss(element, {addClass: directionClass})
+ .start()
+ .done(removeClassFn);
+
+ return function() {
+ stopped = true;
+ };
+ }
+ done();
+ },
+ beforeRemoveClass: function (element, className, done) {
+ if (className === 'active') {
+ var stopped = false;
+ var direction = element.data(SLIDE_DIRECTION);
+ var directionClass = direction === 'next' ? 'left' : 'right';
+ var removeClassFn = removeClass.bind(this, element, directionClass, done);
+
+ $animateCss(element, {addClass: directionClass})
+ .start()
+ .done(removeClassFn);
+
+ return function() {
+ stopped = true;
+ };
+ }
+ done();
+ }
+ };
+}]);
+
+angular.module('ui.bootstrap.dateparser', [])
+
+.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) {
+ // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
+ var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
+
+ var localeId;
+ var formatCodeToRegex;
+
+ this.init = function() {
+ localeId = $locale.id;
+
+ this.parsers = {};
+ this.formatters = {};
+
+ formatCodeToRegex = [
+ {
+ key: 'yyyy',
+ regex: '\\d{4}',
+ apply: function(value) { this.year = +value; },
+ formatter: function(date) {
+ var _date = new Date();
+ _date.setFullYear(Math.abs(date.getFullYear()));
+ return dateFilter(_date, 'yyyy');
+ }
+ },
+ {
+ key: 'yy',
+ regex: '\\d{2}',
+ apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; },
+ formatter: function(date) {
+ var _date = new Date();
+ _date.setFullYear(Math.abs(date.getFullYear()));
+ return dateFilter(_date, 'yy');
+ }
+ },
+ {
+ key: 'y',
+ regex: '\\d{1,4}',
+ apply: function(value) { this.year = +value; },
+ formatter: function(date) {
+ var _date = new Date();
+ _date.setFullYear(Math.abs(date.getFullYear()));
+ return dateFilter(_date, 'y');
+ }
+ },
+ {
+ key: 'M!',
+ regex: '0?[1-9]|1[0-2]',
+ apply: function(value) { this.month = value - 1; },
+ formatter: function(date) {
+ var value = date.getMonth();
+ if (/^[0-9]$/.test(value)) {
+ return dateFilter(date, 'MM');
+ }
+
+ return dateFilter(date, 'M');
+ }
+ },
+ {
+ key: 'MMMM',
+ regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
+ apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); },
+ formatter: function(date) { return dateFilter(date, 'MMMM'); }
+ },
+ {
+ key: 'MMM',
+ regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
+ apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); },
+ formatter: function(date) { return dateFilter(date, 'MMM'); }
+ },
+ {
+ key: 'MM',
+ regex: '0[1-9]|1[0-2]',
+ apply: function(value) { this.month = value - 1; },
+ formatter: function(date) { return dateFilter(date, 'MM'); }
+ },
+ {
+ key: 'M',
+ regex: '[1-9]|1[0-2]',
+ apply: function(value) { this.month = value - 1; },
+ formatter: function(date) { return dateFilter(date, 'M'); }
+ },
+ {
+ key: 'd!',
+ regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
+ apply: function(value) { this.date = +value; },
+ formatter: function(date) {
+ var value = date.getDate();
+ if (/^[1-9]$/.test(value)) {
+ return dateFilter(date, 'dd');
+ }
+
+ return dateFilter(date, 'd');
+ }
+ },
+ {
+ key: 'dd',
+ regex: '[0-2][0-9]{1}|3[0-1]{1}',
+ apply: function(value) { this.date = +value; },
+ formatter: function(date) { return dateFilter(date, 'dd'); }
+ },
+ {
+ key: 'd',
+ regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
+ apply: function(value) { this.date = +value; },
+ formatter: function(date) { return dateFilter(date, 'd'); }
+ },
+ {
+ key: 'EEEE',
+ regex: $locale.DATETIME_FORMATS.DAY.join('|'),
+ formatter: function(date) { return dateFilter(date, 'EEEE'); }
+ },
+ {
+ key: 'EEE',
+ regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
+ formatter: function(date) { return dateFilter(date, 'EEE'); }
+ },
+ {
+ key: 'HH',
+ regex: '(?:0|1)[0-9]|2[0-3]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'HH'); }
+ },
+ {
+ key: 'hh',
+ regex: '0[0-9]|1[0-2]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'hh'); }
+ },
+ {
+ key: 'H',
+ regex: '1?[0-9]|2[0-3]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'H'); }
+ },
+ {
+ key: 'h',
+ regex: '[0-9]|1[0-2]',
+ apply: function(value) { this.hours = +value; },
+ formatter: function(date) { return dateFilter(date, 'h'); }
+ },
+ {
+ key: 'mm',
+ regex: '[0-5][0-9]',
+ apply: function(value) { this.minutes = +value; },
+ formatter: function(date) { return dateFilter(date, 'mm'); }
+ },
+ {
+ key: 'm',
+ regex: '[0-9]|[1-5][0-9]',
+ apply: function(value) { this.minutes = +value; },
+ formatter: function(date) { return dateFilter(date, 'm'); }
+ },
+ {
+ key: 'sss',
+ regex: '[0-9][0-9][0-9]',
+ apply: function(value) { this.milliseconds = +value; },
+ formatter: function(date) { return dateFilter(date, 'sss'); }
+ },
+ {
+ key: 'ss',
+ regex: '[0-5][0-9]',
+ apply: function(value) { this.seconds = +value; },
+ formatter: function(date) { return dateFilter(date, 'ss'); }
+ },
+ {
+ key: 's',
+ regex: '[0-9]|[1-5][0-9]',
+ apply: function(value) { this.seconds = +value; },
+ formatter: function(date) { return dateFilter(date, 's'); }
+ },
+ {
+ key: 'a',
+ regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
+ apply: function(value) {
+ if (this.hours === 12) {
+ this.hours = 0;
+ }
+
+ if (value === 'PM') {
+ this.hours += 12;
+ }
+ },
+ formatter: function(date) { return dateFilter(date, 'a'); }
+ },
+ {
+ key: 'Z',
+ regex: '[+-]\\d{4}',
+ apply: function(value) {
+ var matches = value.match(/([+-])(\d{2})(\d{2})/),
+ sign = matches[1],
+ hours = matches[2],
+ minutes = matches[3];
+ this.hours += toInt(sign + hours);
+ this.minutes += toInt(sign + minutes);
+ },
+ formatter: function(date) {
+ return dateFilter(date, 'Z');
+ }
+ },
+ {
+ key: 'ww',
+ regex: '[0-4][0-9]|5[0-3]',
+ formatter: function(date) { return dateFilter(date, 'ww'); }
+ },
+ {
+ key: 'w',
+ regex: '[0-9]|[1-4][0-9]|5[0-3]',
+ formatter: function(date) { return dateFilter(date, 'w'); }
+ },
+ {
+ key: 'GGGG',
+ regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'),
+ formatter: function(date) { return dateFilter(date, 'GGGG'); }
+ },
+ {
+ key: 'GGG',
+ regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
+ formatter: function(date) { return dateFilter(date, 'GGG'); }
+ },
+ {
+ key: 'GG',
+ regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
+ formatter: function(date) { return dateFilter(date, 'GG'); }
+ },
+ {
+ key: 'G',
+ regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
+ formatter: function(date) { return dateFilter(date, 'G'); }
+ }
+ ];
+ };
+
+ this.init();
+
+ function createParser(format, func) {
+ var map = [], regex = format.split('');
+
+ // check for literal values
+ var quoteIndex = format.indexOf('\'');
+ if (quoteIndex > -1) {
+ var inLiteral = false;
+ format = format.split('');
+ for (var i = quoteIndex; i < format.length; i++) {
+ if (inLiteral) {
+ if (format[i] === '\'') {
+ if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
+ format[i+1] = '$';
+ regex[i+1] = '';
+ } else { // end of literal
+ regex[i] = '';
+ inLiteral = false;
+ }
+ }
+ format[i] = '$';
+ } else {
+ if (format[i] === '\'') { // start of literal
+ format[i] = '$';
+ regex[i] = '';
+ inLiteral = true;
+ }
+ }
+ }
+
+ format = format.join('');
+ }
+
+ angular.forEach(formatCodeToRegex, function(data) {
+ var index = format.indexOf(data.key);
+
+ if (index > -1) {
+ format = format.split('');
+
+ regex[index] = '(' + data.regex + ')';
+ format[index] = '$'; // Custom symbol to define consumed part of format
+ for (var i = index + 1, n = index + data.key.length; i < n; i++) {
+ regex[i] = '';
+ format[i] = '$';
+ }
+ format = format.join('');
+
+ map.push({
+ index: index,
+ key: data.key,
+ apply: data[func],
+ matcher: data.regex
+ });
+ }
+ });
+
+ return {
+ regex: new RegExp('^' + regex.join('') + '$'),
+ map: orderByFilter(map, 'index')
+ };
+ }
+
+ this.filter = function(date, format) {
+ if (!angular.isDate(date) || isNaN(date) || !format) {
+ return '';
+ }
+
+ format = $locale.DATETIME_FORMATS[format] || format;
+
+ if ($locale.id !== localeId) {
+ this.init();
+ }
+
+ if (!this.formatters[format]) {
+ this.formatters[format] = createParser(format, 'formatter');
+ }
+
+ var parser = this.formatters[format],
+ map = parser.map;
+
+ var _format = format;
+
+ return map.reduce(function(str, mapper, i) {
+ var match = _format.match(new RegExp('(.*)' + mapper.key));
+ if (match && angular.isString(match[1])) {
+ str += match[1];
+ _format = _format.replace(match[1] + mapper.key, '');
+ }
+
+ var endStr = i === map.length - 1 ? _format : '';
+
+ if (mapper.apply) {
+ return str + mapper.apply.call(null, date) + endStr;
+ }
+
+ return str + endStr;
+ }, '');
+ };
+
+ this.parse = function(input, format, baseDate) {
+ if (!angular.isString(input) || !format) {
+ return input;
+ }
+
+ format = $locale.DATETIME_FORMATS[format] || format;
+ format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
+
+ if ($locale.id !== localeId) {
+ this.init();
+ }
+
+ if (!this.parsers[format]) {
+ this.parsers[format] = createParser(format, 'apply');
+ }
+
+ var parser = this.parsers[format],
+ regex = parser.regex,
+ map = parser.map,
+ results = input.match(regex),
+ tzOffset = false;
+ if (results && results.length) {
+ var fields, dt;
+ if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
+ fields = {
+ year: baseDate.getFullYear(),
+ month: baseDate.getMonth(),
+ date: baseDate.getDate(),
+ hours: baseDate.getHours(),
+ minutes: baseDate.getMinutes(),
+ seconds: baseDate.getSeconds(),
+ milliseconds: baseDate.getMilliseconds()
+ };
+ } else {
+ if (baseDate) {
+ $log.warn('dateparser:', 'baseDate is not a valid date');
+ }
+ fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
+ }
+
+ for (var i = 1, n = results.length; i < n; i++) {
+ var mapper = map[i - 1];
+ if (mapper.matcher === 'Z') {
+ tzOffset = true;
+ }
+
+ if (mapper.apply) {
+ mapper.apply.call(fields, results[i]);
+ }
+ }
+
+ var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
+ Date.prototype.setFullYear;
+ var timesetter = tzOffset ? Date.prototype.setUTCHours :
+ Date.prototype.setHours;
+
+ if (isValid(fields.year, fields.month, fields.date)) {
+ if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
+ dt = new Date(baseDate);
+ datesetter.call(dt, fields.year, fields.month, fields.date);
+ timesetter.call(dt, fields.hours, fields.minutes,
+ fields.seconds, fields.milliseconds);
+ } else {
+ dt = new Date(0);
+ datesetter.call(dt, fields.year, fields.month, fields.date);
+ timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
+ fields.seconds || 0, fields.milliseconds || 0);
+ }
+ }
+
+ return dt;
+ }
+ };
+
+ // Check if date is valid for specific month (and year for February).
+ // Month: 0 = Jan, 1 = Feb, etc
+ function isValid(year, month, date) {
+ if (date < 1) {
+ return false;
+ }
+
+ if (month === 1 && date > 28) {
+ return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
+ }
+
+ if (month === 3 || month === 5 || month === 8 || month === 10) {
+ return date < 31;
+ }
+
+ return true;
+ }
+
+ function toInt(str) {
+ return parseInt(str, 10);
+ }
+
+ this.toTimezone = toTimezone;
+ this.fromTimezone = fromTimezone;
+ this.timezoneToOffset = timezoneToOffset;
+ this.addDateMinutes = addDateMinutes;
+ this.convertTimezoneToLocal = convertTimezoneToLocal;
+
+ function toTimezone(date, timezone) {
+ return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
+ }
+
+ function fromTimezone(date, timezone) {
+ return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
+ }
+
+ //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207
+ function timezoneToOffset(timezone, fallback) {
+ timezone = timezone.replace(/:/g, '');
+ var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
+ return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
+ }
+
+ function addDateMinutes(date, minutes) {
+ date = new Date(date.getTime());
+ date.setMinutes(date.getMinutes() + minutes);
+ return date;
+ }
+
+ function convertTimezoneToLocal(date, timezone, reverse) {
+ reverse = reverse ? -1 : 1;
+ var dateTimezoneOffset = date.getTimezoneOffset();
+ var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
+ return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
+ }
+}]);
+
+// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
+// at most one element.
+angular.module('ui.bootstrap.isClass', [])
+.directive('uibIsClass', [
+ '$animate',
+function ($animate) {
+ // 11111111 22222222
+ var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
+ // 11111111 22222222
+ var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
+
+ var dataPerTracked = {};
+
+ return {
+ restrict: 'A',
+ compile: function(tElement, tAttrs) {
+ var linkedScopes = [];
+ var instances = [];
+ var expToData = {};
+ var lastActivated = null;
+ var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
+ var onExp = onExpMatches[2];
+ var expsStr = onExpMatches[1];
+ var exps = expsStr.split(',');
+
+ return linkFn;
+
+ function linkFn(scope, element, attrs) {
+ linkedScopes.push(scope);
+ instances.push({
+ scope: scope,
+ element: element
+ });
+
+ exps.forEach(function(exp, k) {
+ addForExp(exp, scope);
+ });
+
+ scope.$on('$destroy', removeScope);
+ }
+
+ function addForExp(exp, scope) {
+ var matches = exp.match(IS_REGEXP);
+ var clazz = scope.$eval(matches[1]);
+ var compareWithExp = matches[2];
+ var data = expToData[exp];
+ if (!data) {
+ var watchFn = function(compareWithVal) {
+ var newActivated = null;
+ instances.some(function(instance) {
+ var thisVal = instance.scope.$eval(onExp);
+ if (thisVal === compareWithVal) {
+ newActivated = instance;
+ return true;
+ }
+ });
+ if (data.lastActivated !== newActivated) {
+ if (data.lastActivated) {
+ $animate.removeClass(data.lastActivated.element, clazz);
+ }
+ if (newActivated) {
+ $animate.addClass(newActivated.element, clazz);
+ }
+ data.lastActivated = newActivated;
+ }
+ };
+ expToData[exp] = data = {
+ lastActivated: null,
+ scope: scope,
+ watchFn: watchFn,
+ compareWithExp: compareWithExp,
+ watcher: scope.$watch(compareWithExp, watchFn)
+ };
+ }
+ data.watchFn(scope.$eval(compareWithExp));
+ }
+
+ function removeScope(e) {
+ var removedScope = e.targetScope;
+ var index = linkedScopes.indexOf(removedScope);
+ linkedScopes.splice(index, 1);
+ instances.splice(index, 1);
+ if (linkedScopes.length) {
+ var newWatchScope = linkedScopes[0];
+ angular.forEach(expToData, function(data) {
+ if (data.scope === removedScope) {
+ data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
+ data.scope = newWatchScope;
+ }
+ });
+ } else {
+ expToData = {};
+ }
+ }
+ }
+ };
+}]);
+angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass'])
+
+.value('$datepickerSuppressError', false)
+
+.value('$datepickerLiteralWarning', true)
+
+.constant('uibDatepickerConfig', {
+ datepickerMode: 'day',
+ formatDay: 'dd',
+ formatMonth: 'MMMM',
+ formatYear: 'yyyy',
+ formatDayHeader: 'EEE',
+ formatDayTitle: 'MMMM yyyy',
+ formatMonthTitle: 'yyyy',
+ maxDate: null,
+ maxMode: 'year',
+ minDate: null,
+ minMode: 'day',
+ ngModelOptions: {},
+ shortcutPropagation: false,
+ showWeeks: true,
+ yearColumns: 5,
+ yearRows: 4
+})
+
+.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
+ function($scope, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
+ var self = this,
+ ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
+ ngModelOptions = {},
+ watchListeners = [],
+ optionsUsed = !!$attrs.datepickerOptions;
+
+ if (!$scope.datepickerOptions) {
+ $scope.datepickerOptions = {};
+ }
+
+ // Modes chain
+ this.modes = ['day', 'month', 'year'];
+
+ [
+ 'customClass',
+ 'dateDisabled',
+ 'datepickerMode',
+ 'formatDay',
+ 'formatDayHeader',
+ 'formatDayTitle',
+ 'formatMonth',
+ 'formatMonthTitle',
+ 'formatYear',
+ 'maxDate',
+ 'maxMode',
+ 'minDate',
+ 'minMode',
+ 'showWeeks',
+ 'shortcutPropagation',
+ 'startingDay',
+ 'yearColumns',
+ 'yearRows'
+ ].forEach(function(key) {
+ switch (key) {
+ case 'customClass':
+ case 'dateDisabled':
+ $scope[key] = $scope.datepickerOptions[key] || angular.noop;
+ break;
+ case 'datepickerMode':
+ $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ?
+ $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode;
+ break;
+ case 'formatDay':
+ case 'formatDayHeader':
+ case 'formatDayTitle':
+ case 'formatMonth':
+ case 'formatMonthTitle':
+ case 'formatYear':
+ self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
+ $interpolate($scope.datepickerOptions[key])($scope.$parent) :
+ datepickerConfig[key];
+ break;
+ case 'showWeeks':
+ case 'shortcutPropagation':
+ case 'yearColumns':
+ case 'yearRows':
+ self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
+ $scope.datepickerOptions[key] : datepickerConfig[key];
+ break;
+ case 'startingDay':
+ if (angular.isDefined($scope.datepickerOptions.startingDay)) {
+ self.startingDay = $scope.datepickerOptions.startingDay;
+ } else if (angular.isNumber(datepickerConfig.startingDay)) {
+ self.startingDay = datepickerConfig.startingDay;
+ } else {
+ self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7;
+ }
+
+ break;
+ case 'maxDate':
+ case 'minDate':
+ $scope.$watch('datepickerOptions.' + key, function(value) {
+ if (value) {
+ if (angular.isDate(value)) {
+ self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
+ } else {
+ if ($datepickerLiteralWarning) {
+ $log.warn('Literal date support has been deprecated, please switch to date object usage');
+ }
+
+ self[key] = new Date(dateFilter(value, 'medium'));
+ }
+ } else {
+ self[key] = datepickerConfig[key] ?
+ dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) :
+ null;
+ }
+
+ self.refreshView();
+ });
+
+ break;
+ case 'maxMode':
+ case 'minMode':
+ if ($scope.datepickerOptions[key]) {
+ $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) {
+ self[key] = $scope[key] = angular.isDefined(value) ? value : datepickerOptions[key];
+ if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) ||
+ key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) {
+ $scope.datepickerMode = self[key];
+ $scope.datepickerOptions.datepickerMode = self[key];
+ }
+ });
+ } else {
+ self[key] = $scope[key] = datepickerConfig[key] || null;
+ }
+
+ break;
+ }
+ });
+
+ $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
+
+ $scope.disabled = angular.isDefined($attrs.disabled) || false;
+ if (angular.isDefined($attrs.ngDisabled)) {
+ watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
+ $scope.disabled = disabled;
+ self.refreshView();
+ }));
+ }
+
+ $scope.isActive = function(dateObject) {
+ if (self.compare(dateObject.date, self.activeDate) === 0) {
+ $scope.activeDateId = dateObject.uid;
+ return true;
+ }
+ return false;
+ };
+
+ this.init = function(ngModelCtrl_) {
+ ngModelCtrl = ngModelCtrl_;
+ ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
+ if ($scope.datepickerOptions.initDate) {
+ self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date();
+ $scope.$watch('datepickerOptions.initDate', function(initDate) {
+ if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
+ self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
+ self.refreshView();
+ }
+ });
+ } else {
+ self.activeDate = new Date();
+ }
+
+ var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date();
+ this.activeDate = !isNaN(date) ?
+ dateParser.fromTimezone(date, ngModelOptions.timezone) :
+ dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
+
+ ngModelCtrl.$render = function() {
+ self.render();
+ };
+ };
+
+ this.render = function() {
+ if (ngModelCtrl.$viewValue) {
+ var date = new Date(ngModelCtrl.$viewValue),
+ isValid = !isNaN(date);
+
+ if (isValid) {
+ this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
+ } else if (!$datepickerSuppressError) {
+ $log.error('Datepicker directive: "ng-model" value must be a Date object');
+ }
+ }
+ this.refreshView();
+ };
+
+ this.refreshView = function() {
+ if (this.element) {
+ $scope.selectedDt = null;
+ this._refreshView();
+ if ($scope.activeDt) {
+ $scope.activeDateId = $scope.activeDt.uid;
+ }
+
+ var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
+ date = dateParser.fromTimezone(date, ngModelOptions.timezone);
+ ngModelCtrl.$setValidity('dateDisabled', !date ||
+ this.element && !this.isDisabled(date));
+ }
+ };
+
+ this.createDateObject = function(date, format) {
+ var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
+ model = dateParser.fromTimezone(model, ngModelOptions.timezone);
+ var today = new Date();
+ today = dateParser.fromTimezone(today, ngModelOptions.timezone);
+ var time = this.compare(date, today);
+ var dt = {
+ date: date,
+ label: dateParser.filter(date, format),
+ selected: model && this.compare(date, model) === 0,
+ disabled: this.isDisabled(date),
+ past: time < 0,
+ current: time === 0,
+ future: time > 0,
+ customClass: this.customClass(date) || null
+ };
+
+ if (model && this.compare(date, model) === 0) {
+ $scope.selectedDt = dt;
+ }
+
+ if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
+ $scope.activeDt = dt;
+ }
+
+ return dt;
+ };
+
+ this.isDisabled = function(date) {
+ return $scope.disabled ||
+ this.minDate && this.compare(date, this.minDate) < 0 ||
+ this.maxDate && this.compare(date, this.maxDate) > 0 ||
+ $scope.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
+ };
+
+ this.customClass = function(date) {
+ return $scope.customClass({date: date, mode: $scope.datepickerMode});
+ };
+
+ // Split array into smaller arrays
+ this.split = function(arr, size) {
+ var arrays = [];
+ while (arr.length > 0) {
+ arrays.push(arr.splice(0, size));
+ }
+ return arrays;
+ };
+
+ $scope.select = function(date) {
+ if ($scope.datepickerMode === self.minMode) {
+ var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
+ dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
+ dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
+ ngModelCtrl.$setViewValue(dt);
+ ngModelCtrl.$render();
+ } else {
+ self.activeDate = date;
+ setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]);
+
+ $scope.$emit('uib:datepicker.mode');
+ }
+
+ $scope.$broadcast('uib:datepicker.focus');
+ };
+
+ $scope.move = function(direction) {
+ var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
+ month = self.activeDate.getMonth() + direction * (self.step.months || 0);
+ self.activeDate.setFullYear(year, month, 1);
+ self.refreshView();
+ };
+
+ $scope.toggleMode = function(direction) {
+ direction = direction || 1;
+
+ if ($scope.datepickerMode === self.maxMode && direction === 1 ||
+ $scope.datepickerMode === self.minMode && direction === -1) {
+ return;
+ }
+
+ setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]);
+
+ $scope.$emit('uib:datepicker.mode');
+ };
+
+ // Key event mapper
+ $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
+
+ var focusElement = function() {
+ self.element[0].focus();
+ };
+
+ // Listen for focus requests from popup directive
+ $scope.$on('uib:datepicker.focus', focusElement);
+
+ $scope.keydown = function(evt) {
+ var key = $scope.keys[evt.which];
+
+ if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
+ return;
+ }
+
+ evt.preventDefault();
+ if (!self.shortcutPropagation) {
+ evt.stopPropagation();
+ }
+
+ if (key === 'enter' || key === 'space') {
+ if (self.isDisabled(self.activeDate)) {
+ return; // do nothing
+ }
+ $scope.select(self.activeDate);
+ } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
+ $scope.toggleMode(key === 'up' ? 1 : -1);
+ } else {
+ self.handleKeyDown(key, evt);
+ self.refreshView();
+ }
+ };
+
+ $scope.$on('$destroy', function() {
+ //Clear all watch listeners on destroy
+ while (watchListeners.length) {
+ watchListeners.shift()();
+ }
+ });
+
+ function setMode(mode) {
+ $scope.datepickerMode = mode;
+ $scope.datepickerOptions.datepickerMode = mode;
+ }
+}])
+
+.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+ this.step = { months: 1 };
+ this.element = $element;
+ function getDaysInMonth(year, month) {
+ return month === 1 && year % 4 === 0 &&
+ (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
+ }
+
+ this.init = function(ctrl) {
+ angular.extend(ctrl, this);
+ scope.showWeeks = ctrl.showWeeks;
+ ctrl.refreshView();
+ };
+
+ this.getDates = function(startDate, n) {
+ var dates = new Array(n), current = new Date(startDate), i = 0, date;
+ while (i < n) {
+ date = new Date(current);
+ dates[i++] = date;
+ current.setDate(current.getDate() + 1);
+ }
+ return dates;
+ };
+
+ this._refreshView = function() {
+ var year = this.activeDate.getFullYear(),
+ month = this.activeDate.getMonth(),
+ firstDayOfMonth = new Date(this.activeDate);
+
+ firstDayOfMonth.setFullYear(year, month, 1);
+
+ var difference = this.startingDay - firstDayOfMonth.getDay(),
+ numDisplayedFromPreviousMonth = difference > 0 ?
+ 7 - difference : - difference,
+ firstDate = new Date(firstDayOfMonth);
+
+ if (numDisplayedFromPreviousMonth > 0) {
+ firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
+ }
+
+ // 42 is the number of days on a six-week calendar
+ var days = this.getDates(firstDate, 42);
+ for (var i = 0; i < 42; i ++) {
+ days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
+ secondary: days[i].getMonth() !== month,
+ uid: scope.uniqueId + '-' + i
+ });
+ }
+
+ scope.labels = new Array(7);
+ for (var j = 0; j < 7; j++) {
+ scope.labels[j] = {
+ abbr: dateFilter(days[j].date, this.formatDayHeader),
+ full: dateFilter(days[j].date, 'EEEE')
+ };
+ }
+
+ scope.title = dateFilter(this.activeDate, this.formatDayTitle);
+ scope.rows = this.split(days, 7);
+
+ if (scope.showWeeks) {
+ scope.weekNumbers = [];
+ var thursdayIndex = (4 + 7 - this.startingDay) % 7,
+ numWeeks = scope.rows.length;
+ for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
+ scope.weekNumbers.push(
+ getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
+ }
+ }
+ };
+
+ this.compare = function(date1, date2) {
+ var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
+ var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
+ _date1.setFullYear(date1.getFullYear());
+ _date2.setFullYear(date2.getFullYear());
+ return _date1 - _date2;
+ };
+
+ function getISO8601WeekNumber(date) {
+ var checkDate = new Date(date);
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
+ var time = checkDate.getTime();
+ checkDate.setMonth(0); // Compare with Jan 1
+ checkDate.setDate(1);
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
+ }
+
+ this.handleKeyDown = function(key, evt) {
+ var date = this.activeDate.getDate();
+
+ if (key === 'left') {
+ date = date - 1;
+ } else if (key === 'up') {
+ date = date - 7;
+ } else if (key === 'right') {
+ date = date + 1;
+ } else if (key === 'down') {
+ date = date + 7;
+ } else if (key === 'pageup' || key === 'pagedown') {
+ var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
+ this.activeDate.setMonth(month, 1);
+ date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
+ } else if (key === 'home') {
+ date = 1;
+ } else if (key === 'end') {
+ date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
+ }
+ this.activeDate.setDate(date);
+ };
+}])
+
+.controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
+ this.step = { years: 1 };
+ this.element = $element;
+
+ this.init = function(ctrl) {
+ angular.extend(ctrl, this);
+ ctrl.refreshView();
+ };
+
+ this._refreshView = function() {
+ var months = new Array(12),
+ year = this.activeDate.getFullYear(),
+ date;
+
+ for (var i = 0; i < 12; i++) {
+ date = new Date(this.activeDate);
+ date.setFullYear(year, i, 1);
+ months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
+ uid: scope.uniqueId + '-' + i
+ });
+ }
+
+ scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
+ scope.rows = this.split(months, 3);
+ };
+
+ this.compare = function(date1, date2) {
+ var _date1 = new Date(date1.getFullYear(), date1.getMonth());
+ var _date2 = new Date(date2.getFullYear(), date2.getMonth());
+ _date1.setFullYear(date1.getFullYear());
+ _date2.setFullYear(date2.getFullYear());
+ return _date1 - _date2;
+ };
+
+ this.handleKeyDown = function(key, evt) {
+ var date = this.activeDate.getMonth();
+
+ if (key === 'left') {
+ date = date - 1;
+ } else if (key === 'up') {
+ date = date - 3;
+ } else if (key === 'right') {
+ date = date + 1;
+ } else if (key === 'down') {
+ date = date + 3;
+ } else if (key === 'pageup' || key === 'pagedown') {
+ var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
+ this.activeDate.setFullYear(year);
+ } else if (key === 'home') {
+ date = 0;
+ } else if (key === 'end') {
+ date = 11;
+ }
+ this.activeDate.setMonth(date);
+ };
+}])
+
+.controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
+ var columns, range;
+ this.element = $element;
+
+ function getStartingYear(year) {
+ return parseInt((year - 1) / range, 10) * range + 1;
+ }
+
+ this.yearpickerInit = function() {
+ columns = this.yearColumns;
+ range = this.yearRows * columns;
+ this.step = { years: range };
+ };
+
+ this._refreshView = function() {
+ var years = new Array(range), date;
+
+ for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
+ date = new Date(this.activeDate);
+ date.setFullYear(start + i, 0, 1);
+ years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
+ uid: scope.uniqueId + '-' + i
+ });
+ }
+
+ scope.title = [years[0].label, years[range - 1].label].join(' - ');
+ scope.rows = this.split(years, columns);
+ scope.columns = columns;
+ };
+
+ this.compare = function(date1, date2) {
+ return date1.getFullYear() - date2.getFullYear();
+ };
+
+ this.handleKeyDown = function(key, evt) {
+ var date = this.activeDate.getFullYear();
+
+ if (key === 'left') {
+ date = date - 1;
+ } else if (key === 'up') {
+ date = date - columns;
+ } else if (key === 'right') {
+ date = date + 1;
+ } else if (key === 'down') {
+ date = date + columns;
+ } else if (key === 'pageup' || key === 'pagedown') {
+ date += (key === 'pageup' ? - 1 : 1) * range;
+ } else if (key === 'home') {
+ date = getStartingYear(this.activeDate.getFullYear());
+ } else if (key === 'end') {
+ date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
+ }
+ this.activeDate.setFullYear(date);
+ };
+}])
+
+.directive('uibDatepicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
+ },
+ scope: {
+ datepickerOptions: '=?'
+ },
+ require: ['uibDatepicker', '^ngModel'],
+ controller: 'UibDatepickerController',
+ controllerAs: 'datepicker',
+ link: function(scope, element, attrs, ctrls) {
+ var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ datepickerCtrl.init(ngModelCtrl);
+ }
+ };
+})
+
+.directive('uibDaypicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/day.html';
+ },
+ require: ['^uibDatepicker', 'uibDaypicker'],
+ controller: 'UibDaypickerController',
+ link: function(scope, element, attrs, ctrls) {
+ var datepickerCtrl = ctrls[0],
+ daypickerCtrl = ctrls[1];
+
+ daypickerCtrl.init(datepickerCtrl);
+ }
+ };
+})
+
+.directive('uibMonthpicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/month.html';
+ },
+ require: ['^uibDatepicker', 'uibMonthpicker'],
+ controller: 'UibMonthpickerController',
+ link: function(scope, element, attrs, ctrls) {
+ var datepickerCtrl = ctrls[0],
+ monthpickerCtrl = ctrls[1];
+
+ monthpickerCtrl.init(datepickerCtrl);
+ }
+ };
+})
+
+.directive('uibYearpicker', function() {
+ return {
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/datepicker/year.html';
+ },
+ require: ['^uibDatepicker', 'uibYearpicker'],
+ controller: 'UibYearpickerController',
+ link: function(scope, element, attrs, ctrls) {
+ var ctrl = ctrls[0];
+ angular.extend(ctrl, ctrls[1]);
+ ctrl.yearpickerInit();
+
+ ctrl.refreshView();
+ }
+ };
+});
+
+angular.module('ui.bootstrap.position', [])
+
+/**
+ * A set of utility methods for working with the DOM.
+ * It is meant to be used where we need to absolute-position elements in
+ * relation to another element (this is the case for tooltips, popovers,
+ * typeahead suggestions etc.).
+ */
+ .factory('$uibPosition', ['$document', '$window', function($document, $window) {
+ /**
+ * Used by scrollbarWidth() function to cache scrollbar's width.
+ * Do not access this variable directly, use scrollbarWidth() instead.
+ */
+ var SCROLLBAR_WIDTH;
+ /**
+ * scrollbar on body and html element in IE and Edge overlay
+ * content and should be considered 0 width.
+ */
+ var BODY_SCROLLBAR_WIDTH;
+ var OVERFLOW_REGEX = {
+ normal: /(auto|scroll)/,
+ hidden: /(auto|scroll|hidden)/
+ };
+ var PLACEMENT_REGEX = {
+ auto: /\s?auto?\s?/i,
+ primary: /^(top|bottom|left|right)$/,
+ secondary: /^(top|bottom|left|right|center)$/,
+ vertical: /^(top|bottom)$/
+ };
+ var BODY_REGEX = /(HTML|BODY)/;
+
+ return {
+
+ /**
+ * Provides a raw DOM element from a jQuery/jQLite element.
+ *
+ * @param {element} elem - The element to convert.
+ *
+ * @returns {element} A HTML element.
+ */
+ getRawNode: function(elem) {
+ return elem.nodeName ? elem : elem[0] || elem;
+ },
+
+ /**
+ * Provides a parsed number for a style property. Strips
+ * units and casts invalid numbers to 0.
+ *
+ * @param {string} value - The style value to parse.
+ *
+ * @returns {number} A valid number.
+ */
+ parseStyle: function(value) {
+ value = parseFloat(value);
+ return isFinite(value) ? value : 0;
+ },
+
+ /**
+ * Provides the closest positioned ancestor.
+ *
+ * @param {element} element - The element to get the offest parent for.
+ *
+ * @returns {element} The closest positioned ancestor.
+ */
+ offsetParent: function(elem) {
+ elem = this.getRawNode(elem);
+
+ var offsetParent = elem.offsetParent || $document[0].documentElement;
+
+ function isStaticPositioned(el) {
+ return ($window.getComputedStyle(el).position || 'static') === 'static';
+ }
+
+ while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
+ offsetParent = offsetParent.offsetParent;
+ }
+
+ return offsetParent || $document[0].documentElement;
+ },
+
+ /**
+ * Provides the scrollbar width, concept from TWBS measureScrollbar()
+ * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
+ * In IE and Edge, scollbar on body and html element overlay and should
+ * return a width of 0.
+ *
+ * @returns {number} The width of the browser scollbar.
+ */
+ scrollbarWidth: function(isBody) {
+ if (isBody) {
+ if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) {
+ var bodyElem = $document.find('body');
+ bodyElem.addClass('uib-position-body-scrollbar-measure');
+ BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth;
+ BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0;
+ bodyElem.removeClass('uib-position-body-scrollbar-measure');
+ }
+ return BODY_SCROLLBAR_WIDTH;
+ }
+
+ if (angular.isUndefined(SCROLLBAR_WIDTH)) {
+ var scrollElem = angular.element('');
+ $document.find('body').append(scrollElem);
+ SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
+ SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
+ scrollElem.remove();
+ }
+
+ return SCROLLBAR_WIDTH;
+ },
+
+ /**
+ * Provides the padding required on an element to replace the scrollbar.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**scrollbarWidth**: the width of the scrollbar
+ *
**widthOverflow**: whether the the width is overflowing
+ *
**right**: the amount of right padding on the element needed to replace the scrollbar
+ *
**rightOriginal**: the amount of right padding currently on the element
+ *
**heightOverflow**: whether the the height is overflowing
+ *
**bottom**: the amount of bottom padding on the element needed to replace the scrollbar
+ *
**bottomOriginal**: the amount of bottom padding currently on the element
+ *
+ */
+ scrollbarPadding: function(elem) {
+ elem = this.getRawNode(elem);
+
+ var elemStyle = $window.getComputedStyle(elem);
+ var paddingRight = this.parseStyle(elemStyle.paddingRight);
+ var paddingBottom = this.parseStyle(elemStyle.paddingBottom);
+ var scrollParent = this.scrollParent(elem, false, true);
+ var scrollbarWidth = this.scrollbarWidth(scrollParent, BODY_REGEX.test(scrollParent.tagName));
+
+ return {
+ scrollbarWidth: scrollbarWidth,
+ widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth,
+ right: paddingRight + scrollbarWidth,
+ originalRight: paddingRight,
+ heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight,
+ bottom: paddingBottom + scrollbarWidth,
+ originalBottom: paddingBottom
+ };
+ },
+
+ /**
+ * Checks to see if the element is scrollable.
+ *
+ * @param {element} elem - The element to check.
+ * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
+ * default is false.
+ *
+ * @returns {boolean} Whether the element is scrollable.
+ */
+ isScrollable: function(elem, includeHidden) {
+ elem = this.getRawNode(elem);
+
+ var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
+ var elemStyle = $window.getComputedStyle(elem);
+ return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX);
+ },
+
+ /**
+ * Provides the closest scrollable ancestor.
+ * A port of the jQuery UI scrollParent method:
+ * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
+ *
+ * @param {element} elem - The element to find the scroll parent of.
+ * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
+ * default is false.
+ * @param {boolean=} [includeSelf=false] - Should the element being passed be
+ * included in the scrollable llokup.
+ *
+ * @returns {element} A HTML element.
+ */
+ scrollParent: function(elem, includeHidden, includeSelf) {
+ elem = this.getRawNode(elem);
+
+ var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
+ var documentEl = $document[0].documentElement;
+ var elemStyle = $window.getComputedStyle(elem);
+ if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) {
+ return elem;
+ }
+ var excludeStatic = elemStyle.position === 'absolute';
+ var scrollParent = elem.parentElement || documentEl;
+
+ if (scrollParent === documentEl || elemStyle.position === 'fixed') {
+ return documentEl;
+ }
+
+ while (scrollParent.parentElement && scrollParent !== documentEl) {
+ var spStyle = $window.getComputedStyle(scrollParent);
+ if (excludeStatic && spStyle.position !== 'static') {
+ excludeStatic = false;
+ }
+
+ if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
+ break;
+ }
+ scrollParent = scrollParent.parentElement;
+ }
+
+ return scrollParent;
+ },
+
+ /**
+ * Provides read-only equivalent of jQuery's position function:
+ * http://api.jquery.com/position/ - distance to closest positioned
+ * ancestor. Does not account for margins by default like jQuery position.
+ *
+ * @param {element} elem - The element to caclulate the position on.
+ * @param {boolean=} [includeMargins=false] - Should margins be accounted
+ * for, default is false.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**width**: the width of the element
+ *
**height**: the height of the element
+ *
**top**: distance to top edge of offset parent
+ *
**left**: distance to left edge of offset parent
+ *
+ */
+ position: function(elem, includeMagins) {
+ elem = this.getRawNode(elem);
+
+ var elemOffset = this.offset(elem);
+ if (includeMagins) {
+ var elemStyle = $window.getComputedStyle(elem);
+ elemOffset.top -= this.parseStyle(elemStyle.marginTop);
+ elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
+ }
+ var parent = this.offsetParent(elem);
+ var parentOffset = {top: 0, left: 0};
+
+ if (parent !== $document[0].documentElement) {
+ parentOffset = this.offset(parent);
+ parentOffset.top += parent.clientTop - parent.scrollTop;
+ parentOffset.left += parent.clientLeft - parent.scrollLeft;
+ }
+
+ return {
+ width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
+ height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
+ top: Math.round(elemOffset.top - parentOffset.top),
+ left: Math.round(elemOffset.left - parentOffset.left)
+ };
+ },
+
+ /**
+ * Provides read-only equivalent of jQuery's offset function:
+ * http://api.jquery.com/offset/ - distance to viewport. Does
+ * not account for borders, margins, or padding on the body
+ * element.
+ *
+ * @param {element} elem - The element to calculate the offset on.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**width**: the width of the element
+ *
**height**: the height of the element
+ *
**top**: distance to top edge of viewport
+ *
**right**: distance to bottom edge of viewport
+ *
+ */
+ offset: function(elem) {
+ elem = this.getRawNode(elem);
+
+ var elemBCR = elem.getBoundingClientRect();
+ return {
+ width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
+ height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
+ top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
+ left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
+ };
+ },
+
+ /**
+ * Provides offset distance to the closest scrollable ancestor
+ * or viewport. Accounts for border and scrollbar width.
+ *
+ * Right and bottom dimensions represent the distance to the
+ * respective edge of the viewport element. If the element
+ * edge extends beyond the viewport, a negative value will be
+ * reported.
+ *
+ * @param {element} elem - The element to get the viewport offset for.
+ * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
+ * of the first scrollable element, default is false.
+ * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
+ * be accounted for, default is true.
+ *
+ * @returns {object} An object with the following properties:
+ *
+ *
**top**: distance to the top content edge of viewport element
+ *
**bottom**: distance to the bottom content edge of viewport element
+ *
**left**: distance to the left content edge of viewport element
+ *
**right**: distance to the right content edge of viewport element
top: element on top, horizontally centered on host element.
+ *
top-left: element on top, left edge aligned with host element left edge.
+ *
top-right: element on top, lerightft edge aligned with host element right edge.
+ *
bottom: element on bottom, horizontally centered on host element.
+ *
bottom-left: element on bottom, left edge aligned with host element left edge.
+ *
bottom-right: element on bottom, right edge aligned with host element right edge.
+ *
left: element on left, vertically centered on host element.
+ *
left-top: element on left, top edge aligned with host element top edge.
+ *
left-bottom: element on left, bottom edge aligned with host element bottom edge.
+ *
right: element on right, vertically centered on host element.
+ *
right-top: element on right, top edge aligned with host element top edge.
+ *
right-bottom: element on right, bottom edge aligned with host element bottom edge.
+ *
+ * A placement string with an 'auto' indicator is expected to be
+ * space separated from the placement, i.e: 'auto bottom-left' If
+ * the primary and secondary placement values do not match 'top,
+ * bottom, left, right' then 'top' will be the primary placement and
+ * 'center' will be the secondary placement. If 'auto' is passed, true
+ * will be returned as the 3rd value of the array.
+ *
+ * @param {string} placement - The placement string to parse.
+ *
+ * @returns {array} An array with the following values
+ *
+ *
**[0]**: The primary placement.
+ *
**[1]**: The secondary placement.
+ *
**[2]**: If auto is passed: true, else undefined.
+ *
+ */
+ parsePlacement: function(placement) {
+ var autoPlace = PLACEMENT_REGEX.auto.test(placement);
+ if (autoPlace) {
+ placement = placement.replace(PLACEMENT_REGEX.auto, '');
+ }
+
+ placement = placement.split('-');
+
+ placement[0] = placement[0] || 'top';
+ if (!PLACEMENT_REGEX.primary.test(placement[0])) {
+ placement[0] = 'top';
+ }
+
+ placement[1] = placement[1] || 'center';
+ if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
+ placement[1] = 'center';
+ }
+
+ if (autoPlace) {
+ placement[2] = true;
+ } else {
+ placement[2] = false;
+ }
+
+ return placement;
+ },
+
+ /**
+ * Provides coordinates for an element to be positioned relative to
+ * another element. Passing 'auto' as part of the placement parameter
+ * will enable smart placement - where the element fits. i.e:
+ * 'auto left-top' will check to see if there is enough space to the left
+ * of the hostElem to fit the targetElem, if not place right (same for secondary
+ * top placement). Available space is calculated using the viewportOffset
+ * function.
+ *
+ * @param {element} hostElem - The element to position against.
+ * @param {element} targetElem - The element to position.
+ * @param {string=} [placement=top] - The placement for the targetElem,
+ * default is 'top'. 'center' is assumed as secondary placement for
+ * 'top', 'left', 'right', and 'bottom' placements. Available placements are:
+ *
+ *
top
+ *
top-right
+ *
top-left
+ *
bottom
+ *
bottom-left
+ *
bottom-right
+ *
left
+ *
left-top
+ *
left-bottom
+ *
right
+ *
right-top
+ *
right-bottom
+ *
+ * @param {boolean=} [appendToBody=false] - Should the top and left values returned
+ * be calculated from the body element, default is false.
+ *
+ * @returns {object} An object with the following properties:
+ *
');
+ self.dropdownMenu.replaceWith(newEl);
+ self.dropdownMenu = newEl;
+ }
+
+ uibDropdownService.close(scope, $element);
+ self.selectedOption = null;
+ }
+
+ if (angular.isFunction(setIsOpen)) {
+ setIsOpen($scope, isOpen);
+ }
+ });
+}])
+
+.directive('uibDropdown', function() {
+ return {
+ controller: 'UibDropdownController',
+ link: function(scope, element, attrs, dropdownCtrl) {
+ dropdownCtrl.init();
+ }
+ };
+})
+
+.directive('uibDropdownMenu', function() {
+ return {
+ restrict: 'A',
+ require: '?^uibDropdown',
+ link: function(scope, element, attrs, dropdownCtrl) {
+ if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
+ return;
+ }
+
+ element.addClass('dropdown-menu');
+
+ var tplUrl = attrs.templateUrl;
+ if (tplUrl) {
+ dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
+ }
+
+ if (!dropdownCtrl.dropdownMenu) {
+ dropdownCtrl.dropdownMenu = element;
+ }
+ }
+ };
+})
+
+.directive('uibDropdownToggle', function() {
+ return {
+ require: '?^uibDropdown',
+ link: function(scope, element, attrs, dropdownCtrl) {
+ if (!dropdownCtrl) {
+ return;
+ }
+
+ element.addClass('dropdown-toggle');
+
+ dropdownCtrl.toggleElement = element;
+
+ var toggleDropdown = function(event) {
+ event.preventDefault();
+
+ if (!element.hasClass('disabled') && !attrs.disabled) {
+ scope.$apply(function() {
+ dropdownCtrl.toggle();
+ });
+ }
+ };
+
+ element.bind('click', toggleDropdown);
+
+ // WAI-ARIA
+ element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
+ scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
+ element.attr('aria-expanded', !!isOpen);
+ });
+
+ scope.$on('$destroy', function() {
+ element.unbind('click', toggleDropdown);
+ });
+ }
+ };
+});
+
+angular.module('ui.bootstrap.stackedMap', [])
+/**
+ * A helper, internal data structure that acts as a map but also allows getting / removing
+ * elements in the LIFO order
+ */
+ .factory('$$stackedMap', function() {
+ return {
+ createNew: function() {
+ var stack = [];
+
+ return {
+ add: function(key, value) {
+ stack.push({
+ key: key,
+ value: value
+ });
+ },
+ get: function(key) {
+ for (var i = 0; i < stack.length; i++) {
+ if (key === stack[i].key) {
+ return stack[i];
+ }
+ }
+ },
+ keys: function() {
+ var keys = [];
+ for (var i = 0; i < stack.length; i++) {
+ keys.push(stack[i].key);
+ }
+ return keys;
+ },
+ top: function() {
+ return stack[stack.length - 1];
+ },
+ remove: function(key) {
+ var idx = -1;
+ for (var i = 0; i < stack.length; i++) {
+ if (key === stack[i].key) {
+ idx = i;
+ break;
+ }
+ }
+ return stack.splice(idx, 1)[0];
+ },
+ removeTop: function() {
+ return stack.splice(stack.length - 1, 1)[0];
+ },
+ length: function() {
+ return stack.length;
+ }
+ };
+ }
+ };
+ });
+angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
+/**
+ * A helper, internal data structure that stores all references attached to key
+ */
+ .factory('$$multiMap', function() {
+ return {
+ createNew: function() {
+ var map = {};
+
+ return {
+ entries: function() {
+ return Object.keys(map).map(function(key) {
+ return {
+ key: key,
+ value: map[key]
+ };
+ });
+ },
+ get: function(key) {
+ return map[key];
+ },
+ hasKey: function(key) {
+ return !!map[key];
+ },
+ keys: function() {
+ return Object.keys(map);
+ },
+ put: function(key, value) {
+ if (!map[key]) {
+ map[key] = [];
+ }
+
+ map[key].push(value);
+ },
+ remove: function(key, value) {
+ var values = map[key];
+
+ if (!values) {
+ return;
+ }
+
+ var idx = values.indexOf(value);
+
+ if (idx !== -1) {
+ values.splice(idx, 1);
+ }
+
+ if (!values.length) {
+ delete map[key];
+ }
+ }
+ };
+ }
+ };
+ })
+
+/**
+ * Pluggable resolve mechanism for the modal resolve resolution
+ * Supports UI Router's $resolve service
+ */
+ .provider('$uibResolve', function() {
+ var resolve = this;
+ this.resolver = null;
+
+ this.setResolver = function(resolver) {
+ this.resolver = resolver;
+ };
+
+ this.$get = ['$injector', '$q', function($injector, $q) {
+ var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null;
+ return {
+ resolve: function(invocables, locals, parent, self) {
+ if (resolver) {
+ return resolver.resolve(invocables, locals, parent, self);
+ }
+
+ var promises = [];
+
+ angular.forEach(invocables, function(value) {
+ if (angular.isFunction(value) || angular.isArray(value)) {
+ promises.push($q.resolve($injector.invoke(value)));
+ } else if (angular.isString(value)) {
+ promises.push($q.resolve($injector.get(value)));
+ } else {
+ promises.push($q.resolve(value));
+ }
+ });
+
+ return $q.all(promises).then(function(resolves) {
+ var resolveObj = {};
+ var resolveIter = 0;
+ angular.forEach(invocables, function(value, key) {
+ resolveObj[key] = resolves[resolveIter++];
+ });
+
+ return resolveObj;
+ });
+ }
+ };
+ }];
+ })
+
+/**
+ * A helper directive for the $modal service. It creates a backdrop element.
+ */
+ .directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack',
+ function($animate, $injector, $modalStack) {
+ return {
+ replace: true,
+ templateUrl: 'uib/template/modal/backdrop.html',
+ compile: function(tElement, tAttrs) {
+ tElement.addClass(tAttrs.backdropClass);
+ return linkFn;
+ }
+ };
+
+ function linkFn(scope, element, attrs) {
+ if (attrs.modalInClass) {
+ $animate.addClass(element, attrs.modalInClass);
+
+ scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
+ var done = setIsAsync();
+ if (scope.modalOptions.animation) {
+ $animate.removeClass(element, attrs.modalInClass).then(done);
+ } else {
+ done();
+ }
+ });
+ }
+ }
+ }])
+
+ .directive('uibModalWindow', ['$uibModalStack', '$q', '$animateCss', '$document',
+ function($modalStack, $q, $animateCss, $document) {
+ return {
+ scope: {
+ index: '@'
+ },
+ replace: true,
+ transclude: true,
+ templateUrl: function(tElement, tAttrs) {
+ return tAttrs.templateUrl || 'uib/template/modal/window.html';
+ },
+ link: function(scope, element, attrs) {
+ element.addClass(attrs.windowClass || '');
+ element.addClass(attrs.windowTopClass || '');
+ scope.size = attrs.size;
+
+ scope.close = function(evt) {
+ var modal = $modalStack.getTop();
+ if (modal && modal.value.backdrop &&
+ modal.value.backdrop !== 'static' &&
+ evt.target === evt.currentTarget) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ $modalStack.dismiss(modal.key, 'backdrop click');
+ }
+ };
+
+ // moved from template to fix issue #2280
+ element.on('click', scope.close);
+
+ // This property is only added to the scope for the purpose of detecting when this directive is rendered.
+ // We can detect that by using this property in the template associated with this directive and then use
+ // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
+ scope.$isRendered = true;
+
+ // Deferred object that will be resolved when this modal is render.
+ var modalRenderDeferObj = $q.defer();
+ // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
+ // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
+ attrs.$observe('modalRender', function(value) {
+ if (value === 'true') {
+ modalRenderDeferObj.resolve();
+ }
+ });
+
+ modalRenderDeferObj.promise.then(function() {
+ var animationPromise = null;
+
+ if (attrs.modalInClass) {
+ animationPromise = $animateCss(element, {
+ addClass: attrs.modalInClass
+ }).start();
+
+ scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
+ var done = setIsAsync();
+ $animateCss(element, {
+ removeClass: attrs.modalInClass
+ }).start().then(done);
+ });
+ }
+
+
+ $q.when(animationPromise).then(function() {
+ // Notify {@link $modalStack} that modal is rendered.
+ var modal = $modalStack.getTop();
+ if (modal) {
+ $modalStack.modalRendered(modal.key);
+ }
+
+ /**
+ * If something within the freshly-opened modal already has focus (perhaps via a
+ * directive that causes focus). then no need to try and focus anything.
+ */
+ if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
+ var inputWithAutofocus = element[0].querySelector('[autofocus]');
+ /**
+ * Auto-focusing of a freshly-opened modal element causes any child elements
+ * with the autofocus attribute to lose focus. This is an issue on touch
+ * based devices which will show and then hide the onscreen keyboard.
+ * Attempts to refocus the autofocus element via JavaScript will not reopen
+ * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
+ * the modal element if the modal does not contain an autofocus element.
+ */
+ if (inputWithAutofocus) {
+ inputWithAutofocus.focus();
+ } else {
+ element[0].focus();
+ }
+ }
+ });
+ });
+ }
+ };
+ }])
+
+ .directive('uibModalAnimationClass', function() {
+ return {
+ compile: function(tElement, tAttrs) {
+ if (tAttrs.modalAnimation) {
+ tElement.addClass(tAttrs.uibModalAnimationClass);
+ }
+ }
+ };
+ })
+
+ .directive('uibModalTransclude', function() {
+ return {
+ link: function(scope, element, attrs, controller, transclude) {
+ transclude(scope.$parent, function(clone) {
+ element.empty();
+ element.append(clone);
+ });
+ }
+ };
+ })
+
+ .factory('$uibModalStack', ['$animate', '$animateCss', '$document',
+ '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition',
+ function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap, $uibPosition) {
+ var OPENED_MODAL_CLASS = 'modal-open';
+
+ var backdropDomEl, backdropScope;
+ var openedWindows = $$stackedMap.createNew();
+ var openedClasses = $$multiMap.createNew();
+ var $modalStack = {
+ NOW_CLOSING_EVENT: 'modal.stack.now-closing'
+ };
+ var topModalIndex = 0;
+ var previousTopOpenedModal = null;
+
+ //Modal focus behavior
+ var tabableSelector = 'a[href], area[href], input:not([disabled]), ' +
+ 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
+ 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
+ var scrollbarPadding;
+
+ function isVisible(element) {
+ return !!(element.offsetWidth ||
+ element.offsetHeight ||
+ element.getClientRects().length);
+ }
+
+ function backdropIndex() {
+ var topBackdropIndex = -1;
+ var opened = openedWindows.keys();
+ for (var i = 0; i < opened.length; i++) {
+ if (openedWindows.get(opened[i]).value.backdrop) {
+ topBackdropIndex = i;
+ }
+ }
+
+ // If any backdrop exist, ensure that it's index is always
+ // right below the top modal
+ if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) {
+ topBackdropIndex = topModalIndex;
+ }
+ return topBackdropIndex;
+ }
+
+ $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
+ if (backdropScope) {
+ backdropScope.index = newBackdropIndex;
+ }
+ });
+
+ function removeModalWindow(modalInstance, elementToReceiveFocus) {
+ var modalWindow = openedWindows.get(modalInstance).value;
+ var appendToElement = modalWindow.appendTo;
+
+ //clean up the stack
+ openedWindows.remove(modalInstance);
+ previousTopOpenedModal = openedWindows.top();
+ if (previousTopOpenedModal) {
+ topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10);
+ }
+
+ removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
+ var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
+ openedClasses.remove(modalBodyClass, modalInstance);
+ var areAnyOpen = openedClasses.hasKey(modalBodyClass);
+ appendToElement.toggleClass(modalBodyClass, areAnyOpen);
+ if (!areAnyOpen && scrollbarPadding && scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
+ if (scrollbarPadding.originalRight) {
+ appendToElement.css({paddingRight: scrollbarPadding.originalRight + 'px'});
+ } else {
+ appendToElement.css({paddingRight: ''});
+ }
+ scrollbarPadding = null;
+ }
+ toggleTopWindowClass(true);
+ }, modalWindow.closedDeferred);
+ checkRemoveBackdrop();
+
+ //move focus to specified element if available, or else to body
+ if (elementToReceiveFocus && elementToReceiveFocus.focus) {
+ elementToReceiveFocus.focus();
+ } else if (appendToElement.focus) {
+ appendToElement.focus();
+ }
+ }
+
+ // Add or remove "windowTopClass" from the top window in the stack
+ function toggleTopWindowClass(toggleSwitch) {
+ var modalWindow;
+
+ if (openedWindows.length() > 0) {
+ modalWindow = openedWindows.top().value;
+ modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch);
+ }
+ }
+
+ function checkRemoveBackdrop() {
+ //remove backdrop if no longer needed
+ if (backdropDomEl && backdropIndex() === -1) {
+ var backdropScopeRef = backdropScope;
+ removeAfterAnimate(backdropDomEl, backdropScope, function() {
+ backdropScopeRef = null;
+ });
+ backdropDomEl = undefined;
+ backdropScope = undefined;
+ }
+ }
+
+ function removeAfterAnimate(domEl, scope, done, closedDeferred) {
+ var asyncDeferred;
+ var asyncPromise = null;
+ var setIsAsync = function() {
+ if (!asyncDeferred) {
+ asyncDeferred = $q.defer();
+ asyncPromise = asyncDeferred.promise;
+ }
+
+ return function asyncDone() {
+ asyncDeferred.resolve();
+ };
+ };
+ scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
+
+ // Note that it's intentional that asyncPromise might be null.
+ // That's when setIsAsync has not been called during the
+ // NOW_CLOSING_EVENT broadcast.
+ return $q.when(asyncPromise).then(afterAnimating);
+
+ function afterAnimating() {
+ if (afterAnimating.done) {
+ return;
+ }
+ afterAnimating.done = true;
+
+ $animate.leave(domEl).then(function() {
+ domEl.remove();
+ if (closedDeferred) {
+ closedDeferred.resolve();
+ }
+ });
+
+ scope.$destroy();
+ if (done) {
+ done();
+ }
+ }
+ }
+
+ $document.on('keydown', keydownListener);
+
+ $rootScope.$on('$destroy', function() {
+ $document.off('keydown', keydownListener);
+ });
+
+ function keydownListener(evt) {
+ if (evt.isDefaultPrevented()) {
+ return evt;
+ }
+
+ var modal = openedWindows.top();
+ if (modal) {
+ switch (evt.which) {
+ case 27: {
+ if (modal.value.keyboard) {
+ evt.preventDefault();
+ $rootScope.$apply(function() {
+ $modalStack.dismiss(modal.key, 'escape key press');
+ });
+ }
+ break;
+ }
+ case 9: {
+ var list = $modalStack.loadFocusElementList(modal);
+ var focusChanged = false;
+ if (evt.shiftKey) {
+ if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) {
+ focusChanged = $modalStack.focusLastFocusableElement(list);
+ }
+ } else {
+ if ($modalStack.isFocusInLastItem(evt, list)) {
+ focusChanged = $modalStack.focusFirstFocusableElement(list);
+ }
+ }
+
+ if (focusChanged) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ $modalStack.open = function(modalInstance, modal) {
+ var modalOpener = $document[0].activeElement,
+ modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
+
+ toggleTopWindowClass(false);
+
+ // Store the current top first, to determine what index we ought to use
+ // for the current top modal
+ previousTopOpenedModal = openedWindows.top();
+
+ openedWindows.add(modalInstance, {
+ deferred: modal.deferred,
+ renderDeferred: modal.renderDeferred,
+ closedDeferred: modal.closedDeferred,
+ modalScope: modal.scope,
+ backdrop: modal.backdrop,
+ keyboard: modal.keyboard,
+ openedClass: modal.openedClass,
+ windowTopClass: modal.windowTopClass,
+ animation: modal.animation,
+ appendTo: modal.appendTo
+ });
+
+ openedClasses.put(modalBodyClass, modalInstance);
+
+ var appendToElement = modal.appendTo,
+ currBackdropIndex = backdropIndex();
+
+ if (!appendToElement.length) {
+ throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
+ }
+
+ if (currBackdropIndex >= 0 && !backdropDomEl) {
+ backdropScope = $rootScope.$new(true);
+ backdropScope.modalOptions = modal;
+ backdropScope.index = currBackdropIndex;
+ backdropDomEl = angular.element('');
+ backdropDomEl.attr('backdrop-class', modal.backdropClass);
+ if (modal.animation) {
+ backdropDomEl.attr('modal-animation', 'true');
+ }
+ $compile(backdropDomEl)(backdropScope);
+ $animate.enter(backdropDomEl, appendToElement);
+ scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement);
+ if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
+ appendToElement.css({paddingRight: scrollbarPadding.right + 'px'});
+ }
+ }
+
+ // Set the top modal index based on the index of the previous top modal
+ topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0;
+ var angularDomEl = angular.element('');
+ angularDomEl.attr({
+ 'template-url': modal.windowTemplateUrl,
+ 'window-class': modal.windowClass,
+ 'window-top-class': modal.windowTopClass,
+ 'size': modal.size,
+ 'index': topModalIndex,
+ 'animate': 'animate'
+ }).html(modal.content);
+ if (modal.animation) {
+ angularDomEl.attr('modal-animation', 'true');
+ }
+
+ appendToElement.addClass(modalBodyClass);
+ $animate.enter($compile(angularDomEl)(modal.scope), appendToElement);
+
+ openedWindows.top().value.modalDomEl = angularDomEl;
+ openedWindows.top().value.modalOpener = modalOpener;
+ };
+
+ function broadcastClosing(modalWindow, resultOrReason, closing) {
+ return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
+ }
+
+ $modalStack.close = function(modalInstance, result) {
+ var modalWindow = openedWindows.get(modalInstance);
+ if (modalWindow && broadcastClosing(modalWindow, result, true)) {
+ modalWindow.value.modalScope.$$uibDestructionScheduled = true;
+ modalWindow.value.deferred.resolve(result);
+ removeModalWindow(modalInstance, modalWindow.value.modalOpener);
+ return true;
+ }
+ return !modalWindow;
+ };
+
+ $modalStack.dismiss = function(modalInstance, reason) {
+ var modalWindow = openedWindows.get(modalInstance);
+ if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
+ modalWindow.value.modalScope.$$uibDestructionScheduled = true;
+ modalWindow.value.deferred.reject(reason);
+ removeModalWindow(modalInstance, modalWindow.value.modalOpener);
+ return true;
+ }
+ return !modalWindow;
+ };
+
+ $modalStack.dismissAll = function(reason) {
+ var topModal = this.getTop();
+ while (topModal && this.dismiss(topModal.key, reason)) {
+ topModal = this.getTop();
+ }
+ };
+
+ $modalStack.getTop = function() {
+ return openedWindows.top();
+ };
+
+ $modalStack.modalRendered = function(modalInstance) {
+ var modalWindow = openedWindows.get(modalInstance);
+ if (modalWindow) {
+ modalWindow.value.renderDeferred.resolve();
+ }
+ };
+
+ $modalStack.focusFirstFocusableElement = function(list) {
+ if (list.length > 0) {
+ list[0].focus();
+ return true;
+ }
+ return false;
+ };
+
+ $modalStack.focusLastFocusableElement = function(list) {
+ if (list.length > 0) {
+ list[list.length - 1].focus();
+ return true;
+ }
+ return false;
+ };
+
+ $modalStack.isModalFocused = function(evt, modalWindow) {
+ if (evt && modalWindow) {
+ var modalDomEl = modalWindow.value.modalDomEl;
+ if (modalDomEl && modalDomEl.length) {
+ return (evt.target || evt.srcElement) === modalDomEl[0];
+ }
+ }
+ return false;
+ };
+
+ $modalStack.isFocusInFirstItem = function(evt, list) {
+ if (list.length > 0) {
+ return (evt.target || evt.srcElement) === list[0];
+ }
+ return false;
+ };
+
+ $modalStack.isFocusInLastItem = function(evt, list) {
+ if (list.length > 0) {
+ return (evt.target || evt.srcElement) === list[list.length - 1];
+ }
+ return false;
+ };
+
+ $modalStack.loadFocusElementList = function(modalWindow) {
+ if (modalWindow) {
+ var modalDomE1 = modalWindow.value.modalDomEl;
+ if (modalDomE1 && modalDomE1.length) {
+ var elements = modalDomE1[0].querySelectorAll(tabableSelector);
+ return elements ?
+ Array.prototype.filter.call(elements, function(element) {
+ return isVisible(element);
+ }) : elements;
+ }
+ }
+ };
+
+ return $modalStack;
+ }])
+
+ .provider('$uibModal', function() {
+ var $modalProvider = {
+ options: {
+ animation: true,
+ backdrop: true, //can also be false or 'static'
+ keyboard: true
+ },
+ $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack',
+ function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) {
+ var $modal = {};
+
+ function getTemplatePromise(options) {
+ return options.template ? $q.when(options.template) :
+ $templateRequest(angular.isFunction(options.templateUrl) ?
+ options.templateUrl() : options.templateUrl);
+ }
+
+ var promiseChain = null;
+ $modal.getPromiseChain = function() {
+ return promiseChain;
+ };
+
+ $modal.open = function(modalOptions) {
+ var modalResultDeferred = $q.defer();
+ var modalOpenedDeferred = $q.defer();
+ var modalClosedDeferred = $q.defer();
+ var modalRenderDeferred = $q.defer();
+
+ //prepare an instance of a modal to be injected into controllers and returned to a caller
+ var modalInstance = {
+ result: modalResultDeferred.promise,
+ opened: modalOpenedDeferred.promise,
+ closed: modalClosedDeferred.promise,
+ rendered: modalRenderDeferred.promise,
+ close: function (result) {
+ return $modalStack.close(modalInstance, result);
+ },
+ dismiss: function (reason) {
+ return $modalStack.dismiss(modalInstance, reason);
+ }
+ };
+
+ //merge and clean up options
+ modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
+ modalOptions.resolve = modalOptions.resolve || {};
+ modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
+
+ //verify options
+ if (!modalOptions.template && !modalOptions.templateUrl) {
+ throw new Error('One of template or templateUrl options is required.');
+ }
+
+ var templateAndResolvePromise =
+ $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
+
+ function resolveWithTemplate() {
+ return templateAndResolvePromise;
+ }
+
+ // Wait for the resolution of the existing promise chain.
+ // Then switch to our own combined promise dependency (regardless of how the previous modal fared).
+ // Then add to $modalStack and resolve opened.
+ // Finally clean up the chain variable if no subsequent modal has overwritten it.
+ var samePromise;
+ samePromise = promiseChain = $q.all([promiseChain])
+ .then(resolveWithTemplate, resolveWithTemplate)
+ .then(function resolveSuccess(tplAndVars) {
+ var providedScope = modalOptions.scope || $rootScope;
+
+ var modalScope = providedScope.$new();
+ modalScope.$close = modalInstance.close;
+ modalScope.$dismiss = modalInstance.dismiss;
+
+ modalScope.$on('$destroy', function() {
+ if (!modalScope.$$uibDestructionScheduled) {
+ modalScope.$dismiss('$uibUnscheduledDestruction');
+ }
+ });
+
+ var ctrlInstance, ctrlInstantiate, ctrlLocals = {};
+
+ //controllers
+ if (modalOptions.controller) {
+ ctrlLocals.$scope = modalScope;
+ ctrlLocals.$scope.$resolve = {};
+ ctrlLocals.$uibModalInstance = modalInstance;
+ angular.forEach(tplAndVars[1], function(value, key) {
+ ctrlLocals[key] = value;
+ ctrlLocals.$scope.$resolve[key] = value;
+ });
+
+ // the third param will make the controller instantiate later,private api
+ // @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126
+ ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true, modalOptions.controllerAs);
+ if (modalOptions.controllerAs && modalOptions.bindToController) {
+ ctrlInstance = ctrlInstantiate.instance;
+ ctrlInstance.$close = modalScope.$close;
+ ctrlInstance.$dismiss = modalScope.$dismiss;
+ angular.extend(ctrlInstance, {
+ $resolve: ctrlLocals.$scope.$resolve
+ }, providedScope);
+ }
+
+ ctrlInstance = ctrlInstantiate();
+
+ if (angular.isFunction(ctrlInstance.$onInit)) {
+ ctrlInstance.$onInit();
+ }
+ }
+
+ $modalStack.open(modalInstance, {
+ scope: modalScope,
+ deferred: modalResultDeferred,
+ renderDeferred: modalRenderDeferred,
+ closedDeferred: modalClosedDeferred,
+ content: tplAndVars[0],
+ animation: modalOptions.animation,
+ backdrop: modalOptions.backdrop,
+ keyboard: modalOptions.keyboard,
+ backdropClass: modalOptions.backdropClass,
+ windowTopClass: modalOptions.windowTopClass,
+ windowClass: modalOptions.windowClass,
+ windowTemplateUrl: modalOptions.windowTemplateUrl,
+ size: modalOptions.size,
+ openedClass: modalOptions.openedClass,
+ appendTo: modalOptions.appendTo
+ });
+ modalOpenedDeferred.resolve(true);
+
+ }, function resolveError(reason) {
+ modalOpenedDeferred.reject(reason);
+ modalResultDeferred.reject(reason);
+ })['finally'](function() {
+ if (promiseChain === samePromise) {
+ promiseChain = null;
+ }
+ });
+
+ return modalInstance;
+ };
+
+ return $modal;
+ }
+ ]
+ };
+
+ return $modalProvider;
+ });
+
+angular.module('ui.bootstrap.paging', [])
+/**
+ * Helper internal service for generating common controller code between the
+ * pager and pagination components
+ */
+.factory('uibPaging', ['$parse', function($parse) {
+ return {
+ create: function(ctrl, $scope, $attrs) {
+ ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
+ ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
+ ctrl._watchers = [];
+
+ ctrl.init = function(ngModelCtrl, config) {
+ ctrl.ngModelCtrl = ngModelCtrl;
+ ctrl.config = config;
+
+ ngModelCtrl.$render = function() {
+ ctrl.render();
+ };
+
+ if ($attrs.itemsPerPage) {
+ ctrl._watchers.push($scope.$parent.$watch($attrs.itemsPerPage, function(value) {
+ ctrl.itemsPerPage = parseInt(value, 10);
+ $scope.totalPages = ctrl.calculateTotalPages();
+ ctrl.updatePage();
+ }));
+ } else {
+ ctrl.itemsPerPage = config.itemsPerPage;
+ }
+
+ $scope.$watch('totalItems', function(newTotal, oldTotal) {
+ if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
+ $scope.totalPages = ctrl.calculateTotalPages();
+ ctrl.updatePage();
+ }
+ });
+ };
+
+ ctrl.calculateTotalPages = function() {
+ var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
+ return Math.max(totalPages || 0, 1);
+ };
+
+ ctrl.render = function() {
+ $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
+ };
+
+ $scope.selectPage = function(page, evt) {
+ if (evt) {
+ evt.preventDefault();
+ }
+
+ var clickAllowed = !$scope.ngDisabled || !evt;
+ if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
+ if (evt && evt.target) {
+ evt.target.blur();
+ }
+ ctrl.ngModelCtrl.$setViewValue(page);
+ ctrl.ngModelCtrl.$render();
+ }
+ };
+
+ $scope.getText = function(key) {
+ return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
+ };
+
+ $scope.noPrevious = function() {
+ return $scope.page === 1;
+ };
+
+ $scope.noNext = function() {
+ return $scope.page === $scope.totalPages;
+ };
+
+ ctrl.updatePage = function() {
+ ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
+
+ if ($scope.page > $scope.totalPages) {
+ $scope.selectPage($scope.totalPages);
+ } else {
+ ctrl.ngModelCtrl.$render();
+ }
+ };
+
+ $scope.$on('$destroy', function() {
+ while (ctrl._watchers.length) {
+ ctrl._watchers.shift()();
+ }
+ });
+ }
+ };
+}]);
+
+angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
+
+.controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
+ $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
+
+ uibPaging.create(this, $scope, $attrs);
+}])
+
+.constant('uibPagerConfig', {
+ itemsPerPage: 10,
+ previousText: '« Previous',
+ nextText: 'Next »',
+ align: true
+})
+
+.directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) {
+ return {
+ scope: {
+ totalItems: '=',
+ previousText: '@',
+ nextText: '@',
+ ngDisabled: '='
+ },
+ require: ['uibPager', '?ngModel'],
+ controller: 'UibPagerController',
+ controllerAs: 'pager',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/pager/pager.html';
+ },
+ replace: true,
+ link: function(scope, element, attrs, ctrls) {
+ var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ if (!ngModelCtrl) {
+ return; // do nothing if no ng-model
+ }
+
+ paginationCtrl.init(ngModelCtrl, uibPagerConfig);
+ }
+ };
+}]);
+
+angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
+.controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
+ var ctrl = this;
+ // Setup configuration parameters
+ var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
+ rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
+ forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
+ boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers,
+ pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity;
+ $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
+ $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
+
+ uibPaging.create(this, $scope, $attrs);
+
+ if ($attrs.maxSize) {
+ ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) {
+ maxSize = parseInt(value, 10);
+ ctrl.render();
+ }));
+ }
+
+ // Create page object used in template
+ function makePage(number, text, isActive) {
+ return {
+ number: number,
+ text: text,
+ active: isActive
+ };
+ }
+
+ function getPages(currentPage, totalPages) {
+ var pages = [];
+
+ // Default page limits
+ var startPage = 1, endPage = totalPages;
+ var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
+
+ // recompute if maxSize
+ if (isMaxSized) {
+ if (rotate) {
+ // Current page is displayed in the middle of the visible ones
+ startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
+ endPage = startPage + maxSize - 1;
+
+ // Adjust if limit is exceeded
+ if (endPage > totalPages) {
+ endPage = totalPages;
+ startPage = endPage - maxSize + 1;
+ }
+ } else {
+ // Visible pages are paginated with maxSize
+ startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
+
+ // Adjust last page if limit is exceeded
+ endPage = Math.min(startPage + maxSize - 1, totalPages);
+ }
+ }
+
+ // Add page number links
+ for (var number = startPage; number <= endPage; number++) {
+ var page = makePage(number, pageLabel(number), number === currentPage);
+ pages.push(page);
+ }
+
+ // Add links to move between page sets
+ if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
+ if (startPage > 1) {
+ if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
+ var previousPageSet = makePage(startPage - 1, '...', false);
+ pages.unshift(previousPageSet);
+ }
+ if (boundaryLinkNumbers) {
+ if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
+ var secondPageLink = makePage(2, '2', false);
+ pages.unshift(secondPageLink);
+ }
+ //add the first page
+ var firstPageLink = makePage(1, '1', false);
+ pages.unshift(firstPageLink);
+ }
+ }
+
+ if (endPage < totalPages) {
+ if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
+ var nextPageSet = makePage(endPage + 1, '...', false);
+ pages.push(nextPageSet);
+ }
+ if (boundaryLinkNumbers) {
+ if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
+ var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
+ pages.push(secondToLastPageLink);
+ }
+ //add the last page
+ var lastPageLink = makePage(totalPages, totalPages, false);
+ pages.push(lastPageLink);
+ }
+ }
+ }
+ return pages;
+ }
+
+ var originalRender = this.render;
+ this.render = function() {
+ originalRender();
+ if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
+ $scope.pages = getPages($scope.page, $scope.totalPages);
+ }
+ };
+}])
+
+.constant('uibPaginationConfig', {
+ itemsPerPage: 10,
+ boundaryLinks: false,
+ boundaryLinkNumbers: false,
+ directionLinks: true,
+ firstText: 'First',
+ previousText: 'Previous',
+ nextText: 'Next',
+ lastText: 'Last',
+ rotate: true,
+ forceEllipses: false
+})
+
+.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
+ return {
+ scope: {
+ totalItems: '=',
+ firstText: '@',
+ previousText: '@',
+ nextText: '@',
+ lastText: '@',
+ ngDisabled:'='
+ },
+ require: ['uibPagination', '?ngModel'],
+ controller: 'UibPaginationController',
+ controllerAs: 'pagination',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/pagination/pagination.html';
+ },
+ replace: true,
+ link: function(scope, element, attrs, ctrls) {
+ var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ if (!ngModelCtrl) {
+ return; // do nothing if no ng-model
+ }
+
+ paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
+ }
+ };
+}]);
+
+/**
+ * The following features are still outstanding: animation as a
+ * function, placement as a function, inside, support for more triggers than
+ * just mouse enter/leave, html tooltips, and selector delegation.
+ */
+angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap'])
+
+/**
+ * The $tooltip service creates tooltip- and popover-like directives as well as
+ * houses global options for them.
+ */
+.provider('$uibTooltip', function() {
+ // The default options tooltip and popover.
+ var defaultOptions = {
+ placement: 'top',
+ placementClassPrefix: '',
+ animation: true,
+ popupDelay: 0,
+ popupCloseDelay: 0,
+ useContentExp: false
+ };
+
+ // Default hide triggers for each show trigger
+ var triggerMap = {
+ 'mouseenter': 'mouseleave',
+ 'click': 'click',
+ 'outsideClick': 'outsideClick',
+ 'focus': 'blur',
+ 'none': ''
+ };
+
+ // The options specified to the provider globally.
+ var globalOptions = {};
+
+ /**
+ * `options({})` allows global configuration of all tooltips in the
+ * application.
+ *
+ * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
+ * // place tooltips left instead of top by default
+ * $tooltipProvider.options( { placement: 'left' } );
+ * });
+ */
+ this.options = function(value) {
+ angular.extend(globalOptions, value);
+ };
+
+ /**
+ * This allows you to extend the set of trigger mappings available. E.g.:
+ *
+ * $tooltipProvider.setTriggers( { 'openTrigger': 'closeTrigger' } );
+ */
+ this.setTriggers = function setTriggers(triggers) {
+ angular.extend(triggerMap, triggers);
+ };
+
+ /**
+ * This is a helper function for translating camel-case to snake_case.
+ */
+ function snake_case(name) {
+ var regexp = /[A-Z]/g;
+ var separator = '-';
+ return name.replace(regexp, function(letter, pos) {
+ return (pos ? separator : '') + letter.toLowerCase();
+ });
+ }
+
+ /**
+ * Returns the actual instance of the $tooltip service.
+ * TODO support multiple triggers
+ */
+ this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
+ var openedTooltips = $$stackedMap.createNew();
+ $document.on('keypress', keypressListener);
+
+ $rootScope.$on('$destroy', function() {
+ $document.off('keypress', keypressListener);
+ });
+
+ function keypressListener(e) {
+ if (e.which === 27) {
+ var last = openedTooltips.top();
+ if (last) {
+ last.value.close();
+ openedTooltips.removeTop();
+ last = null;
+ }
+ }
+ }
+
+ return function $tooltip(ttType, prefix, defaultTriggerShow, options) {
+ options = angular.extend({}, defaultOptions, globalOptions, options);
+
+ /**
+ * Returns an object of show and hide triggers.
+ *
+ * If a trigger is supplied,
+ * it is used to show the tooltip; otherwise, it will use the `trigger`
+ * option passed to the `$tooltipProvider.options` method; else it will
+ * default to the trigger supplied to this directive factory.
+ *
+ * The hide trigger is based on the show trigger. If the `trigger` option
+ * was passed to the `$tooltipProvider.options` method, it will use the
+ * mapped trigger from `triggerMap` or the passed trigger if the map is
+ * undefined; otherwise, it uses the `triggerMap` value of the show
+ * trigger; else it will just use the show trigger.
+ */
+ function getTriggers(trigger) {
+ var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
+ var hide = show.map(function(trigger) {
+ return triggerMap[trigger] || trigger;
+ });
+ return {
+ show: show,
+ hide: hide
+ };
+ }
+
+ var directiveName = snake_case(ttType);
+
+ var startSym = $interpolate.startSymbol();
+ var endSym = $interpolate.endSymbol();
+ var template =
+ '
' +
+ '
';
+
+ return {
+ compile: function(tElem, tAttrs) {
+ var tooltipLinker = $compile(template);
+
+ return function link(scope, element, attrs, tooltipCtrl) {
+ var tooltip;
+ var tooltipLinkedScope;
+ var transitionTimeout;
+ var showTimeout;
+ var hideTimeout;
+ var positionTimeout;
+ var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
+ var triggers = getTriggers(undefined);
+ var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
+ var ttScope = scope.$new(true);
+ var repositionScheduled = false;
+ var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
+ var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
+ var observers = [];
+ var lastPlacement;
+
+ var positionTooltip = function() {
+ // check if tooltip exists and is not empty
+ if (!tooltip || !tooltip.html()) { return; }
+
+ if (!positionTimeout) {
+ positionTimeout = $timeout(function() {
+ var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
+ tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' });
+
+ if (!tooltip.hasClass(ttPosition.placement.split('-')[0])) {
+ tooltip.removeClass(lastPlacement.split('-')[0]);
+ tooltip.addClass(ttPosition.placement.split('-')[0]);
+ }
+
+ if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) {
+ tooltip.removeClass(options.placementClassPrefix + lastPlacement);
+ tooltip.addClass(options.placementClassPrefix + ttPosition.placement);
+ }
+
+ // first time through tt element will have the
+ // uib-position-measure class or if the placement
+ // has changed we need to position the arrow.
+ if (tooltip.hasClass('uib-position-measure')) {
+ $position.positionArrow(tooltip, ttPosition.placement);
+ tooltip.removeClass('uib-position-measure');
+ } else if (lastPlacement !== ttPosition.placement) {
+ $position.positionArrow(tooltip, ttPosition.placement);
+ }
+ lastPlacement = ttPosition.placement;
+
+ positionTimeout = null;
+ }, 0, false);
+ }
+ };
+
+ // Set up the correct scope to allow transclusion later
+ ttScope.origScope = scope;
+
+ // By default, the tooltip is not open.
+ // TODO add ability to start tooltip opened
+ ttScope.isOpen = false;
+ openedTooltips.add(ttScope, {
+ close: hide
+ });
+
+ function toggleTooltipBind() {
+ if (!ttScope.isOpen) {
+ showTooltipBind();
+ } else {
+ hideTooltipBind();
+ }
+ }
+
+ // Show the tooltip with delay if specified, otherwise show it immediately
+ function showTooltipBind() {
+ if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
+ return;
+ }
+
+ cancelHide();
+ prepareTooltip();
+
+ if (ttScope.popupDelay) {
+ // Do nothing if the tooltip was already scheduled to pop-up.
+ // This happens if show is triggered multiple times before any hide is triggered.
+ if (!showTimeout) {
+ showTimeout = $timeout(show, ttScope.popupDelay, false);
+ }
+ } else {
+ show();
+ }
+ }
+
+ function hideTooltipBind() {
+ cancelShow();
+
+ if (ttScope.popupCloseDelay) {
+ if (!hideTimeout) {
+ hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false);
+ }
+ } else {
+ hide();
+ }
+ }
+
+ // Show the tooltip popup element.
+ function show() {
+ cancelShow();
+ cancelHide();
+
+ // Don't show empty tooltips.
+ if (!ttScope.content) {
+ return angular.noop;
+ }
+
+ createTooltip();
+
+ // And show the tooltip.
+ ttScope.$evalAsync(function() {
+ ttScope.isOpen = true;
+ assignIsOpen(true);
+ positionTooltip();
+ });
+ }
+
+ function cancelShow() {
+ if (showTimeout) {
+ $timeout.cancel(showTimeout);
+ showTimeout = null;
+ }
+
+ if (positionTimeout) {
+ $timeout.cancel(positionTimeout);
+ positionTimeout = null;
+ }
+ }
+
+ // Hide the tooltip popup element.
+ function hide() {
+ if (!ttScope) {
+ return;
+ }
+
+ // First things first: we don't show it anymore.
+ ttScope.$evalAsync(function() {
+ if (ttScope) {
+ ttScope.isOpen = false;
+ assignIsOpen(false);
+ // And now we remove it from the DOM. However, if we have animation, we
+ // need to wait for it to expire beforehand.
+ // FIXME: this is a placeholder for a port of the transitions library.
+ // The fade transition in TWBS is 150ms.
+ if (ttScope.animation) {
+ if (!transitionTimeout) {
+ transitionTimeout = $timeout(removeTooltip, 150, false);
+ }
+ } else {
+ removeTooltip();
+ }
+ }
+ });
+ }
+
+ function cancelHide() {
+ if (hideTimeout) {
+ $timeout.cancel(hideTimeout);
+ hideTimeout = null;
+ }
+
+ if (transitionTimeout) {
+ $timeout.cancel(transitionTimeout);
+ transitionTimeout = null;
+ }
+ }
+
+ function createTooltip() {
+ // There can only be one tooltip element per directive shown at once.
+ if (tooltip) {
+ return;
+ }
+
+ tooltipLinkedScope = ttScope.$new();
+ tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
+ if (appendToBody) {
+ $document.find('body').append(tooltip);
+ } else {
+ element.after(tooltip);
+ }
+ });
+
+ prepObservers();
+ }
+
+ function removeTooltip() {
+ cancelShow();
+ cancelHide();
+ unregisterObservers();
+
+ if (tooltip) {
+ tooltip.remove();
+ tooltip = null;
+ }
+ if (tooltipLinkedScope) {
+ tooltipLinkedScope.$destroy();
+ tooltipLinkedScope = null;
+ }
+ }
+
+ /**
+ * Set the initial scope values. Once
+ * the tooltip is created, the observers
+ * will be added to keep things in sync.
+ */
+ function prepareTooltip() {
+ ttScope.title = attrs[prefix + 'Title'];
+ if (contentParse) {
+ ttScope.content = contentParse(scope);
+ } else {
+ ttScope.content = attrs[ttType];
+ }
+
+ ttScope.popupClass = attrs[prefix + 'Class'];
+ ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement;
+ var placement = $position.parsePlacement(ttScope.placement);
+ lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0];
+
+ var delay = parseInt(attrs[prefix + 'PopupDelay'], 10);
+ var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10);
+ ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
+ ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay;
+ }
+
+ function assignIsOpen(isOpen) {
+ if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
+ isOpenParse.assign(scope, isOpen);
+ }
+ }
+
+ ttScope.contentExp = function() {
+ return ttScope.content;
+ };
+
+ /**
+ * Observe the relevant attributes.
+ */
+ attrs.$observe('disabled', function(val) {
+ if (val) {
+ cancelShow();
+ }
+
+ if (val && ttScope.isOpen) {
+ hide();
+ }
+ });
+
+ if (isOpenParse) {
+ scope.$watch(isOpenParse, function(val) {
+ if (ttScope && !val === ttScope.isOpen) {
+ toggleTooltipBind();
+ }
+ });
+ }
+
+ function prepObservers() {
+ observers.length = 0;
+
+ if (contentParse) {
+ observers.push(
+ scope.$watch(contentParse, function(val) {
+ ttScope.content = val;
+ if (!val && ttScope.isOpen) {
+ hide();
+ }
+ })
+ );
+
+ observers.push(
+ tooltipLinkedScope.$watch(function() {
+ if (!repositionScheduled) {
+ repositionScheduled = true;
+ tooltipLinkedScope.$$postDigest(function() {
+ repositionScheduled = false;
+ if (ttScope && ttScope.isOpen) {
+ positionTooltip();
+ }
+ });
+ }
+ })
+ );
+ } else {
+ observers.push(
+ attrs.$observe(ttType, function(val) {
+ ttScope.content = val;
+ if (!val && ttScope.isOpen) {
+ hide();
+ } else {
+ positionTooltip();
+ }
+ })
+ );
+ }
+
+ observers.push(
+ attrs.$observe(prefix + 'Title', function(val) {
+ ttScope.title = val;
+ if (ttScope.isOpen) {
+ positionTooltip();
+ }
+ })
+ );
+
+ observers.push(
+ attrs.$observe(prefix + 'Placement', function(val) {
+ ttScope.placement = val ? val : options.placement;
+ if (ttScope.isOpen) {
+ positionTooltip();
+ }
+ })
+ );
+ }
+
+ function unregisterObservers() {
+ if (observers.length) {
+ angular.forEach(observers, function(observer) {
+ observer();
+ });
+ observers.length = 0;
+ }
+ }
+
+ // hide tooltips/popovers for outsideClick trigger
+ function bodyHideTooltipBind(e) {
+ if (!ttScope || !ttScope.isOpen || !tooltip) {
+ return;
+ }
+ // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked
+ if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) {
+ hideTooltipBind();
+ }
+ }
+
+ var unregisterTriggers = function() {
+ triggers.show.forEach(function(trigger) {
+ if (trigger === 'outsideClick') {
+ element.off('click', toggleTooltipBind);
+ } else {
+ element.off(trigger, showTooltipBind);
+ element.off(trigger, toggleTooltipBind);
+ }
+ });
+ triggers.hide.forEach(function(trigger) {
+ if (trigger === 'outsideClick') {
+ $document.off('click', bodyHideTooltipBind);
+ } else {
+ element.off(trigger, hideTooltipBind);
+ }
+ });
+ };
+
+ function prepTriggers() {
+ var val = attrs[prefix + 'Trigger'];
+ unregisterTriggers();
+
+ triggers = getTriggers(val);
+
+ if (triggers.show !== 'none') {
+ triggers.show.forEach(function(trigger, idx) {
+ if (trigger === 'outsideClick') {
+ element.on('click', toggleTooltipBind);
+ $document.on('click', bodyHideTooltipBind);
+ } else if (trigger === triggers.hide[idx]) {
+ element.on(trigger, toggleTooltipBind);
+ } else if (trigger) {
+ element.on(trigger, showTooltipBind);
+ element.on(triggers.hide[idx], hideTooltipBind);
+ }
+
+ element.on('keypress', function(e) {
+ if (e.which === 27) {
+ hideTooltipBind();
+ }
+ });
+ });
+ }
+ }
+
+ prepTriggers();
+
+ var animation = scope.$eval(attrs[prefix + 'Animation']);
+ ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
+
+ var appendToBodyVal;
+ var appendKey = prefix + 'AppendToBody';
+ if (appendKey in attrs && attrs[appendKey] === undefined) {
+ appendToBodyVal = true;
+ } else {
+ appendToBodyVal = scope.$eval(attrs[appendKey]);
+ }
+
+ appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
+
+ // Make sure tooltip is destroyed and removed.
+ scope.$on('$destroy', function onDestroyTooltip() {
+ unregisterTriggers();
+ removeTooltip();
+ openedTooltips.remove(ttScope);
+ ttScope = null;
+ });
+ };
+ }
+ };
+ };
+ }];
+})
+
+// This is mostly ngInclude code but with a custom scope
+.directive('uibTooltipTemplateTransclude', [
+ '$animate', '$sce', '$compile', '$templateRequest',
+function ($animate, $sce, $compile, $templateRequest) {
+ return {
+ link: function(scope, elem, attrs) {
+ var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
+
+ var changeCounter = 0,
+ currentScope,
+ previousElement,
+ currentElement;
+
+ var cleanupLastIncludeContent = function() {
+ if (previousElement) {
+ previousElement.remove();
+ previousElement = null;
+ }
+
+ if (currentScope) {
+ currentScope.$destroy();
+ currentScope = null;
+ }
+
+ if (currentElement) {
+ $animate.leave(currentElement).then(function() {
+ previousElement = null;
+ });
+ previousElement = currentElement;
+ currentElement = null;
+ }
+ };
+
+ scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) {
+ var thisChangeId = ++changeCounter;
+
+ if (src) {
+ //set the 2nd param to true to ignore the template request error so that the inner
+ //contents and scope can be cleaned up.
+ $templateRequest(src, true).then(function(response) {
+ if (thisChangeId !== changeCounter) { return; }
+ var newScope = origScope.$new();
+ var template = response;
+
+ var clone = $compile(template)(newScope, function(clone) {
+ cleanupLastIncludeContent();
+ $animate.enter(clone, elem);
+ });
+
+ currentScope = newScope;
+ currentElement = clone;
+
+ currentScope.$emit('$includeContentLoaded', src);
+ }, function() {
+ if (thisChangeId === changeCounter) {
+ cleanupLastIncludeContent();
+ scope.$emit('$includeContentError', src);
+ }
+ });
+ scope.$emit('$includeContentRequested', src);
+ } else {
+ cleanupLastIncludeContent();
+ }
+ });
+
+ scope.$on('$destroy', cleanupLastIncludeContent);
+ }
+ };
+}])
+
+/**
+ * Note that it's intentional that these classes are *not* applied through $animate.
+ * They must not be animated as they're expected to be present on the tooltip on
+ * initialization.
+ */
+.directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) {
+ return {
+ restrict: 'A',
+ link: function(scope, element, attrs) {
+ // need to set the primary position so the
+ // arrow has space during position measure.
+ // tooltip.positionTooltip()
+ if (scope.placement) {
+ // // There are no top-left etc... classes
+ // // in TWBS, so we need the primary position.
+ var position = $uibPosition.parsePlacement(scope.placement);
+ element.addClass(position[0]);
+ }
+
+ if (scope.popupClass) {
+ element.addClass(scope.popupClass);
+ }
+
+ if (scope.animation()) {
+ element.addClass(attrs.tooltipAnimationClass);
+ }
+ }
+ };
+}])
+
+.directive('uibTooltipPopup', function() {
+ return {
+ replace: true,
+ scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
+ templateUrl: 'uib/template/tooltip/tooltip-popup.html'
+ };
+})
+
+.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) {
+ return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter');
+}])
+
+.directive('uibTooltipTemplatePopup', function() {
+ return {
+ replace: true,
+ scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
+ originScope: '&' },
+ templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
+ };
+})
+
+.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) {
+ return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', {
+ useContentExp: true
+ });
+}])
+
+.directive('uibTooltipHtmlPopup', function() {
+ return {
+ replace: true,
+ scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
+ templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
+ };
+})
+
+.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) {
+ return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', {
+ useContentExp: true
+ });
+}]);
+
+/**
+ * The following features are still outstanding: popup delay, animation as a
+ * function, placement as a function, inside, support for more triggers than
+ * just mouse enter/leave, and selector delegatation.
+ */
+angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
+
+.directive('uibPopoverTemplatePopup', function() {
+ return {
+ replace: true,
+ scope: { uibTitle: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
+ originScope: '&' },
+ templateUrl: 'uib/template/popover/popover-template.html'
+ };
+})
+
+.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) {
+ return $uibTooltip('uibPopoverTemplate', 'popover', 'click', {
+ useContentExp: true
+ });
+}])
+
+.directive('uibPopoverHtmlPopup', function() {
+ return {
+ replace: true,
+ scope: { contentExp: '&', uibTitle: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
+ templateUrl: 'uib/template/popover/popover-html.html'
+ };
+})
+
+.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) {
+ return $uibTooltip('uibPopoverHtml', 'popover', 'click', {
+ useContentExp: true
+ });
+}])
+
+.directive('uibPopoverPopup', function() {
+ return {
+ replace: true,
+ scope: { uibTitle: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
+ templateUrl: 'uib/template/popover/popover.html'
+ };
+})
+
+.directive('uibPopover', ['$uibTooltip', function($uibTooltip) {
+ return $uibTooltip('uibPopover', 'popover', 'click');
+}]);
+
+angular.module('ui.bootstrap.progressbar', [])
+
+.constant('uibProgressConfig', {
+ animate: true,
+ max: 100
+})
+
+.controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) {
+ var self = this,
+ animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
+
+ this.bars = [];
+ $scope.max = getMaxOrDefault();
+
+ this.addBar = function(bar, element, attrs) {
+ if (!animate) {
+ element.css({'transition': 'none'});
+ }
+
+ this.bars.push(bar);
+
+ bar.max = getMaxOrDefault();
+ bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar';
+
+ bar.$watch('value', function(value) {
+ bar.recalculatePercentage();
+ });
+
+ bar.recalculatePercentage = function() {
+ var totalPercentage = self.bars.reduce(function(total, bar) {
+ bar.percent = +(100 * bar.value / bar.max).toFixed(2);
+ return total + bar.percent;
+ }, 0);
+
+ if (totalPercentage > 100) {
+ bar.percent -= totalPercentage - 100;
+ }
+ };
+
+ bar.$on('$destroy', function() {
+ element = null;
+ self.removeBar(bar);
+ });
+ };
+
+ this.removeBar = function(bar) {
+ this.bars.splice(this.bars.indexOf(bar), 1);
+ this.bars.forEach(function (bar) {
+ bar.recalculatePercentage();
+ });
+ };
+
+ //$attrs.$observe('maxParam', function(maxParam) {
+ $scope.$watch('maxParam', function(maxParam) {
+ self.bars.forEach(function(bar) {
+ bar.max = getMaxOrDefault();
+ bar.recalculatePercentage();
+ });
+ });
+
+ function getMaxOrDefault () {
+ return angular.isDefined($scope.maxParam) ? $scope.maxParam : progressConfig.max;
+ }
+}])
+
+.directive('uibProgress', function() {
+ return {
+ replace: true,
+ transclude: true,
+ controller: 'UibProgressController',
+ require: 'uibProgress',
+ scope: {
+ maxParam: '=?max'
+ },
+ templateUrl: 'uib/template/progressbar/progress.html'
+ };
+})
+
+.directive('uibBar', function() {
+ return {
+ replace: true,
+ transclude: true,
+ require: '^uibProgress',
+ scope: {
+ value: '=',
+ type: '@'
+ },
+ templateUrl: 'uib/template/progressbar/bar.html',
+ link: function(scope, element, attrs, progressCtrl) {
+ progressCtrl.addBar(scope, element, attrs);
+ }
+ };
+})
+
+.directive('uibProgressbar', function() {
+ return {
+ replace: true,
+ transclude: true,
+ controller: 'UibProgressController',
+ scope: {
+ value: '=',
+ maxParam: '=?max',
+ type: '@'
+ },
+ templateUrl: 'uib/template/progressbar/progressbar.html',
+ link: function(scope, element, attrs, progressCtrl) {
+ progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title});
+ }
+ };
+});
+
+angular.module('ui.bootstrap.rating', [])
+
+.constant('uibRatingConfig', {
+ max: 5,
+ stateOn: null,
+ stateOff: null,
+ enableReset: true,
+ titles : ['one', 'two', 'three', 'four', 'five']
+})
+
+.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
+ var ngModelCtrl = { $setViewValue: angular.noop },
+ self = this;
+
+ this.init = function(ngModelCtrl_) {
+ ngModelCtrl = ngModelCtrl_;
+ ngModelCtrl.$render = this.render;
+
+ ngModelCtrl.$formatters.push(function(value) {
+ if (angular.isNumber(value) && value << 0 !== value) {
+ value = Math.round(value);
+ }
+
+ return value;
+ });
+
+ this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
+ this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
+ this.enableReset = angular.isDefined($attrs.enableReset) ?
+ $scope.$parent.$eval($attrs.enableReset) : ratingConfig.enableReset;
+ var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles;
+ this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
+ tmpTitles : ratingConfig.titles;
+
+ var ratingStates = angular.isDefined($attrs.ratingStates) ?
+ $scope.$parent.$eval($attrs.ratingStates) :
+ new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
+ $scope.range = this.buildTemplateObjects(ratingStates);
+ };
+
+ this.buildTemplateObjects = function(states) {
+ for (var i = 0, n = states.length; i < n; i++) {
+ states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
+ }
+ return states;
+ };
+
+ this.getTitle = function(index) {
+ if (index >= this.titles.length) {
+ return index + 1;
+ }
+
+ return this.titles[index];
+ };
+
+ $scope.rate = function(value) {
+ if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
+ var newViewValue = self.enableReset && ngModelCtrl.$viewValue === value ? 0 : value;
+ ngModelCtrl.$setViewValue(newViewValue);
+ ngModelCtrl.$render();
+ }
+ };
+
+ $scope.enter = function(value) {
+ if (!$scope.readonly) {
+ $scope.value = value;
+ }
+ $scope.onHover({value: value});
+ };
+
+ $scope.reset = function() {
+ $scope.value = ngModelCtrl.$viewValue;
+ $scope.onLeave();
+ };
+
+ $scope.onKeydown = function(evt) {
+ if (/(37|38|39|40)/.test(evt.which)) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
+ }
+ };
+
+ this.render = function() {
+ $scope.value = ngModelCtrl.$viewValue;
+ $scope.title = self.getTitle($scope.value - 1);
+ };
+}])
+
+.directive('uibRating', function() {
+ return {
+ require: ['uibRating', 'ngModel'],
+ scope: {
+ readonly: '=?readOnly',
+ onHover: '&',
+ onLeave: '&'
+ },
+ controller: 'UibRatingController',
+ templateUrl: 'uib/template/rating/rating.html',
+ replace: true,
+ link: function(scope, element, attrs, ctrls) {
+ var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+ ratingCtrl.init(ngModelCtrl);
+ }
+ };
+});
+
+angular.module('ui.bootstrap.tabs', [])
+
+.controller('UibTabsetController', ['$scope', function ($scope) {
+ var ctrl = this,
+ oldIndex;
+ ctrl.tabs = [];
+
+ ctrl.select = function(index, evt) {
+ if (!destroyed) {
+ var previousIndex = findTabIndex(oldIndex);
+ var previousSelected = ctrl.tabs[previousIndex];
+ if (previousSelected) {
+ previousSelected.tab.onDeselect({
+ $event: evt,
+ $selectedIndex: index
+ });
+ if (evt && evt.isDefaultPrevented()) {
+ return;
+ }
+ previousSelected.tab.active = false;
+ }
+
+ var selected = ctrl.tabs[index];
+ if (selected) {
+ selected.tab.onSelect({
+ $event: evt
+ });
+ selected.tab.active = true;
+ ctrl.active = selected.index;
+ oldIndex = selected.index;
+ } else if (!selected && angular.isDefined(oldIndex)) {
+ ctrl.active = null;
+ oldIndex = null;
+ }
+ }
+ };
+
+ ctrl.addTab = function addTab(tab) {
+ ctrl.tabs.push({
+ tab: tab,
+ index: tab.index
+ });
+ ctrl.tabs.sort(function(t1, t2) {
+ if (t1.index > t2.index) {
+ return 1;
+ }
+
+ if (t1.index < t2.index) {
+ return -1;
+ }
+
+ return 0;
+ });
+
+ if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) {
+ var newActiveIndex = findTabIndex(tab.index);
+ ctrl.select(newActiveIndex);
+ }
+ };
+
+ ctrl.removeTab = function removeTab(tab) {
+ var index;
+ for (var i = 0; i < ctrl.tabs.length; i++) {
+ if (ctrl.tabs[i].tab === tab) {
+ index = i;
+ break;
+ }
+ }
+
+ if (ctrl.tabs[index].index === ctrl.active) {
+ var newActiveTabIndex = index === ctrl.tabs.length - 1 ?
+ index - 1 : index + 1 % ctrl.tabs.length;
+ ctrl.select(newActiveTabIndex);
+ }
+
+ ctrl.tabs.splice(index, 1);
+ };
+
+ $scope.$watch('tabset.active', function(val) {
+ if (angular.isDefined(val) && val !== oldIndex) {
+ ctrl.select(findTabIndex(val));
+ }
+ });
+
+ var destroyed;
+ $scope.$on('$destroy', function() {
+ destroyed = true;
+ });
+
+ function findTabIndex(index) {
+ for (var i = 0; i < ctrl.tabs.length; i++) {
+ if (ctrl.tabs[i].index === index) {
+ return i;
+ }
+ }
+ }
+}])
+
+.directive('uibTabset', function() {
+ return {
+ transclude: true,
+ replace: true,
+ scope: {},
+ bindToController: {
+ active: '=?',
+ type: '@'
+ },
+ controller: 'UibTabsetController',
+ controllerAs: 'tabset',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/tabs/tabset.html';
+ },
+ link: function(scope, element, attrs) {
+ scope.vertical = angular.isDefined(attrs.vertical) ?
+ scope.$parent.$eval(attrs.vertical) : false;
+ scope.justified = angular.isDefined(attrs.justified) ?
+ scope.$parent.$eval(attrs.justified) : false;
+ }
+ };
+})
+
+.directive('uibTab', ['$parse', function($parse) {
+ return {
+ require: '^uibTabset',
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'uib/template/tabs/tab.html';
+ },
+ transclude: true,
+ scope: {
+ heading: '@',
+ index: '=?',
+ classes: '@?',
+ onSelect: '&select', //This callback is called in contentHeadingTransclude
+ //once it inserts the tab's content into the dom
+ onDeselect: '&deselect'
+ },
+ controller: function() {
+ //Empty controller so other directives can require being 'under' a tab
+ },
+ controllerAs: 'tab',
+ link: function(scope, elm, attrs, tabsetCtrl, transclude) {
+ scope.disabled = false;
+ if (attrs.disable) {
+ scope.$parent.$watch($parse(attrs.disable), function(value) {
+ scope.disabled = !! value;
+ });
+ }
+
+ if (angular.isUndefined(attrs.index)) {
+ if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) {
+ scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1;
+ } else {
+ scope.index = 0;
+ }
+ }
+
+ if (angular.isUndefined(attrs.classes)) {
+ scope.classes = '';
+ }
+
+ scope.select = function(evt) {
+ if (!scope.disabled) {
+ var index;
+ for (var i = 0; i < tabsetCtrl.tabs.length; i++) {
+ if (tabsetCtrl.tabs[i].tab === scope) {
+ index = i;
+ break;
+ }
+ }
+
+ tabsetCtrl.select(index, evt);
+ }
+ };
+
+ tabsetCtrl.addTab(scope);
+ scope.$on('$destroy', function() {
+ tabsetCtrl.removeTab(scope);
+ });
+
+ //We need to transclude later, once the content container is ready.
+ //when this link happens, we're inside a tab heading.
+ scope.$transcludeFn = transclude;
+ }
+ };
+}])
+
+.directive('uibTabHeadingTransclude', function() {
+ return {
+ restrict: 'A',
+ require: '^uibTab',
+ link: function(scope, elm) {
+ scope.$watch('headingElement', function updateHeadingElement(heading) {
+ if (heading) {
+ elm.html('');
+ elm.append(heading);
+ }
+ });
+ }
+ };
+})
+
+.directive('uibTabContentTransclude', function() {
+ return {
+ restrict: 'A',
+ require: '^uibTabset',
+ link: function(scope, elm, attrs) {
+ var tab = scope.$eval(attrs.uibTabContentTransclude).tab;
+
+ //Now our tab is ready to be transcluded: both the tab heading area
+ //and the tab content area are loaded. Transclude 'em both.
+ tab.$transcludeFn(tab.$parent, function(contents) {
+ angular.forEach(contents, function(node) {
+ if (isTabHeading(node)) {
+ //Let tabHeadingTransclude know.
+ tab.headingElement = node;
+ } else {
+ elm.append(node);
+ }
+ });
+ });
+ }
+ };
+
+ function isTabHeading(node) {
+ return node.tagName && (
+ node.hasAttribute('uib-tab-heading') ||
+ node.hasAttribute('data-uib-tab-heading') ||
+ node.hasAttribute('x-uib-tab-heading') ||
+ node.tagName.toLowerCase() === 'uib-tab-heading' ||
+ node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
+ node.tagName.toLowerCase() === 'x-uib-tab-heading' ||
+ node.tagName.toLowerCase() === 'uib:tab-heading'
+ );
+ }
+});
+
+angular.module('ui.bootstrap.timepicker', [])
+
+.constant('uibTimepickerConfig', {
+ hourStep: 1,
+ minuteStep: 1,
+ secondStep: 1,
+ showMeridian: true,
+ showSeconds: false,
+ meridians: null,
+ readonlyInput: false,
+ mousewheel: true,
+ arrowkeys: true,
+ showSpinners: true,
+ templateUrl: 'uib/template/timepicker/timepicker.html'
+})
+
+.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
+ var selected = new Date(),
+ watchers = [],
+ ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
+ meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS,
+ padHours = angular.isDefined($attrs.padHours) ? $scope.$parent.$eval($attrs.padHours) : true;
+
+ $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
+ $element.removeAttr('tabindex');
+
+ this.init = function(ngModelCtrl_, inputs) {
+ ngModelCtrl = ngModelCtrl_;
+ ngModelCtrl.$render = this.render;
+
+ ngModelCtrl.$formatters.unshift(function(modelValue) {
+ return modelValue ? new Date(modelValue) : null;
+ });
+
+ var hoursInputEl = inputs.eq(0),
+ minutesInputEl = inputs.eq(1),
+ secondsInputEl = inputs.eq(2);
+
+ var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
+
+ if (mousewheel) {
+ this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
+ }
+
+ var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
+ if (arrowkeys) {
+ this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
+ }
+
+ $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
+ this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
+ };
+
+ var hourStep = timepickerConfig.hourStep;
+ if ($attrs.hourStep) {
+ watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) {
+ hourStep = +value;
+ }));
+ }
+
+ var minuteStep = timepickerConfig.minuteStep;
+ if ($attrs.minuteStep) {
+ watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
+ minuteStep = +value;
+ }));
+ }
+
+ var min;
+ watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) {
+ var dt = new Date(value);
+ min = isNaN(dt) ? undefined : dt;
+ }));
+
+ var max;
+ watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) {
+ var dt = new Date(value);
+ max = isNaN(dt) ? undefined : dt;
+ }));
+
+ var disabled = false;
+ if ($attrs.ngDisabled) {
+ watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
+ disabled = value;
+ }));
+ }
+
+ $scope.noIncrementHours = function() {
+ var incrementedSelected = addMinutes(selected, hourStep * 60);
+ return disabled || incrementedSelected > max ||
+ incrementedSelected < selected && incrementedSelected < min;
+ };
+
+ $scope.noDecrementHours = function() {
+ var decrementedSelected = addMinutes(selected, -hourStep * 60);
+ return disabled || decrementedSelected < min ||
+ decrementedSelected > selected && decrementedSelected > max;
+ };
+
+ $scope.noIncrementMinutes = function() {
+ var incrementedSelected = addMinutes(selected, minuteStep);
+ return disabled || incrementedSelected > max ||
+ incrementedSelected < selected && incrementedSelected < min;
+ };
+
+ $scope.noDecrementMinutes = function() {
+ var decrementedSelected = addMinutes(selected, -minuteStep);
+ return disabled || decrementedSelected < min ||
+ decrementedSelected > selected && decrementedSelected > max;
+ };
+
+ $scope.noIncrementSeconds = function() {
+ var incrementedSelected = addSeconds(selected, secondStep);
+ return disabled || incrementedSelected > max ||
+ incrementedSelected < selected && incrementedSelected < min;
+ };
+
+ $scope.noDecrementSeconds = function() {
+ var decrementedSelected = addSeconds(selected, -secondStep);
+ return disabled || decrementedSelected < min ||
+ decrementedSelected > selected && decrementedSelected > max;
+ };
+
+ $scope.noToggleMeridian = function() {
+ if (selected.getHours() < 12) {
+ return disabled || addMinutes(selected, 12 * 60) > max;
+ }
+
+ return disabled || addMinutes(selected, -12 * 60) < min;
+ };
+
+ var secondStep = timepickerConfig.secondStep;
+ if ($attrs.secondStep) {
+ watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) {
+ secondStep = +value;
+ }));
+ }
+
+ $scope.showSeconds = timepickerConfig.showSeconds;
+ if ($attrs.showSeconds) {
+ watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
+ $scope.showSeconds = !!value;
+ }));
+ }
+
+ // 12H / 24H mode
+ $scope.showMeridian = timepickerConfig.showMeridian;
+ if ($attrs.showMeridian) {
+ watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
+ $scope.showMeridian = !!value;
+
+ if (ngModelCtrl.$error.time) {
+ // Evaluate from template
+ var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
+ if (angular.isDefined(hours) && angular.isDefined(minutes)) {
+ selected.setHours(hours);
+ refresh();
+ }
+ } else {
+ updateTemplate();
+ }
+ }));
+ }
+
+ // Get $scope.hours in 24H mode if valid
+ function getHoursFromTemplate() {
+ var hours = +$scope.hours;
+ var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
+ hours >= 0 && hours < 24;
+ if (!valid || $scope.hours === '') {
+ return undefined;
+ }
+
+ if ($scope.showMeridian) {
+ if (hours === 12) {
+ hours = 0;
+ }
+ if ($scope.meridian === meridians[1]) {
+ hours = hours + 12;
+ }
+ }
+ return hours;
+ }
+
+ function getMinutesFromTemplate() {
+ var minutes = +$scope.minutes;
+ var valid = minutes >= 0 && minutes < 60;
+ if (!valid || $scope.minutes === '') {
+ return undefined;
+ }
+ return minutes;
+ }
+
+ function getSecondsFromTemplate() {
+ var seconds = +$scope.seconds;
+ return seconds >= 0 && seconds < 60 ? seconds : undefined;
+ }
+
+ function pad(value, noPad) {
+ if (value === null) {
+ return '';
+ }
+
+ return angular.isDefined(value) && value.toString().length < 2 && !noPad ?
+ '0' + value : value.toString();
+ }
+
+ // Respond on mousewheel spin
+ this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
+ var isScrollingUp = function(e) {
+ if (e.originalEvent) {
+ e = e.originalEvent;
+ }
+ //pick correct delta variable depending on event
+ var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY;
+ return e.detail || delta > 0;
+ };
+
+ hoursInputEl.bind('mousewheel wheel', function(e) {
+ if (!disabled) {
+ $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
+ }
+ e.preventDefault();
+ });
+
+ minutesInputEl.bind('mousewheel wheel', function(e) {
+ if (!disabled) {
+ $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
+ }
+ e.preventDefault();
+ });
+
+ secondsInputEl.bind('mousewheel wheel', function(e) {
+ if (!disabled) {
+ $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
+ }
+ e.preventDefault();
+ });
+ };
+
+ // Respond on up/down arrowkeys
+ this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
+ hoursInputEl.bind('keydown', function(e) {
+ if (!disabled) {
+ if (e.which === 38) { // up
+ e.preventDefault();
+ $scope.incrementHours();
+ $scope.$apply();
+ } else if (e.which === 40) { // down
+ e.preventDefault();
+ $scope.decrementHours();
+ $scope.$apply();
+ }
+ }
+ });
+
+ minutesInputEl.bind('keydown', function(e) {
+ if (!disabled) {
+ if (e.which === 38) { // up
+ e.preventDefault();
+ $scope.incrementMinutes();
+ $scope.$apply();
+ } else if (e.which === 40) { // down
+ e.preventDefault();
+ $scope.decrementMinutes();
+ $scope.$apply();
+ }
+ }
+ });
+
+ secondsInputEl.bind('keydown', function(e) {
+ if (!disabled) {
+ if (e.which === 38) { // up
+ e.preventDefault();
+ $scope.incrementSeconds();
+ $scope.$apply();
+ } else if (e.which === 40) { // down
+ e.preventDefault();
+ $scope.decrementSeconds();
+ $scope.$apply();
+ }
+ }
+ });
+ };
+
+ this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
+ if ($scope.readonlyInput) {
+ $scope.updateHours = angular.noop;
+ $scope.updateMinutes = angular.noop;
+ $scope.updateSeconds = angular.noop;
+ return;
+ }
+
+ var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
+ ngModelCtrl.$setViewValue(null);
+ ngModelCtrl.$setValidity('time', false);
+ if (angular.isDefined(invalidHours)) {
+ $scope.invalidHours = invalidHours;
+ }
+
+ if (angular.isDefined(invalidMinutes)) {
+ $scope.invalidMinutes = invalidMinutes;
+ }
+
+ if (angular.isDefined(invalidSeconds)) {
+ $scope.invalidSeconds = invalidSeconds;
+ }
+ };
+
+ $scope.updateHours = function() {
+ var hours = getHoursFromTemplate(),
+ minutes = getMinutesFromTemplate();
+
+ ngModelCtrl.$setDirty();
+
+ if (angular.isDefined(hours) && angular.isDefined(minutes)) {
+ selected.setHours(hours);
+ selected.setMinutes(minutes);
+ if (selected < min || selected > max) {
+ invalidate(true);
+ } else {
+ refresh('h');
+ }
+ } else {
+ invalidate(true);
+ }
+ };
+
+ hoursInputEl.bind('blur', function(e) {
+ ngModelCtrl.$setTouched();
+ if (modelIsEmpty()) {
+ makeValid();
+ } else if ($scope.hours === null || $scope.hours === '') {
+ invalidate(true);
+ } else if (!$scope.invalidHours && $scope.hours < 10) {
+ $scope.$apply(function() {
+ $scope.hours = pad($scope.hours, !padHours);
+ });
+ }
+ });
+
+ $scope.updateMinutes = function() {
+ var minutes = getMinutesFromTemplate(),
+ hours = getHoursFromTemplate();
+
+ ngModelCtrl.$setDirty();
+
+ if (angular.isDefined(minutes) && angular.isDefined(hours)) {
+ selected.setHours(hours);
+ selected.setMinutes(minutes);
+ if (selected < min || selected > max) {
+ invalidate(undefined, true);
+ } else {
+ refresh('m');
+ }
+ } else {
+ invalidate(undefined, true);
+ }
+ };
+
+ minutesInputEl.bind('blur', function(e) {
+ ngModelCtrl.$setTouched();
+ if (modelIsEmpty()) {
+ makeValid();
+ } else if ($scope.minutes === null) {
+ invalidate(undefined, true);
+ } else if (!$scope.invalidMinutes && $scope.minutes < 10) {
+ $scope.$apply(function() {
+ $scope.minutes = pad($scope.minutes);
+ });
+ }
+ });
+
+ $scope.updateSeconds = function() {
+ var seconds = getSecondsFromTemplate();
+
+ ngModelCtrl.$setDirty();
+
+ if (angular.isDefined(seconds)) {
+ selected.setSeconds(seconds);
+ refresh('s');
+ } else {
+ invalidate(undefined, undefined, true);
+ }
+ };
+
+ secondsInputEl.bind('blur', function(e) {
+ if (modelIsEmpty()) {
+ makeValid();
+ } else if (!$scope.invalidSeconds && $scope.seconds < 10) {
+ $scope.$apply( function() {
+ $scope.seconds = pad($scope.seconds);
+ });
+ }
+ });
+
+ };
+
+ this.render = function() {
+ var date = ngModelCtrl.$viewValue;
+
+ if (isNaN(date)) {
+ ngModelCtrl.$setValidity('time', false);
+ $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
+ } else {
+ if (date) {
+ selected = date;
+ }
+
+ if (selected < min || selected > max) {
+ ngModelCtrl.$setValidity('time', false);
+ $scope.invalidHours = true;
+ $scope.invalidMinutes = true;
+ } else {
+ makeValid();
+ }
+ updateTemplate();
+ }
+ };
+
+ // Call internally when we know that model is valid.
+ function refresh(keyboardChange) {
+ makeValid();
+ ngModelCtrl.$setViewValue(new Date(selected));
+ updateTemplate(keyboardChange);
+ }
+
+ function makeValid() {
+ ngModelCtrl.$setValidity('time', true);
+ $scope.invalidHours = false;
+ $scope.invalidMinutes = false;
+ $scope.invalidSeconds = false;
+ }
+
+ function updateTemplate(keyboardChange) {
+ if (!ngModelCtrl.$modelValue) {
+ $scope.hours = null;
+ $scope.minutes = null;
+ $scope.seconds = null;
+ $scope.meridian = meridians[0];
+ } else {
+ var hours = selected.getHours(),
+ minutes = selected.getMinutes(),
+ seconds = selected.getSeconds();
+
+ if ($scope.showMeridian) {
+ hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system
+ }
+
+ $scope.hours = keyboardChange === 'h' ? hours : pad(hours, !padHours);
+ if (keyboardChange !== 'm') {
+ $scope.minutes = pad(minutes);
+ }
+ $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
+
+ if (keyboardChange !== 's') {
+ $scope.seconds = pad(seconds);
+ }
+ $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
+ }
+ }
+
+ function addSecondsToSelected(seconds) {
+ selected = addSeconds(selected, seconds);
+ refresh();
+ }
+
+ function addMinutes(selected, minutes) {
+ return addSeconds(selected, minutes*60);
+ }
+
+ function addSeconds(date, seconds) {
+ var dt = new Date(date.getTime() + seconds * 1000);
+ var newDate = new Date(date);
+ newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
+ return newDate;
+ }
+
+ function modelIsEmpty() {
+ return ($scope.hours === null || $scope.hours === '') &&
+ ($scope.minutes === null || $scope.minutes === '') &&
+ (!$scope.showSeconds || $scope.showSeconds && ($scope.seconds === null || $scope.seconds === ''));
+ }
+
+ $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
+ $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
+
+ $scope.incrementHours = function() {
+ if (!$scope.noIncrementHours()) {
+ addSecondsToSelected(hourStep * 60 * 60);
+ }
+ };
+
+ $scope.decrementHours = function() {
+ if (!$scope.noDecrementHours()) {
+ addSecondsToSelected(-hourStep * 60 * 60);
+ }
+ };
+
+ $scope.incrementMinutes = function() {
+ if (!$scope.noIncrementMinutes()) {
+ addSecondsToSelected(minuteStep * 60);
+ }
+ };
+
+ $scope.decrementMinutes = function() {
+ if (!$scope.noDecrementMinutes()) {
+ addSecondsToSelected(-minuteStep * 60);
+ }
+ };
+
+ $scope.incrementSeconds = function() {
+ if (!$scope.noIncrementSeconds()) {
+ addSecondsToSelected(secondStep);
+ }
+ };
+
+ $scope.decrementSeconds = function() {
+ if (!$scope.noDecrementSeconds()) {
+ addSecondsToSelected(-secondStep);
+ }
+ };
+
+ $scope.toggleMeridian = function() {
+ var minutes = getMinutesFromTemplate(),
+ hours = getHoursFromTemplate();
+
+ if (!$scope.noToggleMeridian()) {
+ if (angular.isDefined(minutes) && angular.isDefined(hours)) {
+ addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60));
+ } else {
+ $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0];
+ }
+ }
+ };
+
+ $scope.blur = function() {
+ ngModelCtrl.$setTouched();
+ };
+
+ $scope.$on('$destroy', function() {
+ while (watchers.length) {
+ watchers.shift()();
+ }
+ });
+}])
+
+.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) {
+ return {
+ require: ['uibTimepicker', '?^ngModel'],
+ controller: 'UibTimepickerController',
+ controllerAs: 'timepicker',
+ replace: true,
+ scope: {},
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || uibTimepickerConfig.templateUrl;
+ },
+ link: function(scope, element, attrs, ctrls) {
+ var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ if (ngModelCtrl) {
+ timepickerCtrl.init(ngModelCtrl, element.find('input'));
+ }
+ }
+ };
+}]);
+
+angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
+
+/**
+ * A helper service that can parse typeahead's syntax (string provided by users)
+ * Extracted to a separate service for ease of unit testing
+ */
+ .factory('uibTypeaheadParser', ['$parse', function($parse) {
+ // 00000111000000000000022200000000000000003333333333333330000000000044000
+ var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
+ return {
+ parse: function(input) {
+ var match = input.match(TYPEAHEAD_REGEXP);
+ if (!match) {
+ throw new Error(
+ 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
+ ' but got "' + input + '".');
+ }
+
+ return {
+ itemName: match[3],
+ source: $parse(match[4]),
+ viewMapper: $parse(match[2] || match[1]),
+ modelMapper: $parse(match[1])
+ };
+ }
+ };
+ }])
+
+ .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
+ function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
+ var HOT_KEYS = [9, 13, 27, 38, 40];
+ var eventDebounceTime = 200;
+ var modelCtrl, ngModelOptions;
+ //SUPPORTED ATTRIBUTES (OPTIONS)
+
+ //minimal no of characters that needs to be entered before typeahead kicks-in
+ var minLength = originalScope.$eval(attrs.typeaheadMinLength);
+ if (!minLength && minLength !== 0) {
+ minLength = 1;
+ }
+
+ originalScope.$watch(attrs.typeaheadMinLength, function (newVal) {
+ minLength = !newVal && newVal !== 0 ? 1 : newVal;
+ });
+
+ //minimal wait time after last character typed before typeahead kicks-in
+ var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
+
+ //should it restrict model values to the ones selected from the popup only?
+ var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
+ originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
+ isEditable = newVal !== false;
+ });
+
+ //binding to a variable that indicates if matches are being retrieved asynchronously
+ var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
+
+ //a function to determine if an event should cause selection
+ var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) {
+ var evt = vals.$event;
+ return evt.which === 13 || evt.which === 9;
+ };
+
+ //a callback executed when a match is selected
+ var onSelectCallback = $parse(attrs.typeaheadOnSelect);
+
+ //should it select highlighted popup value when losing focus?
+ var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
+
+ //binding to a variable that indicates if there were no results after the query is completed
+ var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
+
+ var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
+
+ var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
+
+ var appendTo = attrs.typeaheadAppendTo ?
+ originalScope.$eval(attrs.typeaheadAppendTo) : null;
+
+ var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
+
+ //If input matches an item of the list exactly, select it automatically
+ var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
+
+ //binding to a variable that indicates if dropdown is open
+ var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
+
+ var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
+
+ //INTERNAL VARIABLES
+
+ //model setter executed upon match selection
+ var parsedModel = $parse(attrs.ngModel);
+ var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
+ var $setModelValue = function(scope, newValue) {
+ if (angular.isFunction(parsedModel(originalScope)) &&
+ ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
+ return invokeModelSetter(scope, {$$$p: newValue});
+ }
+
+ return parsedModel.assign(scope, newValue);
+ };
+
+ //expressions used by typeahead
+ var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
+
+ var hasFocus;
+
+ //Used to avoid bug in iOS webview where iOS keyboard does not fire
+ //mousedown & mouseup events
+ //Issue #3699
+ var selected;
+
+ //create a child scope for the typeahead directive so we are not polluting original scope
+ //with typeahead-specific data (matches, query etc.)
+ var scope = originalScope.$new();
+ var offDestroy = originalScope.$on('$destroy', function() {
+ scope.$destroy();
+ });
+ scope.$on('$destroy', offDestroy);
+
+ // WAI-ARIA
+ var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
+ element.attr({
+ 'aria-autocomplete': 'list',
+ 'aria-expanded': false,
+ 'aria-owns': popupId
+ });
+
+ var inputsContainer, hintInputElem;
+ //add read-only input to show hint
+ if (showHint) {
+ inputsContainer = angular.element('');
+ inputsContainer.css('position', 'relative');
+ element.after(inputsContainer);
+ hintInputElem = element.clone();
+ hintInputElem.attr('placeholder', '');
+ hintInputElem.attr('tabindex', '-1');
+ hintInputElem.val('');
+ hintInputElem.css({
+ 'position': 'absolute',
+ 'top': '0px',
+ 'left': '0px',
+ 'border-color': 'transparent',
+ 'box-shadow': 'none',
+ 'opacity': 1,
+ 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
+ 'color': '#999'
+ });
+ element.css({
+ 'position': 'relative',
+ 'vertical-align': 'top',
+ 'background-color': 'transparent'
+ });
+ inputsContainer.append(hintInputElem);
+ hintInputElem.after(element);
+ }
+
+ //pop-up element used to display matches
+ var popUpEl = angular.element('');
+ popUpEl.attr({
+ id: popupId,
+ matches: 'matches',
+ active: 'activeIdx',
+ select: 'select(activeIdx, evt)',
+ 'move-in-progress': 'moveInProgress',
+ query: 'query',
+ position: 'position',
+ 'assign-is-open': 'assignIsOpen(isOpen)',
+ debounce: 'debounceUpdate'
+ });
+ //custom item template
+ if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
+ popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
+ }
+
+ if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
+ popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
+ }
+
+ var resetHint = function() {
+ if (showHint) {
+ hintInputElem.val('');
+ }
+ };
+
+ var resetMatches = function() {
+ scope.matches = [];
+ scope.activeIdx = -1;
+ element.attr('aria-expanded', false);
+ resetHint();
+ };
+
+ var getMatchId = function(index) {
+ return popupId + '-option-' + index;
+ };
+
+ // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
+ // This attribute is added or removed automatically when the `activeIdx` changes.
+ scope.$watch('activeIdx', function(index) {
+ if (index < 0) {
+ element.removeAttr('aria-activedescendant');
+ } else {
+ element.attr('aria-activedescendant', getMatchId(index));
+ }
+ });
+
+ var inputIsExactMatch = function(inputValue, index) {
+ if (scope.matches.length > index && inputValue) {
+ return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
+ }
+
+ return false;
+ };
+
+ var getMatchesAsync = function(inputValue, evt) {
+ var locals = {$viewValue: inputValue};
+ isLoadingSetter(originalScope, true);
+ isNoResultsSetter(originalScope, false);
+ $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
+ //it might happen that several async queries were in progress if a user were typing fast
+ //but we are interested only in responses that correspond to the current view value
+ var onCurrentRequest = inputValue === modelCtrl.$viewValue;
+ if (onCurrentRequest && hasFocus) {
+ if (matches && matches.length > 0) {
+ scope.activeIdx = focusFirst ? 0 : -1;
+ isNoResultsSetter(originalScope, false);
+ scope.matches.length = 0;
+
+ //transform labels
+ for (var i = 0; i < matches.length; i++) {
+ locals[parserResult.itemName] = matches[i];
+ scope.matches.push({
+ id: getMatchId(i),
+ label: parserResult.viewMapper(scope, locals),
+ model: matches[i]
+ });
+ }
+
+ scope.query = inputValue;
+ //position pop-up with matches - we need to re-calculate its position each time we are opening a window
+ //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
+ //due to other elements being rendered
+ recalculatePosition();
+
+ element.attr('aria-expanded', true);
+
+ //Select the single remaining option if user input matches
+ if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
+ if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
+ $$debounce(function() {
+ scope.select(0, evt);
+ }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
+ } else {
+ scope.select(0, evt);
+ }
+ }
+
+ if (showHint) {
+ var firstLabel = scope.matches[0].label;
+ if (angular.isString(inputValue) &&
+ inputValue.length > 0 &&
+ firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
+ hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
+ } else {
+ hintInputElem.val('');
+ }
+ }
+ } else {
+ resetMatches();
+ isNoResultsSetter(originalScope, true);
+ }
+ }
+ if (onCurrentRequest) {
+ isLoadingSetter(originalScope, false);
+ }
+ }, function() {
+ resetMatches();
+ isLoadingSetter(originalScope, false);
+ isNoResultsSetter(originalScope, true);
+ });
+ };
+
+ // bind events only if appendToBody params exist - performance feature
+ if (appendToBody) {
+ angular.element($window).on('resize', fireRecalculating);
+ $document.find('body').on('scroll', fireRecalculating);
+ }
+
+ // Declare the debounced function outside recalculating for
+ // proper debouncing
+ var debouncedRecalculate = $$debounce(function() {
+ // if popup is visible
+ if (scope.matches.length) {
+ recalculatePosition();
+ }
+
+ scope.moveInProgress = false;
+ }, eventDebounceTime);
+
+ // Default progress type
+ scope.moveInProgress = false;
+
+ function fireRecalculating() {
+ if (!scope.moveInProgress) {
+ scope.moveInProgress = true;
+ scope.$digest();
+ }
+
+ debouncedRecalculate();
+ }
+
+ // recalculate actual position and set new values to scope
+ // after digest loop is popup in right position
+ function recalculatePosition() {
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
+ scope.position.top += element.prop('offsetHeight');
+ }
+
+ //we need to propagate user's query so we can higlight matches
+ scope.query = undefined;
+
+ //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
+ var timeoutPromise;
+
+ var scheduleSearchWithTimeout = function(inputValue) {
+ timeoutPromise = $timeout(function() {
+ getMatchesAsync(inputValue);
+ }, waitTime);
+ };
+
+ var cancelPreviousTimeout = function() {
+ if (timeoutPromise) {
+ $timeout.cancel(timeoutPromise);
+ }
+ };
+
+ resetMatches();
+
+ scope.assignIsOpen = function (isOpen) {
+ isOpenSetter(originalScope, isOpen);
+ };
+
+ scope.select = function(activeIdx, evt) {
+ //called from within the $digest() cycle
+ var locals = {};
+ var model, item;
+
+ selected = true;
+ locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
+ model = parserResult.modelMapper(originalScope, locals);
+ $setModelValue(originalScope, model);
+ modelCtrl.$setValidity('editable', true);
+ modelCtrl.$setValidity('parse', true);
+
+ onSelectCallback(originalScope, {
+ $item: item,
+ $model: model,
+ $label: parserResult.viewMapper(originalScope, locals),
+ $event: evt
+ });
+
+ resetMatches();
+
+ //return focus to the input element if a match was selected via a mouse click event
+ // use timeout to avoid $rootScope:inprog error
+ if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
+ $timeout(function() { element[0].focus(); }, 0, false);
+ }
+ };
+
+ //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
+ element.on('keydown', function(evt) {
+ //typeahead is open and an "interesting" key was pressed
+ if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
+ return;
+ }
+
+ var shouldSelect = isSelectEvent(originalScope, {$event: evt});
+
+ /**
+ * if there's nothing selected (i.e. focusFirst) and enter or tab is hit
+ * or
+ * shift + tab is pressed to bring focus to the previous element
+ * then clear the results
+ */
+ if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) {
+ resetMatches();
+ scope.$digest();
+ return;
+ }
+
+ evt.preventDefault();
+ var target;
+ switch (evt.which) {
+ case 27: // escape
+ evt.stopPropagation();
+
+ resetMatches();
+ originalScope.$digest();
+ break;
+ case 38: // up arrow
+ scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
+ scope.$digest();
+ target = popUpEl.find('li')[scope.activeIdx];
+ target.parentNode.scrollTop = target.offsetTop;
+ break;
+ case 40: // down arrow
+ scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
+ scope.$digest();
+ target = popUpEl.find('li')[scope.activeIdx];
+ target.parentNode.scrollTop = target.offsetTop;
+ break;
+ default:
+ if (shouldSelect) {
+ scope.$apply(function() {
+ if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
+ $$debounce(function() {
+ scope.select(scope.activeIdx, evt);
+ }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
+ } else {
+ scope.select(scope.activeIdx, evt);
+ }
+ });
+ }
+ }
+ });
+
+ element.bind('focus', function (evt) {
+ hasFocus = true;
+ if (minLength === 0 && !modelCtrl.$viewValue) {
+ $timeout(function() {
+ getMatchesAsync(modelCtrl.$viewValue, evt);
+ }, 0);
+ }
+ });
+
+ element.bind('blur', function(evt) {
+ if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
+ selected = true;
+ scope.$apply(function() {
+ if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
+ $$debounce(function() {
+ scope.select(scope.activeIdx, evt);
+ }, scope.debounceUpdate.blur);
+ } else {
+ scope.select(scope.activeIdx, evt);
+ }
+ });
+ }
+ if (!isEditable && modelCtrl.$error.editable) {
+ modelCtrl.$setViewValue();
+ // Reset validity as we are clearing
+ modelCtrl.$setValidity('editable', true);
+ modelCtrl.$setValidity('parse', true);
+ element.val('');
+ }
+ hasFocus = false;
+ selected = false;
+ });
+
+ // Keep reference to click handler to unbind it.
+ var dismissClickHandler = function(evt) {
+ // Issue #3973
+ // Firefox treats right click as a click on document
+ if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
+ resetMatches();
+ if (!$rootScope.$$phase) {
+ originalScope.$digest();
+ }
+ }
+ };
+
+ $document.on('click', dismissClickHandler);
+
+ originalScope.$on('$destroy', function() {
+ $document.off('click', dismissClickHandler);
+ if (appendToBody || appendTo) {
+ $popup.remove();
+ }
+
+ if (appendToBody) {
+ angular.element($window).off('resize', fireRecalculating);
+ $document.find('body').off('scroll', fireRecalculating);
+ }
+ // Prevent jQuery cache memory leak
+ popUpEl.remove();
+
+ if (showHint) {
+ inputsContainer.remove();
+ }
+ });
+
+ var $popup = $compile(popUpEl)(scope);
+
+ if (appendToBody) {
+ $document.find('body').append($popup);
+ } else if (appendTo) {
+ angular.element(appendTo).eq(0).append($popup);
+ } else {
+ element.after($popup);
+ }
+
+ this.init = function(_modelCtrl, _ngModelOptions) {
+ modelCtrl = _modelCtrl;
+ ngModelOptions = _ngModelOptions;
+
+ scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
+
+ //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
+ //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
+ modelCtrl.$parsers.unshift(function(inputValue) {
+ hasFocus = true;
+
+ if (minLength === 0 || inputValue && inputValue.length >= minLength) {
+ if (waitTime > 0) {
+ cancelPreviousTimeout();
+ scheduleSearchWithTimeout(inputValue);
+ } else {
+ getMatchesAsync(inputValue);
+ }
+ } else {
+ isLoadingSetter(originalScope, false);
+ cancelPreviousTimeout();
+ resetMatches();
+ }
+
+ if (isEditable) {
+ return inputValue;
+ }
+
+ if (!inputValue) {
+ // Reset in case user had typed something previously.
+ modelCtrl.$setValidity('editable', true);
+ return null;
+ }
+
+ modelCtrl.$setValidity('editable', false);
+ return undefined;
+ });
+
+ modelCtrl.$formatters.push(function(modelValue) {
+ var candidateViewValue, emptyViewValue;
+ var locals = {};
+
+ // The validity may be set to false via $parsers (see above) if
+ // the model is restricted to selected values. If the model
+ // is set manually it is considered to be valid.
+ if (!isEditable) {
+ modelCtrl.$setValidity('editable', true);
+ }
+
+ if (inputFormatter) {
+ locals.$model = modelValue;
+ return inputFormatter(originalScope, locals);
+ }
+
+ //it might happen that we don't have enough info to properly render input value
+ //we need to check for this situation and simply return model value if we can't apply custom formatting
+ locals[parserResult.itemName] = modelValue;
+ candidateViewValue = parserResult.viewMapper(originalScope, locals);
+ locals[parserResult.itemName] = undefined;
+ emptyViewValue = parserResult.viewMapper(originalScope, locals);
+
+ return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
+ });
+ };
+ }])
+
+ .directive('uibTypeahead', function() {
+ return {
+ controller: 'UibTypeaheadController',
+ require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
+ link: function(originalScope, element, attrs, ctrls) {
+ ctrls[2].init(ctrls[0], ctrls[1]);
+ }
+ };
+ })
+
+ .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
+ return {
+ scope: {
+ matches: '=',
+ query: '=',
+ active: '=',
+ position: '&',
+ moveInProgress: '=',
+ select: '&',
+ assignIsOpen: '&',
+ debounce: '&'
+ },
+ replace: true,
+ templateUrl: function(element, attrs) {
+ return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
+ },
+ link: function(scope, element, attrs) {
+ scope.templateUrl = attrs.templateUrl;
+
+ scope.isOpen = function() {
+ var isDropdownOpen = scope.matches.length > 0;
+ scope.assignIsOpen({ isOpen: isDropdownOpen });
+ return isDropdownOpen;
+ };
+
+ scope.isActive = function(matchIdx) {
+ return scope.active === matchIdx;
+ };
+
+ scope.selectActive = function(matchIdx) {
+ scope.active = matchIdx;
+ };
+
+ scope.selectMatch = function(activeIdx, evt) {
+ var debounce = scope.debounce();
+ if (angular.isNumber(debounce) || angular.isObject(debounce)) {
+ $$debounce(function() {
+ scope.select({activeIdx: activeIdx, evt: evt});
+ }, angular.isNumber(debounce) ? debounce : debounce['default']);
+ } else {
+ scope.select({activeIdx: activeIdx, evt: evt});
+ }
+ };
+ }
+ };
+ }])
+
+ .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
+ return {
+ scope: {
+ index: '=',
+ match: '=',
+ query: '='
+ },
+ link: function(scope, element, attrs) {
+ var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
+ $templateRequest(tplUrl).then(function(tplContent) {
+ var tplEl = angular.element(tplContent.trim());
+ element.replaceWith(tplEl);
+ $compile(tplEl)(scope);
+ });
+ }
+ };
+ }])
+
+ .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
+ var isSanitizePresent;
+ isSanitizePresent = $injector.has('$sanitize');
+
+ function escapeRegexp(queryToEscape) {
+ // Regex: capture the whole query string and replace it with the string that will be used to match
+ // the results, for example if the capture is "a" the result will be \a
+ return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
+ }
+
+ function containsHtml(matchItem) {
+ return /<.*>/g.test(matchItem);
+ }
+
+ return function(matchItem, query) {
+ if (!isSanitizePresent && containsHtml(matchItem)) {
+ $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
+ }
+ matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
+ if (!isSanitizePresent) {
+ matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
+ }
+ return matchItem;
+ };
+ }]);
+angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibCarouselCss && angular.element(document).find('head').prepend(''); angular.$$uibCarouselCss = true; });
+angular.module('ui.bootstrap.datepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerCss = true; });
+angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend(''); angular.$$uibPositionCss = true; });
+angular.module('ui.bootstrap.datepickerPopup').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerpopupCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerpopupCss = true; });
+angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend(''); angular.$$uibTooltipCss = true; });
+angular.module('ui.bootstrap.timepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTimepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibTimepickerCss = true; });
+angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTypeaheadCss && angular.element(document).find('head').prepend(''); angular.$$uibTypeaheadCss = true; });
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap.min.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap.min.js
new file mode 100644
index 0000000000..c56583cf62
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-bootstrap/ui-bootstrap.min.js
@@ -0,0 +1,10 @@
+/*
+ * angular-ui-bootstrap
+ * http://angular-ui.github.io/bootstrap/
+
+ * Version: 1.3.3 - 2016-05-22
+ * License: MIT
+ */angular.module("ui.bootstrap",["ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.collapse",[]).directive("uibCollapse",["$animate","$q","$parse","$injector",function(a,b,c,d){var e=d.has("$animateCss")?d.get("$animateCss"):null;return{link:function(d,f,g){function h(){f.hasClass("collapse")&&f.hasClass("in")||b.resolve(l(d)).then(function(){f.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),e?e(f,{addClass:"in",easing:"ease",to:{height:f[0].scrollHeight+"px"}}).start()["finally"](i):a.addClass(f,"in",{to:{height:f[0].scrollHeight+"px"}}).then(i)})}function i(){f.removeClass("collapsing").addClass("collapse").css({height:"auto"}),m(d)}function j(){return f.hasClass("collapse")||f.hasClass("in")?void b.resolve(n(d)).then(function(){f.css({height:f[0].scrollHeight+"px"}).removeClass("collapse").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),e?e(f,{removeClass:"in",to:{height:"0"}}).start()["finally"](k):a.removeClass(f,"in",{to:{height:"0"}}).then(k)}):k()}function k(){f.css({height:"0"}),f.removeClass("collapsing").addClass("collapse"),o(d)}var l=c(g.expanding),m=c(g.expanded),n=c(g.collapsing),o=c(g.collapsed);d.$eval(g.uibCollapse)||f.addClass("in").addClass("collapse").attr("aria-expanded",!0).attr("aria-hidden",!1).css({height:"auto"}),d.$watch(g.uibCollapse,function(a){a?j():h()})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("uibAccordionConfig",{closeOthers:!0}).controller("UibAccordionController",["$scope","$attrs","uibAccordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(c){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("uibAccordion",function(){return{controller:"UibAccordionController",controllerAs:"accordion",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion.html"}}}).directive("uibAccordionGroup",function(){return{require:"^uibAccordion",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/accordion/accordion-group.html"},scope:{heading:"@",panelClass:"@?",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){d.addGroup(a),a.openClass=c.openClass||"panel-open",a.panelClass=c.panelClass||"panel-default",a.$watch("isOpen",function(c){b.toggleClass(a.openClass,!!c),c&&d.closeOthers(a)}),a.toggleOpen=function(b){a.isDisabled||b&&32!==b.which||(a.isOpen=!a.isOpen)};var e="accordiongroup-"+a.$id+"-"+Math.floor(1e4*Math.random());a.headingId=e+"-tab",a.panelId=e+"-panel"}}}).directive("uibAccordionHeading",function(){return{transclude:!0,template:"",replace:!0,require:"^uibAccordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,angular.noop))}}}).directive("uibAccordionTransclude",function(){function a(){return"uib-accordion-header,data-uib-accordion-header,x-uib-accordion-header,uib\\:accordion-header,[uib-accordion-header],[data-uib-accordion-header],[x-uib-accordion-header]"}return{require:"^uibAccordionGroup",link:function(b,c,d,e){b.$watch(function(){return e[d.uibAccordionTransclude]},function(b){if(b){var d=angular.element(c[0].querySelector(a()));d.html(""),d.append(b)}})}}}),angular.module("ui.bootstrap.alert",[]).controller("UibAlertController",["$scope","$attrs","$interpolate","$timeout",function(a,b,c,d){a.closeable=!!b.close;var e=angular.isDefined(b.dismissOnTimeout)?c(b.dismissOnTimeout)(a.$parent):null;e&&d(function(){a.close()},parseInt(e,10))}]).directive("uibAlert",function(){return{controller:"UibAlertController",controllerAs:"alert",templateUrl:function(a,b){return b.templateUrl||"uib/template/alert/alert.html"},transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}),angular.module("ui.bootstrap.buttons",[]).constant("uibButtonConfig",{activeClass:"active",toggleEvent:"click"}).controller("UibButtonsController",["uibButtonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("uibBtnRadio",["$parse",function(a){return{require:["uibBtnRadio","ngModel"],controller:"UibButtonsController",controllerAs:"buttons",link:function(b,c,d,e){var f=e[0],g=e[1],h=a(d.uibUncheckable);c.find("input").css({display:"none"}),g.$render=function(){c.toggleClass(f.activeClass,angular.equals(g.$modelValue,b.$eval(d.uibBtnRadio)))},c.on(f.toggleEvent,function(){if(!d.disabled){var a=c.hasClass(f.activeClass);a&&!angular.isDefined(d.uncheckable)||b.$apply(function(){g.$setViewValue(a?null:b.$eval(d.uibBtnRadio)),g.$render()})}}),d.uibUncheckable&&b.$watch(h,function(a){d.$set("uncheckable",a?"":void 0)})}}}]).directive("uibBtnCheckbox",function(){return{require:["uibBtnCheckbox","ngModel"],controller:"UibButtonsController",controllerAs:"button",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){return angular.isDefined(b)?a.$eval(b):c}var h=d[0],i=d[1];b.find("input").css({display:"none"}),i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.on(h.toggleEvent,function(){c.disabled||a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.carousel",[]).controller("UibCarouselController",["$scope","$element","$interval","$timeout","$animate",function(a,b,c,d,e){function f(){for(;t.length;)t.shift()}function g(a){for(var b=0;b1){q[d].element.data(r,c.direction);var j=p.getCurrentIndex();angular.isNumber(j)&&q[j].element&&q[j].element.data(r,c.direction),a.$currentTransition=!0,e.on("addClass",q[d].element,function(b,c){if("close"===c&&(a.$currentTransition=null,e.off("addClass",b),t.length)){var d=t.pop().slide,g=d.index,i=g>p.getCurrentIndex()?"next":"prev";f(),h(d,g,i)}})}a.active=c.index,s=c.index,g(d),l()}}function i(a){for(var b=0;b0&&(n=c(m,b))}function m(){var b=+a.interval;o&&!isNaN(b)&&b>0&&q.length?a.next():a.pause()}var n,o,p=this,q=p.slides=a.slides=[],r="uib-slideDirection",s=a.active,t=[],u=!1;p.addSlide=function(b,c){q.push({slide:b,element:c}),q.sort(function(a,b){return+a.slide.index-+b.slide.index}),(b.index===a.active||1===q.length&&!angular.isNumber(a.active))&&(a.$currentTransition&&(a.$currentTransition=null),s=b.index,a.active=b.index,g(s),p.select(q[i(b)]),1===q.length&&a.play())},p.getCurrentIndex=function(){for(var a=0;a0&&s===c?c>=q.length?(s=q.length-1,a.active=s,g(s),p.select(q[q.length-1])):(s=c,a.active=s,g(s),p.select(q[c])):s>c&&(s--,a.active=s),0===q.length&&(s=null,a.active=null,f())},p.select=a.select=function(b,c){var d=i(b.slide);void 0===c&&(c=d>p.getCurrentIndex()?"next":"prev"),b.slide.index===s||a.$currentTransition?b&&b.slide.index!==s&&a.$currentTransition&&t.push(q[d]):h(b.slide,d,c)},a.indexOfSlide=function(a){return+a.slide.index},a.isActive=function(b){return a.active===b.slide.index},a.isPrevDisabled=function(){return 0===a.active&&a.noWrap()},a.isNextDisabled=function(){return a.active===q.length-1&&a.noWrap()},a.pause=function(){a.noPause||(o=!1,j())},a.play=function(){o||(o=!0,l())},a.$on("$destroy",function(){u=!0,j()}),a.$watch("noTransition",function(a){e.enabled(b,!a)}),a.$watch("interval",l),a.$watchCollection("slides",k),a.$watch("active",function(a){if(angular.isNumber(a)&&s!==a){for(var b=0;b-1){var g=!1;a=a.split("");for(var h=f;h-1){a=a.split(""),e[f]="("+d.regex+")",a[f]="$";for(var g=f+1,h=f+d.key.length;h>g;g++)e[g]="",a[g]="$";a=a.join(""),c.push({index:f,key:d.key,apply:d[b],matcher:d.regex})}}),{regex:new RegExp("^"+e.join("")+"$"),map:d(c,"index")}}function f(a,b,c){return 1>c?!1:1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}function g(a){return parseInt(a,10)}function h(a,b){return a&&b?l(a,b):a}function i(a,b){return a&&b?l(a,b,!0):a}function j(a,b){a=a.replace(/:/g,"");var c=Date.parse("Jan 01, 1970 00:00:00 "+a)/6e4;return isNaN(c)?b:c}function k(a,b){return a=new Date(a.getTime()),a.setMinutes(a.getMinutes()+b),a}function l(a,b,c){c=c?-1:1;var d=a.getTimezoneOffset(),e=j(b,d);return k(a,c*(e-d))}var m,n,o=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){m=b.id,this.parsers={},this.formatters={},n=[{key:"yyyy",regex:"\\d{4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yyyy")}},{key:"yy",regex:"\\d{2}",apply:function(a){a=+a,this.year=69>a?a+2e3:a+1900},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"yy")}},{key:"y",regex:"\\d{1,4}",apply:function(a){this.year=+a},formatter:function(a){var b=new Date;return b.setFullYear(Math.abs(a.getFullYear())),c(b,"y")}},{key:"M!",regex:"0?[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){var b=a.getMonth();return/^[0-9]$/.test(b)?c(a,"MM"):c(a,"M")}},{key:"MMMM",regex:b.DATETIME_FORMATS.MONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.MONTH.indexOf(a)},formatter:function(a){return c(a,"MMMM")}},{key:"MMM",regex:b.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.SHORTMONTH.indexOf(a)},formatter:function(a){return c(a,"MMM")}},{key:"MM",regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"MM")}},{key:"M",regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1},formatter:function(a){return c(a,"M")}},{key:"d!",regex:"[0-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){var b=a.getDate();return/^[1-9]$/.test(b)?c(a,"dd"):c(a,"d")}},{key:"dd",regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"dd")}},{key:"d",regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a},formatter:function(a){return c(a,"d")}},{key:"EEEE",regex:b.DATETIME_FORMATS.DAY.join("|"),formatter:function(a){return c(a,"EEEE")}},{key:"EEE",regex:b.DATETIME_FORMATS.SHORTDAY.join("|"),formatter:function(a){return c(a,"EEE")}},{key:"HH",regex:"(?:0|1)[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"HH")}},{key:"hh",regex:"0[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"hh")}},{key:"H",regex:"1?[0-9]|2[0-3]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"H")}},{key:"h",regex:"[0-9]|1[0-2]",apply:function(a){this.hours=+a},formatter:function(a){return c(a,"h")}},{key:"mm",regex:"[0-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"mm")}},{key:"m",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.minutes=+a},formatter:function(a){return c(a,"m")}},{key:"sss",regex:"[0-9][0-9][0-9]",apply:function(a){this.milliseconds=+a},formatter:function(a){return c(a,"sss")}},{key:"ss",regex:"[0-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"ss")}},{key:"s",regex:"[0-9]|[1-5][0-9]",apply:function(a){this.seconds=+a},formatter:function(a){return c(a,"s")}},{key:"a",regex:b.DATETIME_FORMATS.AMPMS.join("|"),apply:function(a){12===this.hours&&(this.hours=0),"PM"===a&&(this.hours+=12)},formatter:function(a){return c(a,"a")}},{key:"Z",regex:"[+-]\\d{4}",apply:function(a){var b=a.match(/([+-])(\d{2})(\d{2})/),c=b[1],d=b[2],e=b[3];this.hours+=g(c+d),this.minutes+=g(c+e)},formatter:function(a){return c(a,"Z")}},{key:"ww",regex:"[0-4][0-9]|5[0-3]",formatter:function(a){return c(a,"ww")}},{key:"w",regex:"[0-9]|[1-4][0-9]|5[0-3]",formatter:function(a){return c(a,"w")}},{key:"GGGG",regex:b.DATETIME_FORMATS.ERANAMES.join("|").replace(/\s/g,"\\s"),formatter:function(a){return c(a,"GGGG")}},{key:"GGG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GGG")}},{key:"GG",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"GG")}},{key:"G",regex:b.DATETIME_FORMATS.ERAS.join("|"),formatter:function(a){return c(a,"G")}}]},this.init(),this.filter=function(a,c){if(!angular.isDate(a)||isNaN(a)||!c)return"";c=b.DATETIME_FORMATS[c]||c,b.id!==m&&this.init(),this.formatters[c]||(this.formatters[c]=e(c,"formatter"));var d=this.formatters[c],f=d.map,g=c;return f.reduce(function(b,c,d){var e=g.match(new RegExp("(.*)"+c.key));e&&angular.isString(e[1])&&(b+=e[1],g=g.replace(e[1]+c.key,""));var h=d===f.length-1?g:"";return c.apply?b+c.apply.call(null,a)+h:b+h},"")},this.parse=function(c,d,g){if(!angular.isString(c)||!d)return c;d=b.DATETIME_FORMATS[d]||d,d=d.replace(o,"\\$&"),b.id!==m&&this.init(),this.parsers[d]||(this.parsers[d]=e(d,"apply"));var h=this.parsers[d],i=h.regex,j=h.map,k=c.match(i),l=!1;if(k&&k.length){var n,p;angular.isDate(g)&&!isNaN(g.getTime())?n={year:g.getFullYear(),month:g.getMonth(),date:g.getDate(),hours:g.getHours(),minutes:g.getMinutes(),seconds:g.getSeconds(),milliseconds:g.getMilliseconds()}:(g&&a.warn("dateparser:","baseDate is not a valid date"),n={year:1900,month:0,date:1,hours:0,minutes:0,seconds:0,milliseconds:0});for(var q=1,r=k.length;r>q;q++){var s=j[q-1];"Z"===s.matcher&&(l=!0),s.apply&&s.apply.call(n,k[q])}var t=l?Date.prototype.setUTCFullYear:Date.prototype.setFullYear,u=l?Date.prototype.setUTCHours:Date.prototype.setHours;return f(n.year,n.month,n.date)&&(!angular.isDate(g)||isNaN(g.getTime())||l?(p=new Date(0),t.call(p,n.year,n.month,n.date),u.call(p,n.hours||0,n.minutes||0,n.seconds||0,n.milliseconds||0)):(p=new Date(g),t.call(p,n.year,n.month,n.date),u.call(p,n.hours,n.minutes,n.seconds,n.milliseconds))),p}},this.toTimezone=h,this.fromTimezone=i,this.timezoneToOffset=j,this.addDateMinutes=k,this.convertTimezoneToLocal=l}]),angular.module("ui.bootstrap.isClass",[]).directive("uibIsClass",["$animate",function(a){var b=/^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/,c=/^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;return{restrict:"A",compile:function(d,e){function f(a,b,c){i.push(a),j.push({scope:a,element:b}),o.forEach(function(b,c){g(b,a)}),a.$on("$destroy",h)}function g(b,d){var e=b.match(c),f=d.$eval(e[1]),g=e[2],h=k[b];if(!h){var i=function(b){var c=null;j.some(function(a){var d=a.scope.$eval(m);return d===b?(c=a,!0):void 0}),h.lastActivated!==c&&(h.lastActivated&&a.removeClass(h.lastActivated.element,f),c&&a.addClass(c.element,f),h.lastActivated=c)};k[b]=h={lastActivated:null,scope:d,watchFn:i,compareWithExp:g,watcher:d.$watch(g,i)}}h.watchFn(d.$eval(g))}function h(a){var b=a.targetScope,c=i.indexOf(b);if(i.splice(c,1),j.splice(c,1),i.length){var d=i[0];angular.forEach(k,function(a){a.scope===b&&(a.watcher=d.$watch(a.compareWithExp,a.watchFn),a.scope=d)})}else k={}}var i=[],j=[],k={},l=e.uibIsClass.match(b),m=l[2],n=l[1],o=n.split(",");return f}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.isClass"]).value("$datepickerSuppressError",!1).value("$datepickerLiteralWarning",!0).constant("uibDatepickerConfig",{datepickerMode:"day",formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",maxDate:null,maxMode:"year",minDate:null,minMode:"day",ngModelOptions:{},shortcutPropagation:!1,showWeeks:!0,yearColumns:5,yearRows:4}).controller("UibDatepickerController",["$scope","$attrs","$parse","$interpolate","$locale","$log","dateFilter","uibDatepickerConfig","$datepickerLiteralWarning","$datepickerSuppressError","uibDateParser",function(a,b,c,d,e,f,g,h,i,j,k){function l(b){a.datepickerMode=b,a.datepickerOptions.datepickerMode=b}var m=this,n={$setViewValue:angular.noop},o={},p=[];!!b.datepickerOptions;a.datepickerOptions||(a.datepickerOptions={}),this.modes=["day","month","year"],["customClass","dateDisabled","datepickerMode","formatDay","formatDayHeader","formatDayTitle","formatMonth","formatMonthTitle","formatYear","maxDate","maxMode","minDate","minMode","showWeeks","shortcutPropagation","startingDay","yearColumns","yearRows"].forEach(function(b){switch(b){case"customClass":case"dateDisabled":a[b]=a.datepickerOptions[b]||angular.noop;break;case"datepickerMode":a.datepickerMode=angular.isDefined(a.datepickerOptions.datepickerMode)?a.datepickerOptions.datepickerMode:h.datepickerMode;break;case"formatDay":case"formatDayHeader":case"formatDayTitle":case"formatMonth":case"formatMonthTitle":case"formatYear":m[b]=angular.isDefined(a.datepickerOptions[b])?d(a.datepickerOptions[b])(a.$parent):h[b];break;case"showWeeks":case"shortcutPropagation":case"yearColumns":case"yearRows":m[b]=angular.isDefined(a.datepickerOptions[b])?a.datepickerOptions[b]:h[b];break;case"startingDay":angular.isDefined(a.datepickerOptions.startingDay)?m.startingDay=a.datepickerOptions.startingDay:angular.isNumber(h.startingDay)?m.startingDay=h.startingDay:m.startingDay=(e.DATETIME_FORMATS.FIRSTDAYOFWEEK+8)%7;break;case"maxDate":case"minDate":a.$watch("datepickerOptions."+b,function(a){a?angular.isDate(a)?m[b]=k.fromTimezone(new Date(a),o.timezone):(i&&f.warn("Literal date support has been deprecated, please switch to date object usage"),m[b]=new Date(g(a,"medium"))):m[b]=h[b]?k.fromTimezone(new Date(h[b]),o.timezone):null,m.refreshView()});break;case"maxMode":case"minMode":a.datepickerOptions[b]?a.$watch(function(){return a.datepickerOptions[b]},function(c){m[b]=a[b]=angular.isDefined(c)?c:datepickerOptions[b],("minMode"===b&&m.modes.indexOf(a.datepickerOptions.datepickerMode)m.modes.indexOf(m[b]))&&(a.datepickerMode=m[b],a.datepickerOptions.datepickerMode=m[b])}):m[b]=a[b]=h[b]||null}}),a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),a.disabled=angular.isDefined(b.disabled)||!1,angular.isDefined(b.ngDisabled)&&p.push(a.$parent.$watch(b.ngDisabled,function(b){a.disabled=b,m.refreshView()})),a.isActive=function(b){return 0===m.compare(b.date,m.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(b){n=b,o=b.$options||h.ngModelOptions,a.datepickerOptions.initDate?(m.activeDate=k.fromTimezone(a.datepickerOptions.initDate,o.timezone)||new Date,a.$watch("datepickerOptions.initDate",function(a){a&&(n.$isEmpty(n.$modelValue)||n.$invalid)&&(m.activeDate=k.fromTimezone(a,o.timezone),m.refreshView())})):m.activeDate=new Date;var c=n.$modelValue?new Date(n.$modelValue):new Date;this.activeDate=isNaN(c)?k.fromTimezone(new Date,o.timezone):k.fromTimezone(c,o.timezone),n.$render=function(){m.render()}},this.render=function(){if(n.$viewValue){var a=new Date(n.$viewValue),b=!isNaN(a);b?this.activeDate=k.fromTimezone(a,o.timezone):j||f.error('Datepicker directive: "ng-model" value must be a Date object')}this.refreshView()},this.refreshView=function(){if(this.element){a.selectedDt=null,this._refreshView(),a.activeDt&&(a.activeDateId=a.activeDt.uid);var b=n.$viewValue?new Date(n.$viewValue):null;b=k.fromTimezone(b,o.timezone),n.$setValidity("dateDisabled",!b||this.element&&!this.isDisabled(b))}},this.createDateObject=function(b,c){var d=n.$viewValue?new Date(n.$viewValue):null;d=k.fromTimezone(d,o.timezone);var e=new Date;e=k.fromTimezone(e,o.timezone);var f=this.compare(b,e),g={date:b,label:k.filter(b,c),selected:d&&0===this.compare(b,d),disabled:this.isDisabled(b),past:0>f,current:0===f,future:f>0,customClass:this.customClass(b)||null};return d&&0===this.compare(b,d)&&(a.selectedDt=g),m.activeDate&&0===this.compare(g.date,m.activeDate)&&(a.activeDt=g),g},this.isDisabled=function(b){return a.disabled||this.minDate&&this.compare(b,this.minDate)<0||this.maxDate&&this.compare(b,this.maxDate)>0||a.dateDisabled&&a.dateDisabled({date:b,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===m.minMode){var c=n.$viewValue?k.fromTimezone(new Date(n.$viewValue),o.timezone):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),c=k.toTimezone(c,o.timezone),n.$setViewValue(c),n.$render()}else m.activeDate=b,l(m.modes[m.modes.indexOf(a.datepickerMode)-1]),a.$emit("uib:datepicker.mode");a.$broadcast("uib:datepicker.focus")},a.move=function(a){var b=m.activeDate.getFullYear()+a*(m.step.years||0),c=m.activeDate.getMonth()+a*(m.step.months||0);m.activeDate.setFullYear(b,c,1),m.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===m.maxMode&&1===b||a.datepickerMode===m.minMode&&-1===b||(l(m.modes[m.modes.indexOf(a.datepickerMode)+b]),a.$emit("uib:datepicker.mode"))},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var q=function(){m.element[0].focus()};a.$on("uib:datepicker.focus",q),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey&&!a.disabled)if(b.preventDefault(),m.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(m.isDisabled(m.activeDate))return;a.select(m.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(m.handleKeyDown(c,b),m.refreshView()):a.toggleMode("up"===c?1:-1)},a.$on("$destroy",function(){for(;p.length;)p.shift()()})}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?f[b]:29}function e(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var f=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=b,this.init=function(b){angular.extend(b,this),a.showWeeks=b.showWeeks,b.refreshView()},this.getDates=function(a,b){for(var c,d=new Array(b),e=new Date(a),f=0;b>f;)c=new Date(e),d[f++]=c,e.setDate(e.getDate()+1);return d},this._refreshView=function(){var b=this.activeDate.getFullYear(),d=this.activeDate.getMonth(),f=new Date(this.activeDate);f.setFullYear(b,d,1);var g=this.startingDay-f.getDay(),h=g>0?7-g:-g,i=new Date(f);h>0&&i.setDate(-h+1);for(var j=this.getDates(i,42),k=0;42>k;k++)j[k]=angular.extend(this.createDateObject(j[k],this.formatDay),{secondary:j[k].getMonth()!==d,uid:a.uniqueId+"-"+k});a.labels=new Array(7);for(var l=0;7>l;l++)a.labels[l]={abbr:c(j[l].date,this.formatDayHeader),full:c(j[l].date,"EEEE")};if(a.title=c(this.activeDate,this.formatDayTitle),a.rows=this.split(j,7),a.showWeeks){a.weekNumbers=[];for(var m=(11-this.startingDay)%7,n=a.rows.length,o=0;n>o;o++)a.weekNumbers.push(e(a.rows[o][m].date))}},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth(),a.getDate()),d=new Date(b.getFullYear(),b.getMonth(),b.getDate());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getDate();if("left"===a)c-=1;else if("up"===a)c-=7;else if("right"===a)c+=1;else if("down"===a)c+=7;else if("pageup"===a||"pagedown"===a){var e=this.activeDate.getMonth()+("pageup"===a?-1:1);this.activeDate.setMonth(e,1),c=Math.min(d(this.activeDate.getFullYear(),this.activeDate.getMonth()),c)}else"home"===a?c=1:"end"===a&&(c=d(this.activeDate.getFullYear(),this.activeDate.getMonth()));this.activeDate.setDate(c)}}]).controller("UibMonthpickerController",["$scope","$element","dateFilter",function(a,b,c){this.step={years:1},this.element=b,this.init=function(a){angular.extend(a,this),a.refreshView()},this._refreshView=function(){for(var b,d=new Array(12),e=this.activeDate.getFullYear(),f=0;12>f;f++)b=new Date(this.activeDate),b.setFullYear(e,f,1),d[f]=angular.extend(this.createDateObject(b,this.formatMonth),{uid:a.uniqueId+"-"+f});a.title=c(this.activeDate,this.formatMonthTitle),a.rows=this.split(d,3)},this.compare=function(a,b){var c=new Date(a.getFullYear(),a.getMonth()),d=new Date(b.getFullYear(),b.getMonth());return c.setFullYear(a.getFullYear()),d.setFullYear(b.getFullYear()),c-d},this.handleKeyDown=function(a,b){var c=this.activeDate.getMonth();if("left"===a)c-=1;else if("up"===a)c-=3;else if("right"===a)c+=1;else if("down"===a)c+=3;else if("pageup"===a||"pagedown"===a){var d=this.activeDate.getFullYear()+("pageup"===a?-1:1);this.activeDate.setFullYear(d)}else"home"===a?c=0:"end"===a&&(c=11);this.activeDate.setMonth(c)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a){return parseInt((a-1)/f,10)*f+1}var e,f;this.element=b,this.yearpickerInit=function(){e=this.yearColumns,f=this.yearRows*e,this.step={years:f}},this._refreshView=function(){for(var b,c=new Array(f),g=0,h=d(this.activeDate.getFullYear());f>g;g++)b=new Date(this.activeDate),b.setFullYear(h+g,0,1),c[g]=angular.extend(this.createDateObject(b,this.formatYear),{uid:a.uniqueId+"-"+g});a.title=[c[0].label,c[f-1].label].join(" - "),a.rows=this.split(c,e),a.columns=e},this.compare=function(a,b){return a.getFullYear()-b.getFullYear()},this.handleKeyDown=function(a,b){var c=this.activeDate.getFullYear();"left"===a?c-=1:"up"===a?c-=e:"right"===a?c+=1:"down"===a?c+=e:"pageup"===a||"pagedown"===a?c+=("pageup"===a?-1:1)*f:"home"===a?c=d(this.activeDate.getFullYear()):"end"===a&&(c=d(this.activeDate.getFullYear())+f-1),this.activeDate.setFullYear(c)}}]).directive("uibDatepicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/datepicker.html"},scope:{datepickerOptions:"=?"},require:["uibDatepicker","^ngModel"],controller:"UibDatepickerController",controllerAs:"datepicker",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}).directive("uibDaypicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/day.html"},require:["^uibDatepicker","uibDaypicker"],controller:"UibDaypickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibMonthpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/month.html"},require:["^uibDatepicker","uibMonthpicker"],controller:"UibMonthpickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibYearpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepicker/year.html"},require:["^uibDatepicker","uibYearpicker"],controller:"UibYearpickerController",link:function(a,b,c,d){var e=d[0];angular.extend(e,d[1]),e.yearpickerInit(),e.refreshView()}}}),angular.module("ui.bootstrap.position",[]).factory("$uibPosition",["$document","$window",function(a,b){var c,d,e={normal:/(auto|scroll)/,hidden:/(auto|scroll|hidden)/},f={auto:/\s?auto?\s?/i,primary:/^(top|bottom|left|right)$/,secondary:/^(top|bottom|left|right|center)$/,vertical:/^(top|bottom)$/},g=/(HTML|BODY)/;return{getRawNode:function(a){return a.nodeName?a:a[0]||a},parseStyle:function(a){return a=parseFloat(a),isFinite(a)?a:0},offsetParent:function(c){function d(a){return"static"===(b.getComputedStyle(a).position||"static")}c=this.getRawNode(c);for(var e=c.offsetParent||a[0].documentElement;e&&e!==a[0].documentElement&&d(e);)e=e.offsetParent;return e||a[0].documentElement},scrollbarWidth:function(e){if(e){if(angular.isUndefined(d)){var f=a.find("body");f.addClass("uib-position-body-scrollbar-measure"),d=b.innerWidth-f[0].clientWidth,d=isFinite(d)?d:0,f.removeClass("uib-position-body-scrollbar-measure")}return d}if(angular.isUndefined(c)){var g=angular.element('');a.find("body").append(g),c=g[0].offsetWidth-g[0].clientWidth,c=isFinite(c)?c:0,g.remove()}return c},scrollbarPadding:function(a){a=this.getRawNode(a);var c=b.getComputedStyle(a),d=this.parseStyle(c.paddingRight),e=this.parseStyle(c.paddingBottom),f=this.scrollParent(a,!1,!0),h=this.scrollbarWidth(f,g.test(f.tagName));return{scrollbarWidth:h,widthOverflow:f.scrollWidth>f.clientWidth,right:d+h,originalRight:d,heightOverflow:f.scrollHeight>f.clientHeight,bottom:e+h,originalBottom:e}},isScrollable:function(a,c){a=this.getRawNode(a);var d=c?e.hidden:e.normal,f=b.getComputedStyle(a);return d.test(f.overflow+f.overflowY+f.overflowX)},scrollParent:function(c,d,f){c=this.getRawNode(c);var g=d?e.hidden:e.normal,h=a[0].documentElement,i=b.getComputedStyle(c);if(f&&g.test(i.overflow+i.overflowY+i.overflowX))return c;var j="absolute"===i.position,k=c.parentElement||h;if(k===h||"fixed"===i.position)return h;for(;k.parentElement&&k!==h;){var l=b.getComputedStyle(k);if(j&&"static"!==l.position&&(j=!1),!j&&g.test(l.overflow+l.overflowY+l.overflowX))break;k=k.parentElement}return k},position:function(c,d){c=this.getRawNode(c);var e=this.offset(c);if(d){var f=b.getComputedStyle(c);e.top-=this.parseStyle(f.marginTop),e.left-=this.parseStyle(f.marginLeft)}var g=this.offsetParent(c),h={top:0,left:0};return g!==a[0].documentElement&&(h=this.offset(g),h.top+=g.clientTop-g.scrollTop,h.left+=g.clientLeft-g.scrollLeft),{width:Math.round(angular.isNumber(e.width)?e.width:c.offsetWidth),height:Math.round(angular.isNumber(e.height)?e.height:c.offsetHeight),top:Math.round(e.top-h.top),left:Math.round(e.left-h.left)}},offset:function(c){
+c=this.getRawNode(c);var d=c.getBoundingClientRect();return{width:Math.round(angular.isNumber(d.width)?d.width:c.offsetWidth),height:Math.round(angular.isNumber(d.height)?d.height:c.offsetHeight),top:Math.round(d.top+(b.pageYOffset||a[0].documentElement.scrollTop)),left:Math.round(d.left+(b.pageXOffset||a[0].documentElement.scrollLeft))}},viewportOffset:function(c,d,e){c=this.getRawNode(c),e=e!==!1;var f=c.getBoundingClientRect(),g={top:0,left:0,bottom:0,right:0},h=d?a[0].documentElement:this.scrollParent(c),i=h.getBoundingClientRect();if(g.top=i.top+h.clientTop,g.left=i.left+h.clientLeft,h===a[0].documentElement&&(g.top+=b.pageYOffset,g.left+=b.pageXOffset),g.bottom=g.top+h.clientHeight,g.right=g.left+h.clientWidth,e){var j=b.getComputedStyle(h);g.top+=this.parseStyle(j.paddingTop),g.bottom-=this.parseStyle(j.paddingBottom),g.left+=this.parseStyle(j.paddingLeft),g.right-=this.parseStyle(j.paddingRight)}return{top:Math.round(f.top-g.top),bottom:Math.round(g.bottom-f.bottom),left:Math.round(f.left-g.left),right:Math.round(g.right-f.right)}},parsePlacement:function(a){var b=f.auto.test(a);return b&&(a=a.replace(f.auto,"")),a=a.split("-"),a[0]=a[0]||"top",f.primary.test(a[0])||(a[0]="top"),a[1]=a[1]||"center",f.secondary.test(a[1])||(a[1]="center"),b?a[2]=!0:a[2]=!1,a},positionElements:function(a,c,d,e){a=this.getRawNode(a),c=this.getRawNode(c);var g=angular.isDefined(c.offsetWidth)?c.offsetWidth:c.prop("offsetWidth"),h=angular.isDefined(c.offsetHeight)?c.offsetHeight:c.prop("offsetHeight");d=this.parsePlacement(d);var i=e?this.offset(a):this.position(a),j={top:0,left:0,placement:""};if(d[2]){var k=this.viewportOffset(a,e),l=b.getComputedStyle(c),m={width:g+Math.round(Math.abs(this.parseStyle(l.marginLeft)+this.parseStyle(l.marginRight))),height:h+Math.round(Math.abs(this.parseStyle(l.marginTop)+this.parseStyle(l.marginBottom)))};if(d[0]="top"===d[0]&&m.height>k.top&&m.height<=k.bottom?"bottom":"bottom"===d[0]&&m.height>k.bottom&&m.height<=k.top?"top":"left"===d[0]&&m.width>k.left&&m.width<=k.right?"right":"right"===d[0]&&m.width>k.right&&m.width<=k.left?"left":d[0],d[1]="top"===d[1]&&m.height-i.height>k.bottom&&m.height-i.height<=k.top?"bottom":"bottom"===d[1]&&m.height-i.height>k.top&&m.height-i.height<=k.bottom?"top":"left"===d[1]&&m.width-i.width>k.right&&m.width-i.width<=k.left?"right":"right"===d[1]&&m.width-i.width>k.left&&m.width-i.width<=k.right?"left":d[1],"center"===d[1])if(f.vertical.test(d[0])){var n=i.width/2-g/2;k.left+n<0&&m.width-i.width<=k.right?d[1]="left":k.right+n<0&&m.width-i.width<=k.left&&(d[1]="right")}else{var o=i.height/2-m.height/2;k.top+o<0&&m.height-i.height<=k.bottom?d[1]="top":k.bottom+o<0&&m.height-i.height<=k.top&&(d[1]="bottom")}}switch(d[0]){case"top":j.top=i.top-h;break;case"bottom":j.top=i.top+i.height;break;case"left":j.left=i.left-g;break;case"right":j.left=i.left+i.width}switch(d[1]){case"top":j.top=i.top;break;case"bottom":j.top=i.top+i.height-h;break;case"left":j.left=i.left;break;case"right":j.left=i.left+i.width-g;break;case"center":f.vertical.test(d[0])?j.left=i.left+i.width/2-g/2:j.top=i.top+i.height/2-h/2}return j.top=Math.round(j.top),j.left=Math.round(j.left),j.placement="center"===d[1]?d[0]:d[0]+"-"+d[1],j},positionArrow:function(a,c){a=this.getRawNode(a);var d=a.querySelector(".tooltip-inner, .popover-inner");if(d){var e=angular.element(d).hasClass("tooltip-inner"),g=e?a.querySelector(".tooltip-arrow"):a.querySelector(".arrow");if(g){var h={top:"",bottom:"",left:"",right:""};if(c=this.parsePlacement(c),"center"===c[1])return void angular.element(g).css(h);var i="border-"+c[0]+"-width",j=b.getComputedStyle(g)[i],k="border-";k+=f.vertical.test(c[0])?c[0]+"-"+c[1]:c[1]+"-"+c[0],k+="-radius";var l=b.getComputedStyle(e?d:a)[k];switch(c[0]){case"top":h.bottom=e?"0":"-"+j;break;case"bottom":h.top=e?"0":"-"+j;break;case"left":h.right=e?"0":"-"+j;break;case"right":h.left=e?"0":"-"+j}h[c[1]]=l,angular.element(g).css(h)}}}}}]),angular.module("ui.bootstrap.datepickerPopup",["ui.bootstrap.datepicker","ui.bootstrap.position"]).value("$datepickerPopupLiteralWarning",!0).constant("uibDatepickerPopupConfig",{altInputFormats:[],appendToBody:!1,clearText:"Clear",closeOnDateSelection:!0,closeText:"Done",currentText:"Today",datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"uib/template/datepickerPopup/popup.html",datepickerTemplateUrl:"uib/template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},onOpenFocus:!0,showButtonBar:!0,placement:"auto bottom-left"}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$log","$parse","$window","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout","uibDatepickerConfig","$datepickerPopupLiteralWarning",function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){function q(b){var c=l.parse(b,w,a.date);if(isNaN(c))for(var d=0;d"),G?(J=G.timezone,a.ngModelOptions=angular.copy(G),a.ngModelOptions.timezone=null,a.ngModelOptions.updateOnDefault===!0&&(a.ngModelOptions.updateOn=a.ngModelOptions.updateOn?a.ngModelOptions.updateOn+" default":"default"),C.attr("ng-model-options","ngModelOptions")):J=null,C.attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":A}),D=angular.element(C.children()[0]),D.attr("template-url",B),a.datepickerOptions||(a.datepickerOptions={}),K&&"month"===c.type&&(a.datepickerOptions.datepickerMode="month",a.datepickerOptions.minMode="month"),D.attr("datepicker-options","datepickerOptions"),K?F.$formatters.push(function(b){return a.date=l.fromTimezone(b,J),b}):(F.$$parserName="date",F.$validators.date=s,F.$parsers.unshift(r),F.$formatters.push(function(b){return F.$isEmpty(b)?(a.date=b,b):(angular.isNumber(b)&&(b=new Date(b)),a.date=l.fromTimezone(b,J),l.filter(a.date,w))})),F.$viewChangeListeners.push(function(){a.date=q(F.$viewValue)}),b.on("keydown",u),H=d(C)(a),C.remove(),y?h.find("body").append(H):b.after(H),a.$on("$destroy",function(){for(a.isOpen===!0&&(i.$$phase||a.$apply(function(){a.isOpen=!1})),H.remove(),b.off("keydown",u),h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v);L.length;)L.shift()()})},a.getText=function(b){return a[b+"Text"]||m[b+"Text"]},a.isDisabled=function(b){"today"===b&&(b=l.fromTimezone(new Date,J));var c={};return angular.forEach(["minDate","maxDate"],function(b){a.datepickerOptions[b]?angular.isDate(a.datepickerOptions[b])?c[b]=l.fromTimezone(new Date(a.datepickerOptions[b]),J):(p&&e.warn("Literal date support has been deprecated, please switch to date object usage"),c[b]=new Date(k(a.datepickerOptions[b],"medium"))):c[b]=null}),a.datepickerOptions&&c.minDate&&a.compare(b,c.minDate)<0||c.maxDate&&a.compare(b,c.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.dateSelection=function(c){angular.isDefined(c)&&(a.date=c);var d=a.date?l.filter(a.date,w):null;b.val(d),F.$setViewValue(d),x&&(a.isOpen=!1,b[0].focus())},a.keydown=function(c){27===c.which&&(c.stopPropagation(),a.isOpen=!1,b[0].focus())},a.select=function(b,c){if(c.stopPropagation(),"today"===b){var d=new Date;angular.isDate(a.date)?(b=new Date(a.date),b.setFullYear(d.getFullYear(),d.getMonth(),d.getDate())):b=new Date(d.setHours(0,0,0,0))}a.dateSelection(b)},a.close=function(c){c.stopPropagation(),a.isOpen=!1,b[0].focus()},a.disabled=angular.isDefined(c.disabled)||!1,c.ngDisabled&&L.push(a.$parent.$watch(f(c.ngDisabled),function(b){a.disabled=b})),a.$watch("isOpen",function(d){d?a.disabled?a.isOpen=!1:n(function(){v(),z&&a.$broadcast("uib:datepicker.focus"),h.on("click",t);var d=c.popupPlacement?c.popupPlacement:m.placement;y||j.parsePlacement(d)[2]?(E=E||angular.element(j.scrollParent(b)),E&&E.on("scroll",v)):E=null,angular.element(g).on("resize",v)},0,!1):(h.off("click",t),E&&E.off("scroll",v),angular.element(g).off("resize",v))}),a.$on("uib:datepicker.mode",function(){n(v,0,!1)})}]).directive("uibDatepickerPopup",function(){return{require:["ngModel","uibDatepickerPopup"],controller:"UibDatepickerPopupController",scope:{datepickerOptions:"=?",isOpen:"=?",currentText:"@",clearText:"@",closeText:"@"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibDatepickerPopupWrap",function(){return{replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"uib/template/datepickerPopup/popup.html"}}}),angular.module("ui.bootstrap.debounce",[]).factory("$$debounce",["$timeout",function(a){return function(b,c){var d;return function(){var e=this,f=Array.prototype.slice.call(arguments);d&&a.cancel(d),d=a(function(){b.apply(e,f)},c)}}}]),angular.module("ui.bootstrap.dropdown",["ui.bootstrap.position"]).constant("uibDropdownConfig",{appendToOpenClass:"uib-dropdown-open",openClass:"open"}).service("uibDropdownService",["$document","$rootScope",function(a,b){var c=null;this.open=function(b,f){c||(a.on("click",d),f.on("keydown",e)),c&&c!==b&&(c.isOpen=!1),c=b},this.close=function(b,f){c===b&&(c=null,a.off("click",d),f.off("keydown",e))};var d=function(a){if(c&&!(a&&"disabled"===c.getAutoClose()||a&&3===a.which)){var d=c.getToggleElement();if(!(a&&d&&d[0].contains(a.target))){var e=c.getDropdownElement();a&&"outsideClick"===c.getAutoClose()&&e&&e[0].contains(a.target)||(c.isOpen=!1,b.$$phase||c.$apply())}}},e=function(a){27===a.which?(a.stopPropagation(),c.focusToggleElement(),d()):c.isKeynavEnabled()&&-1!==[38,40].indexOf(a.which)&&c.isOpen&&(a.preventDefault(),a.stopPropagation(),c.focusDropdownEntry(a.which))}}]).controller("UibDropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest",function(a,b,c,d,e,f,g,h,i,j,k){var l,m,n=this,o=a.$new(),p=e.appendToOpenClass,q=e.openClass,r=angular.noop,s=c.onToggle?d(c.onToggle):angular.noop,t=!1,u=null,v=!1,w=i.find("body");b.addClass("dropdown"),this.init=function(){if(c.isOpen&&(m=d(c.isOpen),r=m.assign,a.$watch(m,function(a){o.isOpen=!!a})),angular.isDefined(c.dropdownAppendTo)){var e=d(c.dropdownAppendTo)(o);e&&(u=angular.element(e))}t=angular.isDefined(c.dropdownAppendToBody),v=angular.isDefined(c.keyboardNav),t&&!u&&(u=w),u&&n.dropdownMenu&&(u.append(n.dropdownMenu),b.on("$destroy",function(){n.dropdownMenu.remove()}))},this.toggle=function(a){return o.isOpen=arguments.length?!!a:!o.isOpen,angular.isFunction(r)&&r(o,o.isOpen),o.isOpen},this.isOpen=function(){return o.isOpen},o.getToggleElement=function(){return n.toggleElement},o.getAutoClose=function(){return c.autoClose||"always"},o.getElement=function(){return b},o.isKeynavEnabled=function(){return v},o.focusDropdownEntry=function(a){var c=n.dropdownMenu?angular.element(n.dropdownMenu).find("a"):b.find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(n.selectedOption)?n.selectedOption=n.selectedOption===c.length-1?n.selectedOption:n.selectedOption+1:n.selectedOption=0;break;case 38:angular.isNumber(n.selectedOption)?n.selectedOption=0===n.selectedOption?0:n.selectedOption-1:n.selectedOption=c.length-1}c[n.selectedOption].focus()},o.getDropdownElement=function(){return n.dropdownMenu},o.focusToggleElement=function(){n.toggleElement&&n.toggleElement[0].focus()},o.$watch("isOpen",function(c,d){if(u&&n.dropdownMenu){var e,i,m,v=h.positionElements(b,n.dropdownMenu,"bottom-left",!0);if(e={top:v.top+"px",display:c?"block":"none"},i=n.dropdownMenu.hasClass("dropdown-menu-right"),i?(e.left="auto",m=h.scrollbarWidth(!0),e.right=window.innerWidth-m-(v.left+b.prop("offsetWidth"))+"px"):(e.left=v.left+"px",e.right="auto"),!t){var w=h.offset(u);e.top=v.top-w.top+"px",i?e.right=window.innerWidth-(v.left-w.left+b.prop("offsetWidth"))+"px":e.left=v.left-w.left+"px"}n.dropdownMenu.css(e)}var x=u?u:b,y=x.hasClass(u?p:q);if(y===!c&&g[c?"addClass":"removeClass"](x,u?p:q).then(function(){angular.isDefined(c)&&c!==d&&s(a,{open:!!c})}),c)n.dropdownMenuTemplateUrl&&k(n.dropdownMenuTemplateUrl).then(function(a){l=o.$new(),j(a.trim())(l,function(a){var b=a;n.dropdownMenu.replaceWith(b),n.dropdownMenu=b})}),o.focusToggleElement(),f.open(o,b);else{if(n.dropdownMenuTemplateUrl){l&&l.$destroy();var z=angular.element('
');n.dropdownMenu.replaceWith(z),n.dropdownMenu=z}f.close(o,b),n.selectedOption=null}angular.isFunction(r)&&r(a,c)})}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(a,b,c,d){d.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"A",require:"?^uibDropdown",link:function(a,b,c,d){if(d&&!angular.isDefined(c.dropdownNested)){b.addClass("dropdown-menu");var e=c.templateUrl;e&&(d.dropdownMenuTemplateUrl=e),d.dropdownMenu||(d.dropdownMenu=b)}}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(a,b,c,d){if(d){b.addClass("dropdown-toggle"),d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.bind("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.unbind("click",e)})}}}}),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c-1&&y>a&&(a=y),a}function l(a,b){var c=v.get(a).value,d=c.appendTo;v.remove(a),z=v.top(),z&&(y=parseInt(z.value.modalDomEl.attr("index"),10)),o(c.modalDomEl,c.modalScope,function(){var b=c.openedClass||u;w.remove(b,a);var e=w.hasKey(b);d.toggleClass(b,e),!e&&t&&t.heightOverflow&&t.scrollbarWidth&&(t.originalRight?d.css({paddingRight:t.originalRight+"px"}):d.css({paddingRight:""}),t=null),m(!0)},c.closedDeferred),n(),b&&b.focus?b.focus():d.focus&&d.focus()}function m(a){var b;v.length()>0&&(b=v.top().value,b.modalDomEl.toggleClass(b.windowTopClass||"",a))}function n(){if(r&&-1===k()){var a=s;o(r,s,function(){a=null}),r=void 0,s=void 0}}function o(b,c,d,e){function g(){g.done||(g.done=!0,a.leave(b).then(function(){b.remove(),e&&e.resolve()}),c.$destroy(),d&&d())}var h,i=null,j=function(){return h||(h=f.defer(),i=h.promise),function(){h.resolve()}};return c.$broadcast(x.NOW_CLOSING_EVENT,j),f.when(i).then(g)}function p(a){if(a.isDefaultPrevented())return a;var b=v.top();if(b)switch(a.which){case 27:b.value.keyboard&&(a.preventDefault(),e.$apply(function(){x.dismiss(b.key,"escape key press")}));break;case 9:var c=x.loadFocusElementList(b),d=!1;a.shiftKey?(x.isFocusInFirstItem(a,c)||x.isModalFocused(a,b))&&(d=x.focusLastFocusableElement(c)):x.isFocusInLastItem(a,c)&&(d=x.focusFirstFocusableElement(c)),d&&(a.preventDefault(),a.stopPropagation())}}function q(a,b,c){return!a.value.modalScope.$broadcast("modal.closing",b,c).defaultPrevented}var r,s,t,u="modal-open",v=h.createNew(),w=g.createNew(),x={NOW_CLOSING_EVENT:"modal.stack.now-closing"},y=0,z=null,A="a[href], area[href], input:not([disabled]), button:not([disabled]),select:not([disabled]), textarea:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable=true]";return e.$watch(k,function(a){s&&(s.index=a)}),c.on("keydown",p),e.$on("$destroy",function(){c.off("keydown",p)}),x.open=function(b,f){var g=c[0].activeElement,h=f.openedClass||u;m(!1),z=v.top(),v.add(b,{deferred:f.deferred,renderDeferred:f.renderDeferred,closedDeferred:f.closedDeferred,modalScope:f.scope,backdrop:f.backdrop,keyboard:f.keyboard,openedClass:f.openedClass,windowTopClass:f.windowTopClass,animation:f.animation,appendTo:f.appendTo}),w.put(h,b);var j=f.appendTo,l=k();if(!j.length)throw new Error("appendTo element not found. Make sure that the element passed is in DOM.");l>=0&&!r&&(s=e.$new(!0),s.modalOptions=f,s.index=l,r=angular.element(''),r.attr("backdrop-class",f.backdropClass),f.animation&&r.attr("modal-animation","true"),d(r)(s),a.enter(r,j),t=i.scrollbarPadding(j),t.heightOverflow&&t.scrollbarWidth&&j.css({paddingRight:t.right+"px"})),y=z?parseInt(z.value.modalDomEl.attr("index"),10)+1:0;var n=angular.element('');n.attr({"template-url":f.windowTemplateUrl,"window-class":f.windowClass,"window-top-class":f.windowTopClass,size:f.size,index:y,animate:"animate"}).html(f.content),f.animation&&n.attr("modal-animation","true"),j.addClass(h),a.enter(d(n)(f.scope),j),v.top().value.modalDomEl=n,v.top().value.modalOpener=g},x.close=function(a,b){var c=v.get(a);return c&&q(c,b,!0)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.resolve(b),l(a,c.value.modalOpener),!0):!c},x.dismiss=function(a,b){var c=v.get(a);return c&&q(c,b,!1)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.reject(b),l(a,c.value.modalOpener),!0):!c},x.dismissAll=function(a){for(var b=this.getTop();b&&this.dismiss(b.key,a);)b=this.getTop()},x.getTop=function(){return v.top()},x.modalRendered=function(a){var b=v.get(a);b&&b.value.renderDeferred.resolve()},x.focusFirstFocusableElement=function(a){return a.length>0?(a[0].focus(),!0):!1},x.focusLastFocusableElement=function(a){return a.length>0?(a[a.length-1].focus(),!0):!1},x.isModalFocused=function(a,b){if(a&&b){var c=b.value.modalDomEl;if(c&&c.length)return(a.target||a.srcElement)===c[0]}return!1},x.isFocusInFirstItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[0]:!1},x.isFocusInLastItem=function(a,b){return b.length>0?(a.target||a.srcElement)===b[b.length-1]:!1},x.loadFocusElementList=function(a){if(a){var b=a.value.modalDomEl;if(b&&b.length){var c=b[0].querySelectorAll(A);return c?Array.prototype.filter.call(c,function(a){return j(a)}):c}}},x}]).provider("$uibModal",function(){var a={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$rootScope","$q","$document","$templateRequest","$controller","$uibResolve","$uibModalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?c.when(a.template):e(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl)}var j={},k=null;return j.getPromiseChain=function(){return k},j.open=function(e){function j(){return r}var l=c.defer(),m=c.defer(),n=c.defer(),o=c.defer(),p={result:l.promise,opened:m.promise,closed:n.promise,rendered:o.promise,close:function(a){return h.close(p,a)},dismiss:function(a){return h.dismiss(p,a)}};if(e=angular.extend({},a.options,e),e.resolve=e.resolve||{},e.appendTo=e.appendTo||d.find("body").eq(0),!e.template&&!e.templateUrl)throw new Error("One of template or templateUrl options is required.");var q,r=c.all([i(e),g.resolve(e.resolve,{},null,null)]);return q=k=c.all([k]).then(j,j).then(function(a){var c=e.scope||b,d=c.$new();d.$close=p.close,d.$dismiss=p.dismiss,d.$on("$destroy",function(){d.$$uibDestructionScheduled||d.$dismiss("$uibUnscheduledDestruction")});var g,i,j={};e.controller&&(j.$scope=d,j.$scope.$resolve={},j.$uibModalInstance=p,angular.forEach(a[1],function(a,b){j[b]=a,j.$scope.$resolve[b]=a}),i=f(e.controller,j,!0,e.controllerAs),e.controllerAs&&e.bindToController&&(g=i.instance,g.$close=d.$close,g.$dismiss=d.$dismiss,angular.extend(g,{$resolve:j.$scope.$resolve},c)),g=i(),angular.isFunction(g.$onInit)&&g.$onInit()),h.open(p,{scope:d,deferred:l,renderDeferred:o,closedDeferred:n,content:a[0],animation:e.animation,backdrop:e.backdrop,keyboard:e.keyboard,backdropClass:e.backdropClass,windowTopClass:e.windowTopClass,windowClass:e.windowClass,windowTemplateUrl:e.windowTemplateUrl,size:e.size,openedClass:e.openedClass,appendTo:e.appendTo}),m.resolve(!0)},function(a){m.reject(a),l.reject(a)})["finally"](function(){k===q&&(k=null)}),p},j}]};return a}),angular.module("ui.bootstrap.paging",[]).factory("uibPaging",["$parse",function(a){return{create:function(b,c,d){b.setNumPages=d.numPages?a(d.numPages).assign:angular.noop,b.ngModelCtrl={$setViewValue:angular.noop},b._watchers=[],b.init=function(a,e){b.ngModelCtrl=a,b.config=e,a.$render=function(){b.render()},d.itemsPerPage?b._watchers.push(c.$parent.$watch(d.itemsPerPage,function(a){b.itemsPerPage=parseInt(a,10),c.totalPages=b.calculateTotalPages(),b.updatePage()})):b.itemsPerPage=e.itemsPerPage,c.$watch("totalItems",function(a,d){(angular.isDefined(a)||a!==d)&&(c.totalPages=b.calculateTotalPages(),b.updatePage())})},b.calculateTotalPages=function(){var a=b.itemsPerPage<1?1:Math.ceil(c.totalItems/b.itemsPerPage);return Math.max(a||0,1)},b.render=function(){c.page=parseInt(b.ngModelCtrl.$viewValue,10)||1},c.selectPage=function(a,d){d&&d.preventDefault();var e=!c.ngDisabled||!d;e&&c.page!==a&&a>0&&a<=c.totalPages&&(d&&d.target&&d.target.blur(),b.ngModelCtrl.$setViewValue(a),b.ngModelCtrl.$render())},c.getText=function(a){return c[a+"Text"]||b.config[a+"Text"]},c.noPrevious=function(){return 1===c.page},c.noNext=function(){return c.page===c.totalPages},b.updatePage=function(){b.setNumPages(c.$parent,c.totalPages),c.page>c.totalPages?c.selectPage(c.totalPages):b.ngModelCtrl.$render()},c.$on("$destroy",function(){for(;b._watchers.length;)b._watchers.shift()()})}}}]),angular.module("ui.bootstrap.pager",["ui.bootstrap.paging"]).controller("UibPagerController",["$scope","$attrs","uibPaging","uibPagerConfig",function(a,b,c,d){a.align=angular.isDefined(b.align)?a.$parent.$eval(b.align):d.align,c.create(this,a,b)}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(a){return{scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],controller:"UibPagerController",controllerAs:"pager",templateUrl:function(a,b){return b.templateUrl||"uib/template/pager/pager.html"},replace:!0,link:function(b,c,d,e){var f=e[0],g=e[1];g&&f.init(g,a)}}}]),angular.module("ui.bootstrap.pagination",["ui.bootstrap.paging"]).controller("UibPaginationController",["$scope","$attrs","$parse","uibPaging","uibPaginationConfig",function(a,b,c,d,e){function f(a,b,c){return{number:a,text:b,active:c}}function g(a,b){var c=[],d=1,e=b,g=angular.isDefined(i)&&b>i;g&&(j?(d=Math.max(a-Math.floor(i/2),1),e=d+i-1,e>b&&(e=b,d=e-i+1)):(d=(Math.ceil(a/i)-1)*i+1,e=Math.min(d+i-1,b)));for(var h=d;e>=h;h++){var n=f(h,m(h),h===a);c.push(n)}if(g&&i>0&&(!j||k||l)){if(d>1){if(!l||d>3){var o=f(d-1,"...",!1);c.unshift(o)}if(l){if(3===d){var p=f(2,"2",!1);c.unshift(p)}var q=f(1,"1",!1);c.unshift(q)}}if(b>e){if(!l||b-2>e){var r=f(e+1,"...",!1);c.push(r)}if(l){if(e===b-2){var s=f(b-1,b-1,!1);c.push(s)}var t=f(b,b,!1);c.push(t)}}}return c}var h=this,i=angular.isDefined(b.maxSize)?a.$parent.$eval(b.maxSize):e.maxSize,j=angular.isDefined(b.rotate)?a.$parent.$eval(b.rotate):e.rotate,k=angular.isDefined(b.forceEllipses)?a.$parent.$eval(b.forceEllipses):e.forceEllipses,l=angular.isDefined(b.boundaryLinkNumbers)?a.$parent.$eval(b.boundaryLinkNumbers):e.boundaryLinkNumbers,m=angular.isDefined(b.pageLabel)?function(c){return a.$parent.$eval(b.pageLabel,{$page:c})}:angular.identity;a.boundaryLinks=angular.isDefined(b.boundaryLinks)?a.$parent.$eval(b.boundaryLinks):e.boundaryLinks,a.directionLinks=angular.isDefined(b.directionLinks)?a.$parent.$eval(b.directionLinks):e.directionLinks,d.create(this,a,b),b.maxSize&&h._watchers.push(a.$parent.$watch(c(b.maxSize),function(a){i=parseInt(a,10),h.render()}));var n=this.render;this.render=function(){n(),a.page>0&&a.page<=a.totalPages&&(a.pages=g(a.page,a.totalPages))}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,boundaryLinkNumbers:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0,forceEllipses:!1}).directive("uibPagination",["$parse","uibPaginationConfig",function(a,b){return{scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"uib/template/pagination/pagination.html"},replace:!0,link:function(a,c,d,e){var f=e[0],g=e[1];g&&f.init(g,b)}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.stackedMap"]).provider("$uibTooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",placementClassPrefix:"",animation:!0,popupDelay:0,popupCloseDelay:0,useContentExp:!1},c={mouseenter:"mouseleave",click:"click",outsideClick:"outsideClick",focus:"blur",none:""},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$uibPosition","$interpolate","$rootScope","$parse","$$stackedMap",function(e,f,g,h,i,j,k,l,m){function n(a){if(27===a.which){var b=o.top();b&&(b.value.close(),o.removeTop(),b=null)}}var o=m.createNew();return h.on("keypress",n),k.$on("$destroy",function(){h.off("keypress",n)}),function(e,k,m,n){function p(a){var b=(a||n.trigger||m).split(" "),d=b.map(function(a){return c[a]||a});return{show:b,hide:d}}n=angular.extend({},b,d,n);var q=a(e),r=j.startSymbol(),s=j.endSymbol(),t="';return{compile:function(a,b){var c=f(t);return function(a,b,d,f){function j(){N.isOpen?q():m()}function m(){M&&!a.$eval(d[k+"Enable"])||(u(),x(),N.popupDelay?G||(G=g(r,N.popupDelay,!1)):r())}function q(){s(),N.popupCloseDelay?H||(H=g(t,N.popupCloseDelay,!1)):t()}function r(){return s(),u(),N.content?(v(),void N.$evalAsync(function(){N.isOpen=!0,y(!0),S()})):angular.noop}function s(){G&&(g.cancel(G),G=null),I&&(g.cancel(I),I=null)}function t(){N&&N.$evalAsync(function(){N&&(N.isOpen=!1,y(!1),N.animation?F||(F=g(w,150,!1)):w())})}function u(){H&&(g.cancel(H),H=null),F&&(g.cancel(F),F=null)}function v(){D||(E=N.$new(),D=c(E,function(a){K?h.find("body").append(a):b.after(a)}),z())}function w(){s(),u(),A(),D&&(D.remove(),D=null),E&&(E.$destroy(),E=null);
+}function x(){N.title=d[k+"Title"],Q?N.content=Q(a):N.content=d[e],N.popupClass=d[k+"Class"],N.placement=angular.isDefined(d[k+"Placement"])?d[k+"Placement"]:n.placement;var b=i.parsePlacement(N.placement);J=b[1]?b[0]+"-"+b[1]:b[0];var c=parseInt(d[k+"PopupDelay"],10),f=parseInt(d[k+"PopupCloseDelay"],10);N.popupDelay=isNaN(c)?n.popupDelay:c,N.popupCloseDelay=isNaN(f)?n.popupCloseDelay:f}function y(b){P&&angular.isFunction(P.assign)&&P.assign(a,b)}function z(){R.length=0,Q?(R.push(a.$watch(Q,function(a){N.content=a,!a&&N.isOpen&&t()})),R.push(E.$watch(function(){O||(O=!0,E.$$postDigest(function(){O=!1,N&&N.isOpen&&S()}))}))):R.push(d.$observe(e,function(a){N.content=a,!a&&N.isOpen?t():S()})),R.push(d.$observe(k+"Title",function(a){N.title=a,N.isOpen&&S()})),R.push(d.$observe(k+"Placement",function(a){N.placement=a?a:n.placement,N.isOpen&&S()}))}function A(){R.length&&(angular.forEach(R,function(a){a()}),R.length=0)}function B(a){N&&N.isOpen&&D&&(b[0].contains(a.target)||D[0].contains(a.target)||q())}function C(){var a=d[k+"Trigger"];T(),L=p(a),"none"!==L.show&&L.show.forEach(function(a,c){"outsideClick"===a?(b.on("click",j),h.on("click",B)):a===L.hide[c]?b.on(a,j):a&&(b.on(a,m),b.on(L.hide[c],q)),b.on("keypress",function(a){27===a.which&&q()})})}var D,E,F,G,H,I,J,K=angular.isDefined(n.appendToBody)?n.appendToBody:!1,L=p(void 0),M=angular.isDefined(d[k+"Enable"]),N=a.$new(!0),O=!1,P=angular.isDefined(d[k+"IsOpen"])?l(d[k+"IsOpen"]):!1,Q=n.useContentExp?l(d[e]):!1,R=[],S=function(){D&&D.html()&&(I||(I=g(function(){var a=i.positionElements(b,D,N.placement,K);D.css({top:a.top+"px",left:a.left+"px"}),D.hasClass(a.placement.split("-")[0])||(D.removeClass(J.split("-")[0]),D.addClass(a.placement.split("-")[0])),D.hasClass(n.placementClassPrefix+a.placement)||(D.removeClass(n.placementClassPrefix+J),D.addClass(n.placementClassPrefix+a.placement)),D.hasClass("uib-position-measure")?(i.positionArrow(D,a.placement),D.removeClass("uib-position-measure")):J!==a.placement&&i.positionArrow(D,a.placement),J=a.placement,I=null},0,!1)))};N.origScope=a,N.isOpen=!1,o.add(N,{close:t}),N.contentExp=function(){return N.content},d.$observe("disabled",function(a){a&&s(),a&&N.isOpen&&t()}),P&&a.$watch(P,function(a){N&&!a===N.isOpen&&j()});var T=function(){L.show.forEach(function(a){"outsideClick"===a?b.off("click",j):(b.off(a,m),b.off(a,j))}),L.hide.forEach(function(a){"outsideClick"===a?h.off("click",B):b.off(a,q)})};C();var U=a.$eval(d[k+"Animation"]);N.animation=angular.isDefined(U)?!!U:n.animation;var V,W=k+"AppendToBody";V=W in d&&void 0===d[W]?!0:a.$eval(d[W]),K=angular.isDefined(V)?V:K,a.$on("$destroy",function(){T(),w(),o.remove(N),N=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(a,b,c,d){return{link:function(e,f,g){var h,i,j,k=e.$eval(g.tooltipTemplateTranscludeScope),l=0,m=function(){i&&(i.remove(),i=null),h&&(h.$destroy(),h=null),j&&(a.leave(j).then(function(){i=null}),i=j,j=null)};e.$watch(b.parseAsResourceUrl(g.uibTooltipTemplateTransclude),function(b){var g=++l;b?(d(b,!0).then(function(d){if(g===l){var e=k.$new(),i=d,n=c(i)(e,function(b){m(),a.enter(b,f)});h=e,j=n,h.$emit("$includeContentLoaded",b)}},function(){g===l&&(m(),e.$emit("$includeContentError",b))}),e.$emit("$includeContentRequested",b)):m()}),e.$on("$destroy",m)}}}]).directive("uibTooltipClasses",["$uibPosition",function(a){return{restrict:"A",link:function(b,c,d){if(b.placement){var e=a.parsePlacement(b.placement);c.addClass(e[0])}b.popupClass&&c.addClass(b.popupClass),b.animation()&&c.addClass(d.tooltipAnimationClass)}}}]).directive("uibTooltipPopup",function(){return{replace:!0,scope:{content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/tooltip/tooltip-popup.html"}}).directive("uibTooltip",["$uibTooltip",function(a){return a("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"uib/template/tooltip/tooltip-template-popup.html"}}).directive("uibTooltipTemplate",["$uibTooltip",function(a){return a("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/tooltip/tooltip-html-popup.html"}}).directive("uibTooltipHtml",["$uibTooltip",function(a){return a("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{replace:!0,scope:{uibTitle:"@",contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"uib/template/popover/popover-template.html"}}).directive("uibPopoverTemplate",["$uibTooltip",function(a){return a("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",uibTitle:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/popover/popover-html.html"}}).directive("uibPopoverHtml",["$uibTooltip",function(a){return a("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{replace:!0,scope:{uibTitle:"@",content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"uib/template/popover/popover.html"}}).directive("uibPopover",["$uibTooltip",function(a){return a("uibPopover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(a,b,c){function d(){return angular.isDefined(a.maxParam)?a.maxParam:c.max}var e=this,f=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=d(),this.addBar=function(a,b,c){f||b.css({transition:"none"}),this.bars.push(a),a.max=d(),a.title=c&&angular.isDefined(c.title)?c.title:"progressbar",a.$watch("value",function(b){a.recalculatePercentage()}),a.recalculatePercentage=function(){var b=e.bars.reduce(function(a,b){return b.percent=+(100*b.value/b.max).toFixed(2),a+b.percent},0);b>100&&(a.percent-=b-100)},a.$on("$destroy",function(){b=null,e.removeBar(a)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1),this.bars.forEach(function(a){a.recalculatePercentage()})},a.$watch("maxParam",function(a){e.bars.forEach(function(a){a.max=d(),a.recalculatePercentage()})})}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{maxParam:"=?max"},templateUrl:"uib/template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"uib/template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b,c)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",maxParam:"=?max",type:"@"},templateUrl:"uib/template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]),{title:c.title})}}}),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,enableReset:!0,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(a,b,c){var d={$setViewValue:angular.noop},e=this;this.init=function(e){d=e,d.$render=this.render,d.$formatters.push(function(a){return angular.isNumber(a)&&a<<0!==a&&(a=Math.round(a)),a}),this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff,this.enableReset=angular.isDefined(b.enableReset)?a.$parent.$eval(b.enableReset):c.enableReset;var f=angular.isDefined(b.titles)?a.$parent.$eval(b.titles):c.titles;this.titles=angular.isArray(f)&&f.length>0?f:c.titles;var g=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(g)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff,title:this.getTitle(b)},a[b]);return a},this.getTitle=function(a){return a>=this.titles.length?a+1:this.titles[a]},a.rate=function(b){if(!a.readonly&&b>=0&&b<=a.range.length){var c=e.enableReset&&d.$viewValue===b?0:b;d.$setViewValue(c),d.$render()}},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue,a.title=e.getTitle(a.value-1)}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],scope:{readonly:"=?readOnly",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"uib/template/rating/rating.html",replace:!0,link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(a){function b(a){for(var b=0;bb.index?1:a.index0&&13>b:b>=0&&24>b;return c&&""!==a.hours?(a.showMeridian&&(12===b&&(b=0),a.meridian===v[1]&&(b+=12)),b):void 0}function i(){var b=+a.minutes,c=b>=0&&60>b;return c&&""!==a.minutes?b:void 0}function j(){var b=+a.seconds;return b>=0&&60>b?b:void 0}function k(a,b){return null===a?"":angular.isDefined(a)&&a.toString().length<2&&!b?"0"+a:a.toString()}function l(a){m(),u.$setViewValue(new Date(s)),n(a)}function m(){u.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1,a.invalidSeconds=!1}function n(b){if(u.$modelValue){var c=s.getHours(),d=s.getMinutes(),e=s.getSeconds();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:k(c,!w),"m"!==b&&(a.minutes=k(d)),a.meridian=s.getHours()<12?v[0]:v[1],"s"!==b&&(a.seconds=k(e)),a.meridian=s.getHours()<12?v[0]:v[1]}else a.hours=null,a.minutes=null,a.seconds=null,a.meridian=v[0]}function o(a){s=q(s,a),l()}function p(a,b){return q(a,60*b)}function q(a,b){var c=new Date(a.getTime()+1e3*b),d=new Date(a);return d.setHours(c.getHours(),c.getMinutes(),c.getSeconds()),d}function r(){return(null===a.hours||""===a.hours)&&(null===a.minutes||""===a.minutes)&&(!a.showSeconds||a.showSeconds&&(null===a.seconds||""===a.seconds))}var s=new Date,t=[],u={$setViewValue:angular.noop},v=angular.isDefined(c.meridians)?a.$parent.$eval(c.meridians):g.meridians||f.DATETIME_FORMATS.AMPMS,w=angular.isDefined(c.padHours)?a.$parent.$eval(c.padHours):!0;a.tabindex=angular.isDefined(c.tabindex)?c.tabindex:0,b.removeAttr("tabindex"),this.init=function(b,d){u=b,u.$render=this.render,u.$formatters.unshift(function(a){return a?new Date(a):null});var e=d.eq(0),f=d.eq(1),h=d.eq(2),i=angular.isDefined(c.mousewheel)?a.$parent.$eval(c.mousewheel):g.mousewheel;i&&this.setupMousewheelEvents(e,f,h);var j=angular.isDefined(c.arrowkeys)?a.$parent.$eval(c.arrowkeys):g.arrowkeys;j&&this.setupArrowkeyEvents(e,f,h),a.readonlyInput=angular.isDefined(c.readonlyInput)?a.$parent.$eval(c.readonlyInput):g.readonlyInput,this.setupInputEvents(e,f,h)};var x=g.hourStep;c.hourStep&&t.push(a.$parent.$watch(d(c.hourStep),function(a){x=+a}));var y=g.minuteStep;c.minuteStep&&t.push(a.$parent.$watch(d(c.minuteStep),function(a){y=+a}));var z;t.push(a.$parent.$watch(d(c.min),function(a){var b=new Date(a);z=isNaN(b)?void 0:b}));var A;t.push(a.$parent.$watch(d(c.max),function(a){var b=new Date(a);A=isNaN(b)?void 0:b}));var B=!1;c.ngDisabled&&t.push(a.$parent.$watch(d(c.ngDisabled),function(a){B=a})),a.noIncrementHours=function(){var a=p(s,60*x);return B||a>A||s>a&&z>a},a.noDecrementHours=function(){var a=p(s,60*-x);return B||z>a||a>s&&a>A},a.noIncrementMinutes=function(){var a=p(s,y);return B||a>A||s>a&&z>a},a.noDecrementMinutes=function(){var a=p(s,-y);return B||z>a||a>s&&a>A},a.noIncrementSeconds=function(){var a=q(s,C);return B||a>A||s>a&&z>a},a.noDecrementSeconds=function(){var a=q(s,-C);return B||z>a||a>s&&a>A},a.noToggleMeridian=function(){return s.getHours()<12?B||p(s,720)>A:B||p(s,-720)0};b.bind("mousewheel wheel",function(b){B||a.$apply(e(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.bind("mousewheel wheel",function(b){B||a.$apply(e(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()}),d.bind("mousewheel wheel",function(b){B||a.$apply(e(b)?a.incrementSeconds():a.decrementSeconds()),b.preventDefault()})},this.setupArrowkeyEvents=function(b,c,d){b.bind("keydown",function(b){B||(38===b.which?(b.preventDefault(),a.incrementHours(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementHours(),a.$apply()))}),c.bind("keydown",function(b){B||(38===b.which?(b.preventDefault(),a.incrementMinutes(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementMinutes(),a.$apply()))}),d.bind("keydown",function(b){B||(38===b.which?(b.preventDefault(),a.incrementSeconds(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementSeconds(),a.$apply()))})},this.setupInputEvents=function(b,c,d){if(a.readonlyInput)return a.updateHours=angular.noop,a.updateMinutes=angular.noop,void(a.updateSeconds=angular.noop);var e=function(b,c,d){u.$setViewValue(null),u.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b),angular.isDefined(c)&&(a.invalidMinutes=c),angular.isDefined(d)&&(a.invalidSeconds=d)};a.updateHours=function(){var a=h(),b=i();u.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(s.setHours(a),s.setMinutes(b),z>s||s>A?e(!0):l("h")):e(!0)},b.bind("blur",function(b){u.$setTouched(),r()?m():null===a.hours||""===a.hours?e(!0):!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=k(a.hours,!w)})}),a.updateMinutes=function(){var a=i(),b=h();u.$setDirty(),angular.isDefined(a)&&angular.isDefined(b)?(s.setHours(b),s.setMinutes(a),z>s||s>A?e(void 0,!0):l("m")):e(void 0,!0)},c.bind("blur",function(b){u.$setTouched(),r()?m():null===a.minutes?e(void 0,!0):!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=k(a.minutes)})}),a.updateSeconds=function(){var a=j();u.$setDirty(),angular.isDefined(a)?(s.setSeconds(a),l("s")):e(void 0,void 0,!0)},d.bind("blur",function(b){r()?m():!a.invalidSeconds&&a.seconds<10&&a.$apply(function(){a.seconds=k(a.seconds)})})},this.render=function(){var b=u.$viewValue;isNaN(b)?(u.$setValidity("time",!1),e.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(b&&(s=b),z>s||s>A?(u.$setValidity("time",!1),a.invalidHours=!0,a.invalidMinutes=!0):m(),n())},a.showSpinners=angular.isDefined(c.showSpinners)?a.$parent.$eval(c.showSpinners):g.showSpinners,a.incrementHours=function(){a.noIncrementHours()||o(60*x*60)},a.decrementHours=function(){a.noDecrementHours()||o(60*-x*60)},a.incrementMinutes=function(){a.noIncrementMinutes()||o(60*y)},a.decrementMinutes=function(){a.noDecrementMinutes()||o(60*-y)},a.incrementSeconds=function(){a.noIncrementSeconds()||o(C)},a.decrementSeconds=function(){a.noDecrementSeconds()||o(-C)},a.toggleMeridian=function(){var b=i(),c=h();a.noToggleMeridian()||(angular.isDefined(b)&&angular.isDefined(c)?o(720*(s.getHours()<12?60:-60)):a.meridian=a.meridian===v[0]?v[1]:v[0])},a.blur=function(){u.$setTouched()},a.$on("$destroy",function(){for(;t.length;)t.shift()()})}]).directive("uibTimepicker",["uibTimepickerConfig",function(a){return{require:["uibTimepicker","?^ngModel"],controller:"UibTimepickerController",controllerAs:"timepicker",replace:!0,scope:{},templateUrl:function(b,c){return c.templateUrl||a.templateUrl},link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.debounce","ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$$debounce","$uibPosition","uibTypeaheadParser",function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(){O.moveInProgress||(O.moveInProgress=!0,O.$digest()),Z()}function o(){O.position=E?l.offset(b):l.position(b),O.position.top+=b.prop("offsetHeight")}var p,q,r=[9,13,27,38,40],s=200,t=a.$eval(c.typeaheadMinLength);t||0===t||(t=1),a.$watch(c.typeaheadMinLength,function(a){t=a||0===a?a:1});var u=a.$eval(c.typeaheadWaitMs)||0,v=a.$eval(c.typeaheadEditable)!==!1;a.$watch(c.typeaheadEditable,function(a){v=a!==!1});var w,x,y=e(c.typeaheadLoading).assign||angular.noop,z=c.typeaheadShouldSelect?e(c.typeaheadShouldSelect):function(a,b){var c=b.$event;return 13===c.which||9===c.which},A=e(c.typeaheadOnSelect),B=angular.isDefined(c.typeaheadSelectOnBlur)?a.$eval(c.typeaheadSelectOnBlur):!1,C=e(c.typeaheadNoResults).assign||angular.noop,D=c.typeaheadInputFormatter?e(c.typeaheadInputFormatter):void 0,E=c.typeaheadAppendToBody?a.$eval(c.typeaheadAppendToBody):!1,F=c.typeaheadAppendTo?a.$eval(c.typeaheadAppendTo):null,G=a.$eval(c.typeaheadFocusFirst)!==!1,H=c.typeaheadSelectOnExact?a.$eval(c.typeaheadSelectOnExact):!1,I=e(c.typeaheadIsOpen).assign||angular.noop,J=a.$eval(c.typeaheadShowHint)||!1,K=e(c.ngModel),L=e(c.ngModel+"($$$p)"),M=function(b,c){return angular.isFunction(K(a))&&q&&q.$options&&q.$options.getterSetter?L(b,{$$$p:c}):K.assign(b,c)},N=m.parse(c.uibTypeahead),O=a.$new(),P=a.$on("$destroy",function(){O.$destroy()});O.$on("$destroy",P);var Q="typeahead-"+O.$id+"-"+Math.floor(1e4*Math.random());b.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":Q});var R,S;J&&(R=angular.element(""),R.css("position","relative"),b.after(R),S=b.clone(),S.attr("placeholder",""),S.attr("tabindex","-1"),S.val(""),S.css({position:"absolute",top:"0px",left:"0px","border-color":"transparent","box-shadow":"none",opacity:1,background:"none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)",color:"#999"}),b.css({position:"relative","vertical-align":"top","background-color":"transparent"}),R.append(S),S.after(b));var T=angular.element("");T.attr({id:Q,matches:"matches",active:"activeIdx",select:"select(activeIdx, evt)","move-in-progress":"moveInProgress",query:"query",position:"position","assign-is-open":"assignIsOpen(isOpen)",debounce:"debounceUpdate"}),angular.isDefined(c.typeaheadTemplateUrl)&&T.attr("template-url",c.typeaheadTemplateUrl),angular.isDefined(c.typeaheadPopupTemplateUrl)&&T.attr("popup-template-url",c.typeaheadPopupTemplateUrl);var U=function(){J&&S.val("")},V=function(){O.matches=[],O.activeIdx=-1,b.attr("aria-expanded",!1),U()},W=function(a){return Q+"-option-"+a};O.$watch("activeIdx",function(a){0>a?b.removeAttr("aria-activedescendant"):b.attr("aria-activedescendant",W(a))});var X=function(a,b){return O.matches.length>b&&a?a.toUpperCase()===O.matches[b].label.toUpperCase():!1},Y=function(c,d){var e={$viewValue:c};y(a,!0),C(a,!1),f.when(N.source(a,e)).then(function(f){var g=c===p.$viewValue;if(g&&w)if(f&&f.length>0){O.activeIdx=G?0:-1,C(a,!1),O.matches.length=0;for(var h=0;h0&&i.slice(0,c.length).toUpperCase()===c.toUpperCase()?S.val(c+i.slice(c.length)):S.val("")}}else V(),C(a,!0);g&&y(a,!1)},function(){V(),y(a,!1),C(a,!0)})};E&&(angular.element(i).on("resize",n),h.find("body").on("scroll",n));var Z=k(function(){O.matches.length&&o(),O.moveInProgress=!1},s);O.moveInProgress=!1,O.query=void 0;var $,_=function(a){$=g(function(){Y(a)},u)},aa=function(){$&&g.cancel($)};V(),O.assignIsOpen=function(b){I(a,b)},O.select=function(d,e){var f,h,i={};x=!0,i[N.itemName]=h=O.matches[d].model,f=N.modelMapper(a,i),M(a,f),p.$setValidity("editable",!0),p.$setValidity("parse",!0),A(a,{$item:h,$model:f,$label:N.viewMapper(a,i),$event:e}),V(),O.$eval(c.typeaheadFocusOnSelect)!==!1&&g(function(){b[0].focus()},0,!1)},b.on("keydown",function(b){if(0!==O.matches.length&&-1!==r.indexOf(b.which)){var c=z(a,{$event:b});if(-1===O.activeIdx&&c||9===b.which&&b.shiftKey)return V(),void O.$digest();b.preventDefault();var d;switch(b.which){case 27:b.stopPropagation(),V(),a.$digest();break;case 38:O.activeIdx=(O.activeIdx>0?O.activeIdx:O.matches.length)-1,O.$digest(),d=T.find("li")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;case 40:O.activeIdx=(O.activeIdx+1)%O.matches.length,O.$digest(),d=T.find("li")[O.activeIdx],d.parentNode.scrollTop=d.offsetTop;break;default:c&&O.$apply(function(){angular.isNumber(O.debounceUpdate)||angular.isObject(O.debounceUpdate)?k(function(){O.select(O.activeIdx,b)},angular.isNumber(O.debounceUpdate)?O.debounceUpdate:O.debounceUpdate["default"]):O.select(O.activeIdx,b)})}}}),b.bind("focus",function(a){w=!0,0!==t||p.$viewValue||g(function(){Y(p.$viewValue,a)},0)}),b.bind("blur",function(a){B&&O.matches.length&&-1!==O.activeIdx&&!x&&(x=!0,O.$apply(function(){angular.isObject(O.debounceUpdate)&&angular.isNumber(O.debounceUpdate.blur)?k(function(){O.select(O.activeIdx,a)},O.debounceUpdate.blur):O.select(O.activeIdx,a)})),!v&&p.$error.editable&&(p.$setViewValue(),p.$setValidity("editable",!0),p.$setValidity("parse",!0),b.val("")),w=!1,x=!1});var ba=function(c){b[0]!==c.target&&3!==c.which&&0!==O.matches.length&&(V(),j.$$phase||a.$digest())};h.on("click",ba),a.$on("$destroy",function(){h.off("click",ba),(E||F)&&ca.remove(),E&&(angular.element(i).off("resize",n),h.find("body").off("scroll",n)),T.remove(),J&&R.remove()});var ca=d(T)(O);E?h.find("body").append(ca):F?angular.element(F).eq(0).append(ca):b.after(ca),this.init=function(b,c){p=b,q=c,O.debounceUpdate=p.$options&&e(p.$options.debounce)(a),p.$parsers.unshift(function(b){return w=!0,0===t||b&&b.length>=t?u>0?(aa(),_(b)):Y(b):(y(a,!1),aa(),V()),v?b:b?void p.$setValidity("editable",!1):(p.$setValidity("editable",!0),null)}),p.$formatters.push(function(b){var c,d,e={};return v||p.$setValidity("editable",!0),D?(e.$model=b,D(a,e)):(e[N.itemName]=b,c=N.viewMapper(a,e),e[N.itemName]=void 0,d=N.viewMapper(a,e),c!==d?c:b)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","^?ngModelOptions","uibTypeahead"],link:function(a,b,c,d){d[2].init(d[0],d[1])}}}).directive("uibTypeaheadPopup",["$$debounce",function(a){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&",assignIsOpen:"&",debounce:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"uib/template/typeahead/typeahead-popup.html"},link:function(b,c,d){b.templateUrl=d.templateUrl,b.isOpen=function(){var a=b.matches.length>0;return b.assignIsOpen({isOpen:a}),a},b.isActive=function(a){return b.active===a},b.selectActive=function(a){b.active=a},b.selectMatch=function(c,d){var e=b.debounce();angular.isNumber(e)||angular.isObject(e)?a(function(){b.select({activeIdx:c,evt:d})},angular.isNumber(e)?e:e["default"]):b.select({activeIdx:c,evt:d})}}}}]).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(a,b,c){return{scope:{index:"=",match:"=",query:"="},link:function(d,e,f){var g=c(f.templateUrl)(d.$parent)||"uib/template/typeahead/typeahead-match.html";a(g).then(function(a){var c=angular.element(a.trim());e.replaceWith(c),b(c)(d)})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(a,b,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function e(a){return/<.*>/g.test(a)}var f;return f=b.has("$sanitize"),function(b,g){return!f&&e(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=g?(""+b).replace(new RegExp(d(g),"gi"),"$&"):b,f||(b=a.trustAsHtml(b)),b}}]),angular.module("ui.bootstrap.carousel").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibCarouselCss&&angular.element(document).find("head").prepend(''),angular.$$uibCarouselCss=!0}),angular.module("ui.bootstrap.datepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerCss=!0}),angular.module("ui.bootstrap.position").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibPositionCss&&angular.element(document).find("head").prepend(''),angular.$$uibPositionCss=!0}),angular.module("ui.bootstrap.datepickerPopup").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerpopupCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerpopupCss=!0}),angular.module("ui.bootstrap.tooltip").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTooltipCss&&angular.element(document).find("head").prepend(''),
+angular.$$uibTooltipCss=!0}),angular.module("ui.bootstrap.timepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTimepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibTimepickerCss=!0}),angular.module("ui.bootstrap.typeahead").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTypeaheadCss&&angular.element(document).find("head").prepend(''),angular.$$uibTypeaheadCss=!0});
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/.bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/.bower.json
new file mode 100644
index 0000000000..c2dae471af
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/.bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "angular-cache-buster",
+ "version": "0.4.3",
+ "homepage": "https://github.com/saintmac/angular-cache-buster",
+ "authors": [
+ "saintmac "
+ ],
+ "description": "Cache Buster for AngularJS $http (and $resource). Especially useful with Internet Explorer (IE8, IE9)",
+ "main": "angular-cache-buster.js",
+ "keywords": [
+ "angularjs",
+ "cache",
+ "buster",
+ "ie8",
+ "ie9",
+ "$http",
+ "$resource"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "devDependencies": {
+ "angular": "~1.2.13",
+ "angular-mocks": "~1.2.13"
+ },
+ "_release": "0.4.3",
+ "_resolution": {
+ "type": "version",
+ "tag": "0.4.3",
+ "commit": "c6c378db5cfc2431773e05743e72e1e97fa1ef16"
+ },
+ "_source": "https://github.com/saintmac/angular-cache-buster.git",
+ "_target": "0.4.3",
+ "_originalSource": "angular-cache-buster"
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/README.md
new file mode 100644
index 0000000000..23cf042d11
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/README.md
@@ -0,0 +1,37 @@
+Cache Buster for Angular JS $http and $resource.
+Especially useful with Internet Explorer (IE8, IE9)
+
+# install
+
+ bower install angular-cache-buster --save
+
+In your app module definition, add `ngCacheBuster` as a dependency
+
+ angular.module('myApp', ['ngCacheBuster']);
+
+# configure
+
+AngularCacheBuster adds a cache buster to any $http requests (and hence to $resource requests).
+Since you probably want to maintain browser caching for your views, partials or other routes, you can supply a list of regexes that will be matched against all URL's. By default the supplied matchlist is a whitelist (i.e. busting everything not matching an entry in the list) but you can also set it to be a blacklist, (i.e. busting everything except the matching entries)
+
+For instance, if you want to bust everything except views in a 'partials' folder and images in a 'images' folder , you can configure AngularCacheBuster this way:
+
+ angular.module('yourApp', ['ngCacheBuster'])
+ .config(function(httpRequestInterceptorCacheBusterProvider){
+ httpRequestInterceptorCacheBusterProvider.setMatchlist([/.*partials.*/,/.*images.*/]);
+ });
+
+If instead you want to allow everything to be cached, except your "/api/users" and "api/orders" (assuming they are the only things that change frequently), you can supply a matchlist as before, but pass in a second boolean argument "blacklist" set to true as well:
+
+
+ angular.module('yourApp', ['ngCacheBuster'])
+ .config(function(httpRequestInterceptorCacheBusterProvider){
+ httpRequestInterceptorCacheBusterProvider.setMatchlist([/.*orders.*/,/.*users.*/],true);
+ });
+
+# use
+
+That's it! All your resource calls will have a cache buster added for anything not in the whitelist, or if you specified "blacklist", for everything matching the blacklist,
+
+# test
+`karma start` to launch the tests
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/angular-cache-buster.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/angular-cache-buster.js
new file mode 100644
index 0000000000..a368a50085
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/angular-cache-buster.js
@@ -0,0 +1,62 @@
+angular.module('ngCacheBuster', [])
+ .config(['$httpProvider', function($httpProvider) {
+ return $httpProvider.interceptors.push('httpRequestInterceptorCacheBuster');
+ }])
+ .provider('httpRequestInterceptorCacheBuster', function() {
+
+ this.matchlist = [/.*partials.*/, /.*views.*/ ];
+ this.logRequests = false;
+
+ //Default to whitelist (i.e. block all except matches)
+ this.black=false;
+
+ //Select blacklist or whitelist, default to whitelist
+ this.setMatchlist = function(list,black) {
+ this.black = typeof black != 'undefined' ? black : false
+ this.matchlist = list;
+ };
+
+
+ this.setLogRequests = function(logRequests) {
+ this.logRequests = logRequests;
+ };
+
+ this.$get = ['$q', '$log', function($q, $log) {
+ var matchlist = this.matchlist;
+ var logRequests = this.logRequests;
+ var black = this.black;
+ if (logRequests) {
+ $log.log("Blacklist? ",black);
+ }
+ return {
+ 'request': function(config) {
+ //Blacklist by default, match with whitelist
+ var busted= !black;
+
+ for(var i=0; i< matchlist.length; i++){
+ if(config.url.match(matchlist[i])) {
+ busted=black; break;
+ }
+ }
+
+ //Bust if the URL was on blacklist or not on whitelist
+ if (busted) {
+ var d = new Date();
+ config.url = config.url.replace(/[?|&]cacheBuster=\d+/,'');
+ //Some url's allready have '?' attached
+ config.url+=config.url.indexOf('?') === -1 ? '?' : '&'
+ config.url += 'cacheBuster=' + d.getTime();
+ }
+
+ if (logRequests) {
+ var log='request.url =' + config.url
+ busted ? $log.warn(log) : $log.info(log)
+ }
+
+ return config || $q.when(config);
+ }
+ }
+ }];
+ });
+
+
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/bower.json
new file mode 100644
index 0000000000..57187ed64f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "angular-cache-buster",
+ "version": "0.4.3",
+ "homepage": "https://github.com/saintmac/angular-cache-buster",
+ "authors": [
+ "saintmac "
+ ],
+ "description": "Cache Buster for AngularJS $http (and $resource). Especially useful with Internet Explorer (IE8, IE9)",
+ "main": "angular-cache-buster.js",
+ "keywords": [
+ "angularjs",
+ "cache",
+ "buster",
+ "ie8",
+ "ie9",
+ "$http",
+ "$resource"
+ ],
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "devDependencies": {
+ "angular": "~1.2.13",
+ "angular-mocks": "~1.2.13"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/karma.conf.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/karma.conf.js
new file mode 100644
index 0000000000..92dcc9ef65
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/karma.conf.js
@@ -0,0 +1,79 @@
+// Karma configuration
+// Generated on Sat Feb 22 2014 23:17:37 GMT+0100 (CET)
+
+module.exports = function(config) {
+ config.set({
+
+ // base path, that will be used to resolve files and exclude
+ basePath: '',
+
+
+ // frameworks to use
+ frameworks: ['jasmine'],
+
+
+ // list of files / patterns to load in the browser
+ files: [
+ 'bower_components/angular/angular.js',
+ 'bower_components/angular-mocks/angular-mocks.js',
+ 'angular-cache-buster.js',
+ 'test/*.js'
+ ],
+
+
+ // list of files to exclude
+ exclude: [
+
+ ],
+
+
+ // test results reporter to use
+ // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
+ reporters: ['progress'],
+
+
+ // web server port
+ port: 9876,
+
+
+ // enable / disable colors in the output (reporters and logs)
+ colors: true,
+
+
+ // level of logging
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+
+ // Start these browsers, currently available:
+ // - Chrome
+ // - ChromeCanary
+ // - Firefox
+ // - Opera (has to be installed with `npm install karma-opera-launcher`)
+ // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
+ // - PhantomJS
+ // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
+ browsers: ['Chrome'],
+
+
+ // If browser does not capture in given timeout [ms], kill it
+ captureTimeout: 60000,
+
+
+ // Continuous Integration mode
+ // if true, it capture browsers, run tests and exit
+ singleRun: false,
+
+ plugins : [
+ 'karma-junit-reporter',
+ 'karma-chrome-launcher',
+ 'karma-firefox-launcher',
+ 'karma-script-launcher',
+ 'karma-jasmine'
+ ]
+ });
+};
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/package.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/package.json
new file mode 100644
index 0000000000..5f1c7b3ab6
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cache-buster/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "angular-cache-buster",
+ "version": "0.4.3",
+ "description": "Cache Buster for AngularJS $http (and $resource). Especially useful with Internet Explorer (IE8, IE9)",
+ "main": "angular-cache-buster.js",
+ "scripts": {
+ "test": "karma start"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/saintmac/angular-cache-buster.git"
+ },
+ "keywords": [
+ "angularjs",
+ "$http",
+ "$resource",
+ "cache",
+ "buster",
+ "internet",
+ "explorer",
+ "ie8",
+ "ie9"
+ ],
+ "author": "saintmac (Martin Saint-Macary, http://vyte.in)",
+ "contributors": ["Alfred Bratterud "],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/saintmac/angular-cache-buster/issues"
+ },
+ "homepage": "https://github.com/saintmac/angular-cache-buster",
+ "devDependencies": {
+ "karma-script-launcher": "~0.1.0",
+ "karma-chrome-launcher": "~0.1.3",
+ "karma-firefox-launcher": "~0.1.3",
+ "karma-html2js-preprocessor": "~0.1.0",
+ "karma-jasmine": "~0.1.5",
+ "karma-coffee-preprocessor": "~0.1.3",
+ "requirejs": "~2.1.11",
+ "karma-requirejs": "~0.2.1",
+ "karma-phantomjs-launcher": "~0.1.2",
+ "karma": "~0.12.15"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/.bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/.bower.json
new file mode 100644
index 0000000000..59a3fe076a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/.bower.json
@@ -0,0 +1,20 @@
+{
+ "name": "angular-cookies",
+ "version": "1.5.8",
+ "license": "MIT",
+ "main": "./angular-cookies.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.5.8"
+ },
+ "homepage": "https://github.com/angular/bower-angular-cookies",
+ "_release": "1.5.8",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.8",
+ "commit": "a2247a1efb436f0293289fb74ebd76fbe52a7422"
+ },
+ "_source": "https://github.com/angular/bower-angular-cookies.git",
+ "_target": "1.5.8",
+ "_originalSource": "angular-cookies"
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/LICENSE.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/LICENSE.md
new file mode 100644
index 0000000000..2c395eef1b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Angular
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/README.md
new file mode 100644
index 0000000000..7b190d3461
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/README.md
@@ -0,0 +1,68 @@
+# packaged angular-cookies
+
+This repo is for distribution on `npm` and `bower`. The source for this module is in the
+[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngCookies).
+Please file issues and pull requests against that repo.
+
+## Install
+
+You can install this package either with `npm` or with `bower`.
+
+### npm
+
+```shell
+npm install angular-cookies
+```
+
+Then add `ngCookies` as a dependency for your app:
+
+```javascript
+angular.module('myApp', [require('angular-cookies')]);
+```
+
+### bower
+
+```shell
+bower install angular-cookies
+```
+
+Add a `
+```
+
+Then add `ngCookies` as a dependency for your app:
+
+```javascript
+angular.module('myApp', ['ngCookies']);
+```
+
+## Documentation
+
+Documentation is available on the
+[AngularJS docs site](http://docs.angularjs.org/api/ngCookies).
+
+## License
+
+The MIT License
+
+Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.js
new file mode 100644
index 0000000000..a03ff49f2f
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.js
@@ -0,0 +1,322 @@
+/**
+ * @license AngularJS v1.5.8
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular) {'use strict';
+
+/**
+ * @ngdoc module
+ * @name ngCookies
+ * @description
+ *
+ * # ngCookies
+ *
+ * The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
+ *
+ *
+ *
+ *
+ * See {@link ngCookies.$cookies `$cookies`} for usage.
+ */
+
+
+angular.module('ngCookies', ['ng']).
+ /**
+ * @ngdoc provider
+ * @name $cookiesProvider
+ * @description
+ * Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service.
+ * */
+ provider('$cookies', [function $CookiesProvider() {
+ /**
+ * @ngdoc property
+ * @name $cookiesProvider#defaults
+ * @description
+ *
+ * Object containing default options to pass when setting cookies.
+ *
+ * The object may have following properties:
+ *
+ * - **path** - `{string}` - The cookie will be available only for this path and its
+ * sub-paths. By default, this is the URL that appears in your `` tag.
+ * - **domain** - `{string}` - The cookie will be available only for this domain and
+ * its sub-domains. For security reasons the user agent will not accept the cookie
+ * if the current domain is not a sub-domain of this domain or equal to it.
+ * - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
+ * or a Date object indicating the exact date/time this cookie will expire.
+ * - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a
+ * secured connection.
+ *
+ * Note: By default, the address that appears in your `` tag will be used as the path.
+ * This is important so that cookies will be visible for all routes when html5mode is enabled.
+ *
+ **/
+ var defaults = this.defaults = {};
+
+ function calcOptions(options) {
+ return options ? angular.extend({}, defaults, options) : defaults;
+ }
+
+ /**
+ * @ngdoc service
+ * @name $cookies
+ *
+ * @description
+ * Provides read/write access to browser's cookies.
+ *
+ *
+ * Up until Angular 1.3, `$cookies` exposed properties that represented the
+ * current browser cookie values. In version 1.4, this behavior has changed, and
+ * `$cookies` now provides a standard api of getters, setters etc.
+ *
+ *
+ * Requires the {@link ngCookies `ngCookies`} module to be installed.
+ *
+ * @example
+ *
+ * ```js
+ * angular.module('cookiesExample', ['ngCookies'])
+ * .controller('ExampleController', ['$cookies', function($cookies) {
+ * // Retrieving a cookie
+ * var favoriteCookie = $cookies.get('myFavorite');
+ * // Setting a cookie
+ * $cookies.put('myFavorite', 'oatmeal');
+ * }]);
+ * ```
+ */
+ this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) {
+ return {
+ /**
+ * @ngdoc method
+ * @name $cookies#get
+ *
+ * @description
+ * Returns the value of given cookie key
+ *
+ * @param {string} key Id to use for lookup.
+ * @returns {string} Raw cookie value.
+ */
+ get: function(key) {
+ return $$cookieReader()[key];
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#getObject
+ *
+ * @description
+ * Returns the deserialized value of given cookie key
+ *
+ * @param {string} key Id to use for lookup.
+ * @returns {Object} Deserialized cookie value.
+ */
+ getObject: function(key) {
+ var value = this.get(key);
+ return value ? angular.fromJson(value) : value;
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#getAll
+ *
+ * @description
+ * Returns a key value object with all the cookies
+ *
+ * @returns {Object} All cookies
+ */
+ getAll: function() {
+ return $$cookieReader();
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#put
+ *
+ * @description
+ * Sets a value for given cookie key
+ *
+ * @param {string} key Id for the `value`.
+ * @param {string} value Raw value to be stored.
+ * @param {Object=} options Options object.
+ * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
+ */
+ put: function(key, value, options) {
+ $$cookieWriter(key, value, calcOptions(options));
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#putObject
+ *
+ * @description
+ * Serializes and sets a value for given cookie key
+ *
+ * @param {string} key Id for the `value`.
+ * @param {Object} value Value to be stored.
+ * @param {Object=} options Options object.
+ * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
+ */
+ putObject: function(key, value, options) {
+ this.put(key, angular.toJson(value), options);
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookies#remove
+ *
+ * @description
+ * Remove given cookie
+ *
+ * @param {string} key Id of the key-value pair to delete.
+ * @param {Object=} options Options object.
+ * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
+ */
+ remove: function(key, options) {
+ $$cookieWriter(key, undefined, calcOptions(options));
+ }
+ };
+ }];
+ }]);
+
+angular.module('ngCookies').
+/**
+ * @ngdoc service
+ * @name $cookieStore
+ * @deprecated
+ * @requires $cookies
+ *
+ * @description
+ * Provides a key-value (string-object) storage, that is backed by session cookies.
+ * Objects put or retrieved from this storage are automatically serialized or
+ * deserialized by angular's toJson/fromJson.
+ *
+ * Requires the {@link ngCookies `ngCookies`} module to be installed.
+ *
+ *
+ * **Note:** The $cookieStore service is **deprecated**.
+ * Please use the {@link ngCookies.$cookies `$cookies`} service instead.
+ *
+ *
+ * @example
+ *
+ * ```js
+ * angular.module('cookieStoreExample', ['ngCookies'])
+ * .controller('ExampleController', ['$cookieStore', function($cookieStore) {
+ * // Put cookie
+ * $cookieStore.put('myFavorite','oatmeal');
+ * // Get cookie
+ * var favoriteCookie = $cookieStore.get('myFavorite');
+ * // Removing a cookie
+ * $cookieStore.remove('myFavorite');
+ * }]);
+ * ```
+ */
+ factory('$cookieStore', ['$cookies', function($cookies) {
+
+ return {
+ /**
+ * @ngdoc method
+ * @name $cookieStore#get
+ *
+ * @description
+ * Returns the value of given cookie key
+ *
+ * @param {string} key Id to use for lookup.
+ * @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
+ */
+ get: function(key) {
+ return $cookies.getObject(key);
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookieStore#put
+ *
+ * @description
+ * Sets a value for given cookie key
+ *
+ * @param {string} key Id for the `value`.
+ * @param {Object} value Value to be stored.
+ */
+ put: function(key, value) {
+ $cookies.putObject(key, value);
+ },
+
+ /**
+ * @ngdoc method
+ * @name $cookieStore#remove
+ *
+ * @description
+ * Remove given cookie
+ *
+ * @param {string} key Id of the key-value pair to delete.
+ */
+ remove: function(key) {
+ $cookies.remove(key);
+ }
+ };
+
+ }]);
+
+/**
+ * @name $$cookieWriter
+ * @requires $document
+ *
+ * @description
+ * This is a private service for writing cookies
+ *
+ * @param {string} name Cookie name
+ * @param {string=} value Cookie value (if undefined, cookie will be deleted)
+ * @param {Object=} options Object with options that need to be stored for the cookie.
+ */
+function $$CookieWriter($document, $log, $browser) {
+ var cookiePath = $browser.baseHref();
+ var rawDocument = $document[0];
+
+ function buildCookieString(name, value, options) {
+ var path, expires;
+ options = options || {};
+ expires = options.expires;
+ path = angular.isDefined(options.path) ? options.path : cookiePath;
+ if (angular.isUndefined(value)) {
+ expires = 'Thu, 01 Jan 1970 00:00:00 GMT';
+ value = '';
+ }
+ if (angular.isString(expires)) {
+ expires = new Date(expires);
+ }
+
+ var str = encodeURIComponent(name) + '=' + encodeURIComponent(value);
+ str += path ? ';path=' + path : '';
+ str += options.domain ? ';domain=' + options.domain : '';
+ str += expires ? ';expires=' + expires.toUTCString() : '';
+ str += options.secure ? ';secure' : '';
+
+ // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
+ // - 300 cookies
+ // - 20 cookies per unique domain
+ // - 4096 bytes per cookie
+ var cookieLength = str.length + 1;
+ if (cookieLength > 4096) {
+ $log.warn("Cookie '" + name +
+ "' possibly not set or overflowed because it was too large (" +
+ cookieLength + " > 4096 bytes)!");
+ }
+
+ return str;
+ }
+
+ return function(name, value, options) {
+ rawDocument.cookie = buildCookieString(name, value, options);
+ };
+}
+
+$$CookieWriter.$inject = ['$document', '$log', '$browser'];
+
+angular.module('ngCookies').provider('$$cookieWriter', function $$CookieWriterProvider() {
+ this.$get = $$CookieWriter;
+});
+
+
+})(window, window.angular);
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.min.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.min.js
new file mode 100644
index 0000000000..d767aee298
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.min.js
@@ -0,0 +1,9 @@
+/*
+ AngularJS v1.5.8
+ (c) 2010-2016 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(n,c){'use strict';function l(b,a,g){var d=g.baseHref(),k=b[0];return function(b,e,f){var g,h;f=f||{};h=f.expires;g=c.isDefined(f.path)?f.path:d;c.isUndefined(e)&&(h="Thu, 01 Jan 1970 00:00:00 GMT",e="");c.isString(h)&&(h=new Date(h));e=encodeURIComponent(b)+"="+encodeURIComponent(e);e=e+(g?";path="+g:"")+(f.domain?";domain="+f.domain:"");e+=h?";expires="+h.toUTCString():"";e+=f.secure?";secure":"";f=e.length+1;4096 4096 bytes)!");k.cookie=e}}c.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,g){return{get:function(d){return a()[d]},getObject:function(d){return(d=this.get(d))?c.fromJson(d):d},getAll:function(){return a()},put:function(d,a,m){g(d,a,m?c.extend({},b,m):b)},putObject:function(d,b,a){this.put(d,c.toJson(b),a)},remove:function(a,k){g(a,void 0,k?c.extend({},b,k):b)}}}]}]);c.module("ngCookies").factory("$cookieStore",
+["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,c){b.putObject(a,c)},remove:function(a){b.remove(a)}}}]);l.$inject=["$document","$log","$browser"];c.module("ngCookies").provider("$$cookieWriter",function(){this.$get=l})})(window,window.angular);
+//# sourceMappingURL=angular-cookies.min.js.map
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.min.js.map b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.min.js.map
new file mode 100644
index 0000000000..42f748a7c0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/angular-cookies.min.js.map
@@ -0,0 +1,8 @@
+{
+"version":3,
+"file":"angular-cookies.min.js",
+"lineCount":8,
+"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkB,CA2Q3BC,QAASA,EAAc,CAACC,CAAD,CAAYC,CAAZ,CAAkBC,CAAlB,CAA4B,CACjD,IAAIC,EAAaD,CAAAE,SAAA,EAAjB,CACIC,EAAcL,CAAA,CAAU,CAAV,CAmClB,OAAO,SAAQ,CAACM,CAAD,CAAOC,CAAP,CAAcC,CAAd,CAAuB,CAjCW,IAC3CC,CAD2C,CACrCC,CACVF,EAAA,CAgCoDA,CAhCpD,EAAqB,EACrBE,EAAA,CAAUF,CAAAE,QACVD,EAAA,CAAOX,CAAAa,UAAA,CAAkBH,CAAAC,KAAlB,CAAA,CAAkCD,CAAAC,KAAlC,CAAiDN,CACpDL,EAAAc,YAAA,CAAoBL,CAApB,CAAJ,GACEG,CACA,CADU,+BACV,CAAAH,CAAA,CAAQ,EAFV,CAIIT,EAAAe,SAAA,CAAiBH,CAAjB,CAAJ,GACEA,CADF,CACY,IAAII,IAAJ,CAASJ,CAAT,CADZ,CAIIK,EAAAA,CAAMC,kBAAA,CAqB6BV,CArB7B,CAANS,CAAiC,GAAjCA,CAAuCC,kBAAA,CAAmBT,CAAnB,CAE3CQ,EAAA,CADAA,CACA,EADON,CAAA,CAAO,QAAP,CAAkBA,CAAlB,CAAyB,EAChC,GAAOD,CAAAS,OAAA,CAAiB,UAAjB,CAA8BT,CAAAS,OAA9B,CAA+C,EAAtD,CACAF,EAAA,EAAOL,CAAA,CAAU,WAAV,CAAwBA,CAAAQ,YAAA,EAAxB,CAAgD,EACvDH,EAAA,EAAOP,CAAAW,OAAA,CAAiB,SAAjB,CAA6B,EAMhCC,EAAAA,CAAeL,CAAAM,OAAfD,CAA4B,CACb,KAAnB,CAAIA,CAAJ,EACEnB,CAAAqB,KAAA,CAAU,UAAV,CASqChB,CATrC,CACE,6DADF;AAEEc,CAFF,CAEiB,iBAFjB,CASFf,EAAAkB,OAAA,CAJOR,CAG6B,CArCW,CAzPnDjB,CAAA0B,OAAA,CAAe,WAAf,CAA4B,CAAC,IAAD,CAA5B,CAAAC,SAAA,CAOY,UAPZ,CAOwB,CAACC,QAAyB,EAAG,CAwBjD,IAAIC,EAAW,IAAAA,SAAXA,CAA2B,EAiC/B,KAAAC,KAAA,CAAY,CAAC,gBAAD,CAAmB,gBAAnB,CAAqC,QAAQ,CAACC,CAAD,CAAiBC,CAAjB,CAAiC,CACxF,MAAO,CAWLC,IAAKA,QAAQ,CAACC,CAAD,CAAM,CACjB,MAAOH,EAAA,EAAA,CAAiBG,CAAjB,CADU,CAXd,CAyBLC,UAAWA,QAAQ,CAACD,CAAD,CAAM,CAEvB,MAAO,CADHzB,CACG,CADK,IAAAwB,IAAA,CAASC,CAAT,CACL,EAAQlC,CAAAoC,SAAA,CAAiB3B,CAAjB,CAAR,CAAkCA,CAFlB,CAzBpB,CAuCL4B,OAAQA,QAAQ,EAAG,CACjB,MAAON,EAAA,EADU,CAvCd,CAuDLO,IAAKA,QAAQ,CAACJ,CAAD,CAAMzB,CAAN,CAAaC,CAAb,CAAsB,CACjCsB,CAAA,CAAeE,CAAf,CAAoBzB,CAApB,CAAuCC,CAvFpC,CAAUV,CAAAuC,OAAA,CAAe,EAAf,CAAmBV,CAAnB,CAuF0BnB,CAvF1B,CAAV,CAAkDmB,CAuFrD,CADiC,CAvD9B,CAuELW,UAAWA,QAAQ,CAACN,CAAD,CAAMzB,CAAN,CAAaC,CAAb,CAAsB,CACvC,IAAA4B,IAAA,CAASJ,CAAT,CAAclC,CAAAyC,OAAA,CAAehC,CAAf,CAAd,CAAqCC,CAArC,CADuC,CAvEpC,CAsFLgC,OAAQA,QAAQ,CAACR,CAAD,CAAMxB,CAAN,CAAe,CAC7BsB,CAAA,CAAeE,CAAf,CAAoBS,IAAAA,EAApB,CAA2CjC,CAtHxC,CAAUV,CAAAuC,OAAA,CAAe,EAAf,CAAmBV,CAAnB,CAsH8BnB,CAtH9B,CAAV,CAAkDmB,CAsHrD,CAD6B,CAtF1B,CADiF,CAA9E,CAzDqC,CAA7B,CAPxB,CA8JA7B,EAAA0B,OAAA,CAAe,WAAf,CAAAkB,QAAA,CAiCS,cAjCT;AAiCyB,CAAC,UAAD,CAAa,QAAQ,CAACC,CAAD,CAAW,CAErD,MAAO,CAWLZ,IAAKA,QAAQ,CAACC,CAAD,CAAM,CACjB,MAAOW,EAAAV,UAAA,CAAmBD,CAAnB,CADU,CAXd,CAyBLI,IAAKA,QAAQ,CAACJ,CAAD,CAAMzB,CAAN,CAAa,CACxBoC,CAAAL,UAAA,CAAmBN,CAAnB,CAAwBzB,CAAxB,CADwB,CAzBrB,CAsCLiC,OAAQA,QAAQ,CAACR,CAAD,CAAM,CACpBW,CAAAH,OAAA,CAAgBR,CAAhB,CADoB,CAtCjB,CAF8C,CAAhC,CAjCzB,CAqIAjC,EAAA6C,QAAA,CAAyB,CAAC,WAAD,CAAc,MAAd,CAAsB,UAAtB,CAEzB9C,EAAA0B,OAAA,CAAe,WAAf,CAAAC,SAAA,CAAqC,gBAArC,CAAuDoB,QAA+B,EAAG,CACvF,IAAAjB,KAAA,CAAY7B,CAD2E,CAAzF,CAvT2B,CAA1B,CAAD,CA4TGF,MA5TH,CA4TWA,MAAAC,QA5TX;",
+"sources":["angular-cookies.js"],
+"names":["window","angular","$$CookieWriter","$document","$log","$browser","cookiePath","baseHref","rawDocument","name","value","options","path","expires","isDefined","isUndefined","isString","Date","str","encodeURIComponent","domain","toUTCString","secure","cookieLength","length","warn","cookie","module","provider","$CookiesProvider","defaults","$get","$$cookieReader","$$cookieWriter","get","key","getObject","fromJson","getAll","put","extend","putObject","toJson","remove","undefined","factory","$cookies","$inject","$$CookieWriterProvider"]
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/bower.json
new file mode 100644
index 0000000000..72f3a5f9e0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/bower.json
@@ -0,0 +1,10 @@
+{
+ "name": "angular-cookies",
+ "version": "1.5.8",
+ "license": "MIT",
+ "main": "./angular-cookies.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.5.8"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/index.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/index.js
new file mode 100644
index 0000000000..657667549a
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/index.js
@@ -0,0 +1,2 @@
+require('./angular-cookies');
+module.exports = 'ngCookies';
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/package.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/package.json
new file mode 100644
index 0000000000..296652c963
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-cookies/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "angular-cookies",
+ "version": "1.5.8",
+ "description": "AngularJS module for cookies",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/angular/angular.js.git"
+ },
+ "keywords": [
+ "angular",
+ "framework",
+ "browser",
+ "cookies",
+ "client-side"
+ ],
+ "author": "Angular Core Team ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/angular/angular.js/issues"
+ },
+ "homepage": "http://angularjs.org",
+ "jspm": {
+ "shim": {
+ "angular-cookies": {
+ "deps": ["angular"]
+ }
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/.bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/.bower.json
new file mode 100644
index 0000000000..77fd928010
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/.bower.json
@@ -0,0 +1,42 @@
+{
+ "name": "angular-loading-bar",
+ "main": [
+ "build/loading-bar.js",
+ "build/loading-bar.css"
+ ],
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "components",
+ "test",
+ "example"
+ ],
+ "dependencies": {
+ "angular": "^1.2.9"
+ },
+ "devDependencies": {
+ "angular": "~1.2.23",
+ "angular-1.3": "angular#1.3",
+ "angular-1.4": "angular#1.4",
+ "angular-mocks": "~1.2.9",
+ "angular-mocks-1.3": "angular-mocks#1.3",
+ "angular-mocks-1.4": "angular-mocks#1.4",
+ "angular-animate": "~1.2.9",
+ "angular-animate-1.3": "angular-animate#1.3",
+ "angular-animate-1.4": "angular-animate#1.4"
+ },
+ "resolutions": {
+ "angular": "~1.2.23"
+ },
+ "homepage": "https://github.com/chieffancypants/angular-loading-bar",
+ "version": "0.9.0",
+ "_release": "0.9.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "0.9.0",
+ "commit": "d734873e52ded18fa27d67f52272ae43267dfd63"
+ },
+ "_source": "https://github.com/chieffancypants/angular-loading-bar.git",
+ "_target": "0.9.0",
+ "_originalSource": "angular-loading-bar"
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/CHANGELOG.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/CHANGELOG.md
new file mode 100644
index 0000000000..8d124d0cff
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/CHANGELOG.md
@@ -0,0 +1,81 @@
+Changelog
+==========
+## 0.9.0
+- resolved issue with parentSelector when parent has no children
+([#244](https://github.com/chieffancypants/angular-loading-bar/pull/244))
+([#251](https://github.com/chieffancypants/angular-loading-bar/issues/251))
+([#239](https://github.com/chieffancypants/angular-loading-bar/issues/239))
+([#179](https://github.com/chieffancypants/angular-loading-bar/issues/179))
+- added style property to package.json
+([#271](https://github.com/chieffancypants/angular-loading-bar/pull/271))
+([#231](https://github.com/chieffancypants/angular-loading-bar/pull/231))
+- Removed duplicated property declaration in CSS
+([#226](https://github.com/chieffancypants/angular-loading-bar/pull/226))
+
+
+
+## 0.8.0
+- auto incrementing is now configurable
+([#209](https://github.com/chieffancypants/angular-loading-bar/pull/209))
+- removed `version` from bower.json
+([#207](https://github.com/chieffancypants/angular-loading-bar/pull/207))
+- added support for webpack and browserify
+([#200](https://github.com/chieffancypants/angular-loading-bar/pull/200))
+- spinner border radius 10px -> 50%
+([#184](https://github.com/chieffancypants/angular-loading-bar/issues/184))
+
+
+## 0.7.1
+- Merge correct PR for broken interceptor detection ([#133](https://github.com/chieffancypants/angular-loading-bar/pull/133), [#50](https://github.com/chieffancypants/angular-loading-bar/pull/50))
+
+## 0.7.0
+- Changes for animate.enter compatibility for 1.2 and 1.3 ([#170](https://github.com/chieffancypants/angular-loading-bar/pull/170))
+- Detect errors with other interceptors ([#133](https://github.com/chieffancypants/angular-loading-bar/pull/133), [#50](https://github.com/chieffancypants/angular-loading-bar/pull/50))
+- Provide more detail on response/responseError events ([#128](https://github.com/chieffancypants/angular-loading-bar/pull/128))
+- Change angular dependency in bower ([#126](https://github.com/chieffancypants/angular-loading-bar/issues/126))
+
+## 0.6.0
+- Customize progress bar template: ([#111](https://github.com/chieffancypants/angular-loading-bar/pull/111))
+- Only append bar to first parent found ([#108](https://github.com/chieffancypants/angular-loading-bar/pull/108))
+
+## 0.5.2:
+Fixes for Angular 1.3 breaking changes:
+- Circular dependencies: ([#98](https://github.com/chieffancypants/angular-loading-bar/issues/98)), ([#101](https://github.com/chieffancypants/angular-loading-bar/pull/101))
+- $animate no longer accepts callbacks: ([#102](https://github.com/chieffancypants/angular-loading-bar/pull/102))
+
+## 0.5.1
+- Reworked cache logic to allow cache:true ([#96](https://github.com/chieffancypants/angular-loading-bar/pull/96))
+
+## 0.5.0
+- Added spinner template configuration ([#82](https://github.com/chieffancypants/angular-loading-bar/pull/82))
+- $timeout was not canceled properly ([#79](https://github.com/chieffancypants/angular-loading-bar/pull/79))
+
+## 0.4.3
+- update z-index to work with other css frameworks ([#69](https://github.com/chieffancypants/angular-loading-bar/pull/69))
+- ignoreLoadingBar not ignored when calculating percentage complete ([#70](https://github.com/chieffancypants/angular-loading-bar/pull/70))
+
+## 0.4.2
+- Split loading bar into different modules so they can be included separately ([#46](https://github.com/chieffancypants/angular-loading-bar/issues/46))
+
+## 0.4.1
+- Fix for route views defined on body where loading bar is also attached ([#56](https://github.com/chieffancypants/angular-loading-bar/issues/56))
+
+## 0.4.0
+- Initial load percentage is now configurable ([#47](https://github.com/chieffancypants/angular-loading-bar/issues/47))
+- Peg graphic reworked so the loadingbar does not require CSS changes when not at the very top of the page ([#42](https://github.com/chieffancypants/angular-loading-bar/issues/42), [#45](https://github.com/chieffancypants/angular-loading-bar/issues/45), [#10](https://github.com/chieffancypants/angular-loading-bar/issues/10))
+- z-index of spinner increased to work with Bootstrap 3 z-indexes ([#53](https://github.com/chieffancypants/angular-loading-bar/issues/53))
+
+## 0.3.0
+- Loading bar only appears on XHR requests with high latency ([#27](https://github.com/chieffancypants/angular-loading-bar/issues/27))
+
+## 0.2.0
+- Progression bar not calculated correctly for consecutive calls within the 500ms delay ([#29](https://github.com/chieffancypants/angular-loading-bar/issues/29), [#32](https://github.com/chieffancypants/angular-loading-bar/issues/32))
+- Event broadcasts when loading (#31)
+
+## 0.1.1
+- Alias chieffancypants.loadingbar to angular-loading-bar (#25, #19)
+
+## 0.1.0
+- Fixed issues with Angular 1.2-rc3+
+- Ability to ignore particular XHR requests (#21)
+- Broadcasting of events (#18)
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/CONTRIBUTING.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/CONTRIBUTING.md
new file mode 100644
index 0000000000..aada2ccbad
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/CONTRIBUTING.md
@@ -0,0 +1,17 @@
+### Submitting a PR
+Excellent! You've chosen to help advance the project by either fixing a bug, or implementing a new feature. Before you put forth any work on a PR, please follow these steps:
+
+1. Ensure a similar PR has not already been opened or closed.
+1. Clearly define the intent of the PR. The more detail, the more likelihood of it getting merged.
+1. Is this a feature that would benefit the **majority** of users? This is a small library, and it intends to stay that way. If you do not believe most users of the project will benefit from your work, it should probably be added in your own application.
+1. Be sure to include test cases that cover all newly introduced code. This part is essential, as any PRs without tests will be closed.
+1. Link any [issues](https://github.com/chieffancypants/angular-loading-bar/issues) that are addressed by the PR.
+
+### Submitting a bug report
+If you believe you've found a bug in the source code, and are unable to fix it yourself (by submitting a PR) please follow these steps:
+
+1. Ensure the bug has not already been reported by searching the [issues](https://github.com/chieffancypants/angular-loading-bar/issues)
+1. Submit a reduced test case that clearly demonstrates the bug. This means submitting a plunker or jsfiddle with the bare minimum of code necessary to reproduce the bug. Without this, your issue may be closed as invalid.
+1. Include any relevant browser information
+1. If you're unable to fix this bug yourself, but can point to why it is occuring, please send that information along (line# or commit)
+
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/Gruntfile.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/Gruntfile.js
new file mode 100644
index 0000000000..d23ddf1bac
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/Gruntfile.js
@@ -0,0 +1,102 @@
+/*global module:false*/
+module.exports = function(grunt) {
+
+ grunt.initConfig({
+
+ // Metadata.
+ pkg: grunt.file.readJSON('package.json'),
+ banner: '/*! \n * <%= pkg.title || pkg.name %> v<%= pkg.version %>\n' +
+ ' * <%= pkg.homepage %>\n' +
+ ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
+ ' * License: <%= pkg.license %>\n' +
+ ' */\n',
+
+ // Task configuration.
+ uglify: {
+ options: {
+ banner: '<%= banner %>',
+ report: 'gzip'
+ },
+ build: {
+ src: 'src/loading-bar.js',
+ dest: 'build/loading-bar.min.js'
+ }
+ },
+
+ cssmin: {
+ options: {
+ banner: '<%= banner %>',
+ report: 'gzip'
+ },
+ minify: {
+ src: 'src/loading-bar.css',
+ dest: 'build/loading-bar.min.css'
+ }
+ },
+
+ karma: {
+ unit: {
+ configFile: 'test/karma-angular-1.2.conf.js',
+ singleRun: true,
+ coverageReporter: {
+ type: 'text',
+ dir: 'coverage/'
+ }
+ },
+ unit13: {
+ configFile: 'test/karma-angular-1.3.conf.js',
+ singleRun: true,
+ coverageReporter: {
+ type: 'text',
+ dir: 'coverage/'
+ }
+ },
+ unit14: {
+ configFile: 'test/karma-angular-1.4.conf.js',
+ singleRun: true,
+ coverageReporter: {
+ type: 'text',
+ dir: 'coverage/'
+ }
+ },
+ watch: {
+ configFile: 'test/karma-angular-1.2.conf.js',
+ singleRun: false,
+ reporters: ['progress'] // Don't display coverage
+ }
+ },
+
+ jshint: {
+ jshintrc: '.jshintrc',
+ gruntfile: {
+ src: 'Gruntfile.js'
+ },
+ src: {
+ src: ['src/*.js']
+ }
+ },
+
+ concat: {
+ build: {
+ options: {
+ banner: '<%= banner %>'
+ },
+ files: {
+ 'build/loading-bar.css': 'src/loading-bar.css',
+ 'build/loading-bar.js': 'src/loading-bar.js',
+ }
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-cssmin');
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-karma');
+
+ grunt.registerTask('default', ['jshint', 'karma:unit', 'karma:unit13', 'karma:unit14', 'uglify', 'cssmin', 'concat:build']);
+ grunt.registerTask('test', ['karma:watch']);
+ grunt.registerTask('build', ['default']);
+
+};
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/ISSUE_TEMPLATE.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000000..436af93a57
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/ISSUE_TEMPLATE.md
@@ -0,0 +1,14 @@
+#### Description of bug:
+
+
+#### Expected result:
+
+
+#### Actual result:
+
+
+#### Browsers affected:
+
+
+#### URL of reduced test case:
+
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/LICENSE b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/LICENSE
new file mode 100644
index 0000000000..252c23aa19
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2014 Wes Cruver
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/PULL_REQUEST_TEMPLATE.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000..a37ca3f5e9
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,13 @@
+#### Summary:
+
+Provide a general description of the code changes in your pull
+request. If bugs were fixed, please document the changes and why
+they were introduced.
+
+Please ensure that your PR contains test cases that cover all new
+code and any changes to existing code. Without tests, your PR is
+likely to be closed without merging.
+
+#### Related issues:
+Please review the [issues](https://github.com/chieffancypants/angular-loading-bar/issues)
+page, and link any issues that are addressed or related to this PR.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/README.md
new file mode 100644
index 0000000000..b195785f4c
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/README.md
@@ -0,0 +1,190 @@
+angular-loading-bar
+===================
+
+The idea is simple: Add a loading bar / progress bar whenever an XHR request goes out in angular. Multiple requests within the same time period get bundled together such that each response increments the progress bar by the appropriate amount.
+
+This is mostly cool because you simply include it in your app, and it works. There's no complicated setup, and no need to maintain the state of the loading bar; it's all handled automatically by the interceptor.
+
+**Requirements:** AngularJS 1.2+
+
+**File Size:** 2.4Kb minified, 0.5Kb gzipped
+
+
+## Usage:
+
+1. include the loading bar as a dependency for your app. If you want animations, include `ngAnimate` as well. *note: ngAnimate is optional*
+
+ ```js
+ angular.module('myApp', ['angular-loading-bar', 'ngAnimate'])
+ ```
+
+2. include the supplied JS and CSS file (or create your own CSS to override defaults).
+
+ ```html
+
+
+ ```
+
+3. That's it -- you're done!
+
+#### via bower:
+```
+$ bower install angular-loading-bar
+```
+#### via npm:
+```
+$ npm install angular-loading-bar
+```
+
+#### via CDN:
+```html
+
+
+```
+
+## Why I created this
+There are a couple projects similar to this out there, but none were ideal for me. All implementations I've seen require that you maintain state on behalf of the loading bar. In other words, you're setting the value of the loading/progress bar manually from potentially many different locations. This becomes complicated when you have a very large application with several services all making independent XHR requests. It becomes even more complicated if you want these services to be loosly coupled.
+
+Additionally, Angular was created as a highly testable framework, so it pains me to see Angular modules without tests. That is not the case here as this loading bar ships with 100% code coverage.
+
+
+**Goals for this project:**
+
+1. Make it automatic
+2. Unit tests, 100% coverage
+3. Must work well with ngAnimate
+4. Must be styled via external CSS (not inline)
+5. No jQuery dependencies
+
+
+## Configuration
+
+#### Turn the spinner on or off:
+The insertion of the spinner can be controlled through configuration. It's on by default, but if you'd like to turn it off, simply configure the service:
+
+```js
+angular.module('myApp', ['angular-loading-bar'])
+ .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
+ cfpLoadingBarProvider.includeSpinner = false;
+ }])
+```
+
+#### Turn the loading bar on or off:
+Like the spinner configuration above, the loading bar can also be turned off for cases where you only want the spinner:
+
+```js
+angular.module('myApp', ['angular-loading-bar'])
+ .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
+ cfpLoadingBarProvider.includeBar = false;
+ }])
+```
+
+#### Customize the template:
+If you'd like to replace the default HTML template you can configure it by providing inline HTML as a string:
+
+```js
+angular.module('myApp', ['angular-loading-bar'])
+ .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
+ cfpLoadingBarProvider.spinnerTemplate = '
Loading...
';
+ }])
+```
+
+#### Latency Threshold
+By default, the loading bar will only display after it has been waiting for a response for over 100ms. This helps keep things feeling snappy, and avoids the annoyingness of showing a loading bar every few seconds on really chatty applications. This threshold is totally configurable:
+
+```js
+angular.module('myApp', ['angular-loading-bar'])
+ .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
+ cfpLoadingBarProvider.latencyThreshold = 500;
+ }])
+```
+
+#### Ignoring particular XHR requests:
+The loading bar can also be forced to ignore certain requests, for example, when long-polling or periodically sending debugging information back to the server.
+
+```js
+// ignore a particular $http GET:
+$http.get('/status', {
+ ignoreLoadingBar: true
+});
+
+// ignore a particular $http POST. Note: POST and GET have different
+// method signatures:
+$http.post('/save', data, {
+ ignoreLoadingBar: true
+});
+
+```
+
+
+```js
+// ignore particular $resource requests:
+.factory('Restaurant', function($resource) {
+ return $resource('/api/restaurant/:id', {id: '@id'}, {
+ query: {
+ method: 'GET',
+ isArray: true,
+ ignoreLoadingBar: true
+ }
+ });
+});
+
+```
+
+
+
+
+## How it works:
+This library is split into two modules, an $http `interceptor`, and a `service`:
+
+**Interceptor**
+The interceptor simply listens for all outgoing XHR requests, and then instructs the loadingBar service to start, stop, and increment accordingly. There is no public API for the interceptor. It can be used stand-alone by including `cfp.loadingBarInterceptor` as a dependency for your module.
+
+**Service**
+The service is responsible for the presentation of the loading bar. It injects the loading bar into the DOM, adjusts the width whenever `set()` is called, and `complete()`s the whole show by removing the loading bar from the DOM.
+
+## Service API (advanced usage)
+Under normal circumstances you won't need to use this. However, if you wish to use the loading bar without the interceptor, you can do that as well. Simply include the loading bar service as a dependency instead of the main `angular-loading-bar` module:
+
+```js
+angular.module('myApp', ['cfp.loadingBar'])
+```
+
+
+```js
+
+cfpLoadingBar.start();
+// will insert the loading bar into the DOM, and display its progress at 1%.
+// It will automatically call `inc()` repeatedly to give the illusion that the page load is progressing.
+
+cfpLoadingBar.inc();
+// increments the loading bar by a random amount.
+// It is important to note that the auto incrementing will begin to slow down as
+// the progress increases. This is to prevent the loading bar from appearing
+// completed (or almost complete) before the XHR request has responded.
+
+cfpLoadingBar.set(0.3) // Set the loading bar to 30%
+cfpLoadingBar.status() // Returns the loading bar's progress.
+// -> 0.3
+
+cfpLoadingBar.complete()
+// Set the loading bar's progress to 100%, and then remove it from the DOM.
+
+```
+
+## Events
+The loading bar broadcasts the following events over $rootScope allowing further customization:
+
+**`cfpLoadingBar:loading`** triggered upon each XHR request that is not already cached
+
+**`cfpLoadingBar:loaded`** triggered each time an XHR request recieves a response (either successful or error)
+
+**`cfpLoadingBar:started`** triggered once upon the first XHR request. Will trigger again if another request goes out after `cfpLoadingBar:completed` has triggered.
+
+**`cfpLoadingBar:completed`** triggered once when the all XHR requests have returned (either successfully or not)
+
+## Credits:
+Credit goes to [rstacruz](https://github.com/rstacruz) for his excellent [nProgress](https://github.com/rstacruz/nprogress).
+
+## License:
+Licensed under the MIT license
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/bower.json
new file mode 100644
index 0000000000..f1d1f7a879
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "angular-loading-bar",
+ "main": [
+ "build/loading-bar.js",
+ "build/loading-bar.css"
+ ],
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "components",
+ "test",
+ "example"
+ ],
+ "dependencies": {
+ "angular": "^1.2.9"
+ },
+ "devDependencies": {
+ "angular": "~1.2.23",
+ "angular-1.3": "angular#1.3",
+ "angular-1.4": "angular#1.4",
+ "angular-mocks": "~1.2.9",
+ "angular-mocks-1.3": "angular-mocks#1.3",
+ "angular-mocks-1.4": "angular-mocks#1.4",
+ "angular-animate": "~1.2.9",
+ "angular-animate-1.3": "angular-animate#1.3",
+ "angular-animate-1.4": "angular-animate#1.4"
+ },
+ "resolutions": {
+ "angular": "~1.2.23"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.css b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.css
new file mode 100644
index 0000000000..72408d1a99
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.css
@@ -0,0 +1,110 @@
+/*!
+ * angular-loading-bar v0.9.0
+ * https://chieffancypants.github.io/angular-loading-bar
+ * Copyright (c) 2016 Wes Cruver
+ * License: MIT
+ */
+
+/* Make clicks pass-through */
+#loading-bar,
+#loading-bar-spinner {
+ pointer-events: none;
+ -webkit-pointer-events: none;
+ -webkit-transition: 350ms linear all;
+ -moz-transition: 350ms linear all;
+ -o-transition: 350ms linear all;
+ transition: 350ms linear all;
+}
+
+#loading-bar.ng-enter,
+#loading-bar.ng-leave.ng-leave-active,
+#loading-bar-spinner.ng-enter,
+#loading-bar-spinner.ng-leave.ng-leave-active {
+ opacity: 0;
+}
+
+#loading-bar.ng-enter.ng-enter-active,
+#loading-bar.ng-leave,
+#loading-bar-spinner.ng-enter.ng-enter-active,
+#loading-bar-spinner.ng-leave {
+ opacity: 1;
+}
+
+#loading-bar .bar {
+ -webkit-transition: width 350ms;
+ -moz-transition: width 350ms;
+ -o-transition: width 350ms;
+ transition: width 350ms;
+
+ background: #29d;
+ position: fixed;
+ z-index: 10002;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ border-bottom-right-radius: 1px;
+ border-top-right-radius: 1px;
+}
+
+/* Fancy blur effect */
+#loading-bar .peg {
+ position: absolute;
+ width: 70px;
+ right: 0;
+ top: 0;
+ height: 2px;
+ opacity: .45;
+ -moz-box-shadow: #29d 1px 0 6px 1px;
+ -ms-box-shadow: #29d 1px 0 6px 1px;
+ -webkit-box-shadow: #29d 1px 0 6px 1px;
+ box-shadow: #29d 1px 0 6px 1px;
+ -moz-border-radius: 100%;
+ -webkit-border-radius: 100%;
+ border-radius: 100%;
+}
+
+#loading-bar-spinner {
+ display: block;
+ position: fixed;
+ z-index: 10002;
+ top: 10px;
+ left: 10px;
+}
+
+#loading-bar-spinner .spinner-icon {
+ width: 14px;
+ height: 14px;
+
+ border: solid 2px transparent;
+ border-top-color: #29d;
+ border-left-color: #29d;
+ border-radius: 50%;
+
+ -webkit-animation: loading-bar-spinner 400ms linear infinite;
+ -moz-animation: loading-bar-spinner 400ms linear infinite;
+ -ms-animation: loading-bar-spinner 400ms linear infinite;
+ -o-animation: loading-bar-spinner 400ms linear infinite;
+ animation: loading-bar-spinner 400ms linear infinite;
+}
+
+@-webkit-keyframes loading-bar-spinner {
+ 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@-moz-keyframes loading-bar-spinner {
+ 0% { -moz-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -moz-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@-o-keyframes loading-bar-spinner {
+ 0% { -o-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -o-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@-ms-keyframes loading-bar-spinner {
+ 0% { -ms-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@keyframes loading-bar-spinner {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.js
new file mode 100644
index 0000000000..358a368714
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.js
@@ -0,0 +1,341 @@
+/*!
+ * angular-loading-bar v0.9.0
+ * https://chieffancypants.github.io/angular-loading-bar
+ * Copyright (c) 2016 Wes Cruver
+ * License: MIT
+ */
+/*
+ * angular-loading-bar
+ *
+ * intercepts XHR requests and creates a loading bar.
+ * Based on the excellent nprogress work by rstacruz (more info in readme)
+ *
+ * (c) 2013 Wes Cruver
+ * License: MIT
+ */
+
+
+(function() {
+
+'use strict';
+
+// Alias the loading bar for various backwards compatibilities since the project has matured:
+angular.module('angular-loading-bar', ['cfp.loadingBarInterceptor']);
+angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']);
+
+
+/**
+ * loadingBarInterceptor service
+ *
+ * Registers itself as an Angular interceptor and listens for XHR requests.
+ */
+angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar'])
+ .config(['$httpProvider', function ($httpProvider) {
+
+ var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, $log, cfpLoadingBar) {
+
+ /**
+ * The total number of requests made
+ */
+ var reqsTotal = 0;
+
+ /**
+ * The number of requests completed (either successfully or not)
+ */
+ var reqsCompleted = 0;
+
+ /**
+ * The amount of time spent fetching before showing the loading bar
+ */
+ var latencyThreshold = cfpLoadingBar.latencyThreshold;
+
+ /**
+ * $timeout handle for latencyThreshold
+ */
+ var startTimeout;
+
+
+ /**
+ * calls cfpLoadingBar.complete() which removes the
+ * loading bar from the DOM.
+ */
+ function setComplete() {
+ $timeout.cancel(startTimeout);
+ cfpLoadingBar.complete();
+ reqsCompleted = 0;
+ reqsTotal = 0;
+ }
+
+ /**
+ * Determine if the response has already been cached
+ * @param {Object} config the config option from the request
+ * @return {Boolean} retrns true if cached, otherwise false
+ */
+ function isCached(config) {
+ var cache;
+ var defaultCache = $cacheFactory.get('$http');
+ var defaults = $httpProvider.defaults;
+
+ // Choose the proper cache source. Borrowed from angular: $http service
+ if ((config.cache || defaults.cache) && config.cache !== false &&
+ (config.method === 'GET' || config.method === 'JSONP')) {
+ cache = angular.isObject(config.cache) ? config.cache
+ : angular.isObject(defaults.cache) ? defaults.cache
+ : defaultCache;
+ }
+
+ var cached = cache !== undefined ?
+ cache.get(config.url) !== undefined : false;
+
+ if (config.cached !== undefined && cached !== config.cached) {
+ return config.cached;
+ }
+ config.cached = cached;
+ return cached;
+ }
+
+
+ return {
+ 'request': function(config) {
+ // Check to make sure this request hasn't already been cached and that
+ // the requester didn't explicitly ask us to ignore this request:
+ if (!config.ignoreLoadingBar && !isCached(config)) {
+ $rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url});
+ if (reqsTotal === 0) {
+ startTimeout = $timeout(function() {
+ cfpLoadingBar.start();
+ }, latencyThreshold);
+ }
+ reqsTotal++;
+ cfpLoadingBar.set(reqsCompleted / reqsTotal);
+ }
+ return config;
+ },
+
+ 'response': function(response) {
+ if (!response || !response.config) {
+ $log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
+ return response;
+ }
+
+ if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
+ reqsCompleted++;
+ $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
+ if (reqsCompleted >= reqsTotal) {
+ setComplete();
+ } else {
+ cfpLoadingBar.set(reqsCompleted / reqsTotal);
+ }
+ }
+ return response;
+ },
+
+ 'responseError': function(rejection) {
+ if (!rejection || !rejection.config) {
+ $log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
+ return $q.reject(rejection);
+ }
+
+ if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
+ reqsCompleted++;
+ $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
+ if (reqsCompleted >= reqsTotal) {
+ setComplete();
+ } else {
+ cfpLoadingBar.set(reqsCompleted / reqsTotal);
+ }
+ }
+ return $q.reject(rejection);
+ }
+ };
+ }];
+
+ $httpProvider.interceptors.push(interceptor);
+ }]);
+
+
+/**
+ * Loading Bar
+ *
+ * This service handles adding and removing the actual element in the DOM.
+ * Generally, best practices for DOM manipulation is to take place in a
+ * directive, but because the element itself is injected in the DOM only upon
+ * XHR requests, and it's likely needed on every view, the best option is to
+ * use a service.
+ */
+angular.module('cfp.loadingBar', [])
+ .provider('cfpLoadingBar', function() {
+
+ this.autoIncrement = true;
+ this.includeSpinner = true;
+ this.includeBar = true;
+ this.latencyThreshold = 100;
+ this.startSize = 0.02;
+ this.parentSelector = 'body';
+ this.spinnerTemplate = '
';
+ this.loadingBarTemplate = '
';
+
+ this.$get = ['$injector', '$document', '$timeout', '$rootScope', function ($injector, $document, $timeout, $rootScope) {
+ var $animate;
+ var $parentSelector = this.parentSelector,
+ loadingBarContainer = angular.element(this.loadingBarTemplate),
+ loadingBar = loadingBarContainer.find('div').eq(0),
+ spinner = angular.element(this.spinnerTemplate);
+
+ var incTimeout,
+ completeTimeout,
+ started = false,
+ status = 0;
+
+ var autoIncrement = this.autoIncrement;
+ var includeSpinner = this.includeSpinner;
+ var includeBar = this.includeBar;
+ var startSize = this.startSize;
+
+ /**
+ * Inserts the loading bar element into the dom, and sets it to 2%
+ */
+ function _start() {
+ if (!$animate) {
+ $animate = $injector.get('$animate');
+ }
+
+ $timeout.cancel(completeTimeout);
+
+ // do not continually broadcast the started event:
+ if (started) {
+ return;
+ }
+
+ var document = $document[0];
+ var parent = document.querySelector ?
+ document.querySelector($parentSelector)
+ : $document.find($parentSelector)[0]
+ ;
+
+ if (! parent) {
+ parent = document.getElementsByTagName('body')[0];
+ }
+
+ var $parent = angular.element(parent);
+ var $after = parent.lastChild && angular.element(parent.lastChild);
+
+ $rootScope.$broadcast('cfpLoadingBar:started');
+ started = true;
+
+ if (includeBar) {
+ $animate.enter(loadingBarContainer, $parent, $after);
+ }
+
+ if (includeSpinner) {
+ $animate.enter(spinner, $parent, loadingBarContainer);
+ }
+
+ _set(startSize);
+ }
+
+ /**
+ * Set the loading bar's width to a certain percent.
+ *
+ * @param n any value between 0 and 1
+ */
+ function _set(n) {
+ if (!started) {
+ return;
+ }
+ var pct = (n * 100) + '%';
+ loadingBar.css('width', pct);
+ status = n;
+
+ // increment loadingbar to give the illusion that there is always
+ // progress but make sure to cancel the previous timeouts so we don't
+ // have multiple incs running at the same time.
+ if (autoIncrement) {
+ $timeout.cancel(incTimeout);
+ incTimeout = $timeout(function() {
+ _inc();
+ }, 250);
+ }
+ }
+
+ /**
+ * Increments the loading bar by a random amount
+ * but slows down as it progresses
+ */
+ function _inc() {
+ if (_status() >= 1) {
+ return;
+ }
+
+ var rnd = 0;
+
+ // TODO: do this mathmatically instead of through conditions
+
+ var stat = _status();
+ if (stat >= 0 && stat < 0.25) {
+ // Start out between 3 - 6% increments
+ rnd = (Math.random() * (5 - 3 + 1) + 3) / 100;
+ } else if (stat >= 0.25 && stat < 0.65) {
+ // increment between 0 - 3%
+ rnd = (Math.random() * 3) / 100;
+ } else if (stat >= 0.65 && stat < 0.9) {
+ // increment between 0 - 2%
+ rnd = (Math.random() * 2) / 100;
+ } else if (stat >= 0.9 && stat < 0.99) {
+ // finally, increment it .5 %
+ rnd = 0.005;
+ } else {
+ // after 99%, don't increment:
+ rnd = 0;
+ }
+
+ var pct = _status() + rnd;
+ _set(pct);
+ }
+
+ function _status() {
+ return status;
+ }
+
+ function _completeAnimation() {
+ status = 0;
+ started = false;
+ }
+
+ function _complete() {
+ if (!$animate) {
+ $animate = $injector.get('$animate');
+ }
+
+ $rootScope.$broadcast('cfpLoadingBar:completed');
+ _set(1);
+
+ $timeout.cancel(completeTimeout);
+
+ // Attempt to aggregate any start/complete calls within 500ms:
+ completeTimeout = $timeout(function() {
+ var promise = $animate.leave(loadingBarContainer, _completeAnimation);
+ if (promise && promise.then) {
+ promise.then(_completeAnimation);
+ }
+ $animate.leave(spinner);
+ }, 500);
+ }
+
+ return {
+ start : _start,
+ set : _set,
+ status : _status,
+ inc : _inc,
+ complete : _complete,
+ autoIncrement : this.autoIncrement,
+ includeSpinner : this.includeSpinner,
+ latencyThreshold : this.latencyThreshold,
+ parentSelector : this.parentSelector,
+ startSize : this.startSize
+ };
+
+
+ }]; //
+ }); // wtf javascript. srsly
+})(); //
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.min.css b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.min.css
new file mode 100644
index 0000000000..0f9f106777
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.min.css
@@ -0,0 +1 @@
+#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner 400ms linear infinite;-moz-animation:loading-bar-spinner 400ms linear infinite;-ms-animation:loading-bar-spinner 400ms linear infinite;-o-animation:loading-bar-spinner 400ms linear infinite;animation:loading-bar-spinner 400ms linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.min.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.min.js
new file mode 100644
index 0000000000..b6628f4ad0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/build/loading-bar.min.js
@@ -0,0 +1,7 @@
+/*!
+ * angular-loading-bar v0.9.0
+ * https://chieffancypants.github.io/angular-loading-bar
+ * Copyright (c) 2016 Wes Cruver
+ * License: MIT
+ */
+!function(){"use strict";angular.module("angular-loading-bar",["cfp.loadingBarInterceptor"]),angular.module("chieffancypants.loadingBar",["cfp.loadingBarInterceptor"]),angular.module("cfp.loadingBarInterceptor",["cfp.loadingBar"]).config(["$httpProvider",function(a){var b=["$q","$cacheFactory","$timeout","$rootScope","$log","cfpLoadingBar",function(b,c,d,e,f,g){function h(){d.cancel(j),g.complete(),l=0,k=0}function i(b){var d,e=c.get("$http"),f=a.defaults;!b.cache&&!f.cache||b.cache===!1||"GET"!==b.method&&"JSONP"!==b.method||(d=angular.isObject(b.cache)?b.cache:angular.isObject(f.cache)?f.cache:e);var g=void 0!==d?void 0!==d.get(b.url):!1;return void 0!==b.cached&&g!==b.cached?b.cached:(b.cached=g,g)}var j,k=0,l=0,m=g.latencyThreshold;return{request:function(a){return a.ignoreLoadingBar||i(a)||(e.$broadcast("cfpLoadingBar:loading",{url:a.url}),0===k&&(j=d(function(){g.start()},m)),k++,g.set(l/k)),a},response:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),a):(f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),a)},responseError:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),b.reject(a)):(f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),b.reject(a))}}}];a.interceptors.push(b)}]),angular.module("cfp.loadingBar",[]).provider("cfpLoadingBar",function(){this.autoIncrement=!0,this.includeSpinner=!0,this.includeBar=!0,this.latencyThreshold=100,this.startSize=.02,this.parentSelector="body",this.spinnerTemplate='
',this.loadingBarTemplate='
',this.$get=["$injector","$document","$timeout","$rootScope",function(a,b,c,d){function e(){if(k||(k=a.get("$animate")),c.cancel(m),!r){var e=b[0],g=e.querySelector?e.querySelector(n):b.find(n)[0];g||(g=e.getElementsByTagName("body")[0]);var h=angular.element(g),i=g.lastChild&&angular.element(g.lastChild);d.$broadcast("cfpLoadingBar:started"),r=!0,v&&k.enter(o,h,i),u&&k.enter(q,h,o),f(w)}}function f(a){if(r){var b=100*a+"%";p.css("width",b),s=a,t&&(c.cancel(l),l=c(function(){g()},250))}}function g(){if(!(h()>=1)){var a=0,b=h();a=b>=0&&.25>b?(3*Math.random()+3)/100:b>=.25&&.65>b?3*Math.random()/100:b>=.65&&.9>b?2*Math.random()/100:b>=.9&&.99>b?.005:0;var c=h()+a;f(c)}}function h(){return s}function i(){s=0,r=!1}function j(){k||(k=a.get("$animate")),d.$broadcast("cfpLoadingBar:completed"),f(1),c.cancel(m),m=c(function(){var a=k.leave(o,i);a&&a.then&&a.then(i),k.leave(q)},500)}var k,l,m,n=this.parentSelector,o=angular.element(this.loadingBarTemplate),p=o.find("div").eq(0),q=angular.element(this.spinnerTemplate),r=!1,s=0,t=this.autoIncrement,u=this.includeSpinner,v=this.includeBar,w=this.startSize;return{start:e,set:f,status:h,inc:g,complete:j,autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}();
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/index.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/index.js
new file mode 100644
index 0000000000..411428d860
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/index.js
@@ -0,0 +1,2 @@
+require('./build/loading-bar');
+module.exports = 'angular-loading-bar';
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/package.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/package.json
new file mode 100644
index 0000000000..5f44266c75
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "angular-loading-bar",
+ "version": "0.9.0",
+ "description": "An automatic loading bar for AngularJS",
+ "main": "index.js",
+ "style": "build/loading-bar.css",
+ "directories": {
+ "example": "example",
+ "test": "test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/chieffancypants/angular-loading-bar.git"
+ },
+ "keywords": [
+ "angular",
+ "angularjs",
+ "loading",
+ "loadingbar",
+ "progress",
+ "progressbar"
+ ],
+ "author": "Wes Cruver",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/chieffancypants/angular-loading-bar/issues"
+ },
+ "homepage": "https://chieffancypants.github.io/angular-loading-bar",
+ "devDependencies": {
+ "karma-jasmine": "^0.1.3",
+ "karma-coffee-preprocessor": "^0.2.0",
+ "karma-phantomjs-launcher": "^0.1.0",
+ "karma": "~0.12.0",
+ "karma-coverage": "^0.1.0",
+ "grunt": "~0.4.1",
+ "grunt-contrib-jshint": "~0.6.4",
+ "grunt-contrib-uglify": "^0.9.1",
+ "grunt-contrib-cssmin": "~0.12.0",
+ "grunt-karma": "~0.11.0",
+ "grunt-contrib-concat": "^0.5.0"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/src/loading-bar.css b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/src/loading-bar.css
new file mode 100644
index 0000000000..3ee06188b7
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/src/loading-bar.css
@@ -0,0 +1,104 @@
+
+/* Make clicks pass-through */
+#loading-bar,
+#loading-bar-spinner {
+ pointer-events: none;
+ -webkit-pointer-events: none;
+ -webkit-transition: 350ms linear all;
+ -moz-transition: 350ms linear all;
+ -o-transition: 350ms linear all;
+ transition: 350ms linear all;
+}
+
+#loading-bar.ng-enter,
+#loading-bar.ng-leave.ng-leave-active,
+#loading-bar-spinner.ng-enter,
+#loading-bar-spinner.ng-leave.ng-leave-active {
+ opacity: 0;
+}
+
+#loading-bar.ng-enter.ng-enter-active,
+#loading-bar.ng-leave,
+#loading-bar-spinner.ng-enter.ng-enter-active,
+#loading-bar-spinner.ng-leave {
+ opacity: 1;
+}
+
+#loading-bar .bar {
+ -webkit-transition: width 350ms;
+ -moz-transition: width 350ms;
+ -o-transition: width 350ms;
+ transition: width 350ms;
+
+ background: #29d;
+ position: fixed;
+ z-index: 10002;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ border-bottom-right-radius: 1px;
+ border-top-right-radius: 1px;
+}
+
+/* Fancy blur effect */
+#loading-bar .peg {
+ position: absolute;
+ width: 70px;
+ right: 0;
+ top: 0;
+ height: 2px;
+ opacity: .45;
+ -moz-box-shadow: #29d 1px 0 6px 1px;
+ -ms-box-shadow: #29d 1px 0 6px 1px;
+ -webkit-box-shadow: #29d 1px 0 6px 1px;
+ box-shadow: #29d 1px 0 6px 1px;
+ -moz-border-radius: 100%;
+ -webkit-border-radius: 100%;
+ border-radius: 100%;
+}
+
+#loading-bar-spinner {
+ display: block;
+ position: fixed;
+ z-index: 10002;
+ top: 10px;
+ left: 10px;
+}
+
+#loading-bar-spinner .spinner-icon {
+ width: 14px;
+ height: 14px;
+
+ border: solid 2px transparent;
+ border-top-color: #29d;
+ border-left-color: #29d;
+ border-radius: 50%;
+
+ -webkit-animation: loading-bar-spinner 400ms linear infinite;
+ -moz-animation: loading-bar-spinner 400ms linear infinite;
+ -ms-animation: loading-bar-spinner 400ms linear infinite;
+ -o-animation: loading-bar-spinner 400ms linear infinite;
+ animation: loading-bar-spinner 400ms linear infinite;
+}
+
+@-webkit-keyframes loading-bar-spinner {
+ 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@-moz-keyframes loading-bar-spinner {
+ 0% { -moz-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -moz-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@-o-keyframes loading-bar-spinner {
+ 0% { -o-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -o-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@-ms-keyframes loading-bar-spinner {
+ 0% { -ms-transform: rotate(0deg); transform: rotate(0deg); }
+ 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); }
+}
+@keyframes loading-bar-spinner {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/src/loading-bar.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/src/loading-bar.js
new file mode 100644
index 0000000000..dc3fc73ad1
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-loading-bar/src/loading-bar.js
@@ -0,0 +1,335 @@
+/*
+ * angular-loading-bar
+ *
+ * intercepts XHR requests and creates a loading bar.
+ * Based on the excellent nprogress work by rstacruz (more info in readme)
+ *
+ * (c) 2013 Wes Cruver
+ * License: MIT
+ */
+
+
+(function() {
+
+'use strict';
+
+// Alias the loading bar for various backwards compatibilities since the project has matured:
+angular.module('angular-loading-bar', ['cfp.loadingBarInterceptor']);
+angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']);
+
+
+/**
+ * loadingBarInterceptor service
+ *
+ * Registers itself as an Angular interceptor and listens for XHR requests.
+ */
+angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar'])
+ .config(['$httpProvider', function ($httpProvider) {
+
+ var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, $log, cfpLoadingBar) {
+
+ /**
+ * The total number of requests made
+ */
+ var reqsTotal = 0;
+
+ /**
+ * The number of requests completed (either successfully or not)
+ */
+ var reqsCompleted = 0;
+
+ /**
+ * The amount of time spent fetching before showing the loading bar
+ */
+ var latencyThreshold = cfpLoadingBar.latencyThreshold;
+
+ /**
+ * $timeout handle for latencyThreshold
+ */
+ var startTimeout;
+
+
+ /**
+ * calls cfpLoadingBar.complete() which removes the
+ * loading bar from the DOM.
+ */
+ function setComplete() {
+ $timeout.cancel(startTimeout);
+ cfpLoadingBar.complete();
+ reqsCompleted = 0;
+ reqsTotal = 0;
+ }
+
+ /**
+ * Determine if the response has already been cached
+ * @param {Object} config the config option from the request
+ * @return {Boolean} retrns true if cached, otherwise false
+ */
+ function isCached(config) {
+ var cache;
+ var defaultCache = $cacheFactory.get('$http');
+ var defaults = $httpProvider.defaults;
+
+ // Choose the proper cache source. Borrowed from angular: $http service
+ if ((config.cache || defaults.cache) && config.cache !== false &&
+ (config.method === 'GET' || config.method === 'JSONP')) {
+ cache = angular.isObject(config.cache) ? config.cache
+ : angular.isObject(defaults.cache) ? defaults.cache
+ : defaultCache;
+ }
+
+ var cached = cache !== undefined ?
+ cache.get(config.url) !== undefined : false;
+
+ if (config.cached !== undefined && cached !== config.cached) {
+ return config.cached;
+ }
+ config.cached = cached;
+ return cached;
+ }
+
+
+ return {
+ 'request': function(config) {
+ // Check to make sure this request hasn't already been cached and that
+ // the requester didn't explicitly ask us to ignore this request:
+ if (!config.ignoreLoadingBar && !isCached(config)) {
+ $rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url});
+ if (reqsTotal === 0) {
+ startTimeout = $timeout(function() {
+ cfpLoadingBar.start();
+ }, latencyThreshold);
+ }
+ reqsTotal++;
+ cfpLoadingBar.set(reqsCompleted / reqsTotal);
+ }
+ return config;
+ },
+
+ 'response': function(response) {
+ if (!response || !response.config) {
+ $log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
+ return response;
+ }
+
+ if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
+ reqsCompleted++;
+ $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
+ if (reqsCompleted >= reqsTotal) {
+ setComplete();
+ } else {
+ cfpLoadingBar.set(reqsCompleted / reqsTotal);
+ }
+ }
+ return response;
+ },
+
+ 'responseError': function(rejection) {
+ if (!rejection || !rejection.config) {
+ $log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
+ return $q.reject(rejection);
+ }
+
+ if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
+ reqsCompleted++;
+ $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
+ if (reqsCompleted >= reqsTotal) {
+ setComplete();
+ } else {
+ cfpLoadingBar.set(reqsCompleted / reqsTotal);
+ }
+ }
+ return $q.reject(rejection);
+ }
+ };
+ }];
+
+ $httpProvider.interceptors.push(interceptor);
+ }]);
+
+
+/**
+ * Loading Bar
+ *
+ * This service handles adding and removing the actual element in the DOM.
+ * Generally, best practices for DOM manipulation is to take place in a
+ * directive, but because the element itself is injected in the DOM only upon
+ * XHR requests, and it's likely needed on every view, the best option is to
+ * use a service.
+ */
+angular.module('cfp.loadingBar', [])
+ .provider('cfpLoadingBar', function() {
+
+ this.autoIncrement = true;
+ this.includeSpinner = true;
+ this.includeBar = true;
+ this.latencyThreshold = 100;
+ this.startSize = 0.02;
+ this.parentSelector = 'body';
+ this.spinnerTemplate = '
';
+ this.loadingBarTemplate = '
';
+
+ this.$get = ['$injector', '$document', '$timeout', '$rootScope', function ($injector, $document, $timeout, $rootScope) {
+ var $animate;
+ var $parentSelector = this.parentSelector,
+ loadingBarContainer = angular.element(this.loadingBarTemplate),
+ loadingBar = loadingBarContainer.find('div').eq(0),
+ spinner = angular.element(this.spinnerTemplate);
+
+ var incTimeout,
+ completeTimeout,
+ started = false,
+ status = 0;
+
+ var autoIncrement = this.autoIncrement;
+ var includeSpinner = this.includeSpinner;
+ var includeBar = this.includeBar;
+ var startSize = this.startSize;
+
+ /**
+ * Inserts the loading bar element into the dom, and sets it to 2%
+ */
+ function _start() {
+ if (!$animate) {
+ $animate = $injector.get('$animate');
+ }
+
+ $timeout.cancel(completeTimeout);
+
+ // do not continually broadcast the started event:
+ if (started) {
+ return;
+ }
+
+ var document = $document[0];
+ var parent = document.querySelector ?
+ document.querySelector($parentSelector)
+ : $document.find($parentSelector)[0]
+ ;
+
+ if (! parent) {
+ parent = document.getElementsByTagName('body')[0];
+ }
+
+ var $parent = angular.element(parent);
+ var $after = parent.lastChild && angular.element(parent.lastChild);
+
+ $rootScope.$broadcast('cfpLoadingBar:started');
+ started = true;
+
+ if (includeBar) {
+ $animate.enter(loadingBarContainer, $parent, $after);
+ }
+
+ if (includeSpinner) {
+ $animate.enter(spinner, $parent, loadingBarContainer);
+ }
+
+ _set(startSize);
+ }
+
+ /**
+ * Set the loading bar's width to a certain percent.
+ *
+ * @param n any value between 0 and 1
+ */
+ function _set(n) {
+ if (!started) {
+ return;
+ }
+ var pct = (n * 100) + '%';
+ loadingBar.css('width', pct);
+ status = n;
+
+ // increment loadingbar to give the illusion that there is always
+ // progress but make sure to cancel the previous timeouts so we don't
+ // have multiple incs running at the same time.
+ if (autoIncrement) {
+ $timeout.cancel(incTimeout);
+ incTimeout = $timeout(function() {
+ _inc();
+ }, 250);
+ }
+ }
+
+ /**
+ * Increments the loading bar by a random amount
+ * but slows down as it progresses
+ */
+ function _inc() {
+ if (_status() >= 1) {
+ return;
+ }
+
+ var rnd = 0;
+
+ // TODO: do this mathmatically instead of through conditions
+
+ var stat = _status();
+ if (stat >= 0 && stat < 0.25) {
+ // Start out between 3 - 6% increments
+ rnd = (Math.random() * (5 - 3 + 1) + 3) / 100;
+ } else if (stat >= 0.25 && stat < 0.65) {
+ // increment between 0 - 3%
+ rnd = (Math.random() * 3) / 100;
+ } else if (stat >= 0.65 && stat < 0.9) {
+ // increment between 0 - 2%
+ rnd = (Math.random() * 2) / 100;
+ } else if (stat >= 0.9 && stat < 0.99) {
+ // finally, increment it .5 %
+ rnd = 0.005;
+ } else {
+ // after 99%, don't increment:
+ rnd = 0;
+ }
+
+ var pct = _status() + rnd;
+ _set(pct);
+ }
+
+ function _status() {
+ return status;
+ }
+
+ function _completeAnimation() {
+ status = 0;
+ started = false;
+ }
+
+ function _complete() {
+ if (!$animate) {
+ $animate = $injector.get('$animate');
+ }
+
+ $rootScope.$broadcast('cfpLoadingBar:completed');
+ _set(1);
+
+ $timeout.cancel(completeTimeout);
+
+ // Attempt to aggregate any start/complete calls within 500ms:
+ completeTimeout = $timeout(function() {
+ var promise = $animate.leave(loadingBarContainer, _completeAnimation);
+ if (promise && promise.then) {
+ promise.then(_completeAnimation);
+ }
+ $animate.leave(spinner);
+ }, 500);
+ }
+
+ return {
+ start : _start,
+ set : _set,
+ status : _status,
+ inc : _inc,
+ complete : _complete,
+ autoIncrement : this.autoIncrement,
+ includeSpinner : this.includeSpinner,
+ latencyThreshold : this.latencyThreshold,
+ parentSelector : this.parentSelector,
+ startSize : this.startSize
+ };
+
+
+ }]; //
+ }); // wtf javascript. srsly
+})(); //
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/.bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/.bower.json
new file mode 100644
index 0000000000..119ddaab59
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/.bower.json
@@ -0,0 +1,20 @@
+{
+ "name": "angular-mocks",
+ "version": "1.5.8",
+ "license": "MIT",
+ "main": "./angular-mocks.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.5.8"
+ },
+ "homepage": "https://github.com/angular/bower-angular-mocks",
+ "_release": "1.5.8",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.8",
+ "commit": "482eefcf6b03057c5fcddb9750e460f458ee3487"
+ },
+ "_source": "https://github.com/angular/bower-angular-mocks.git",
+ "_target": "1.5.8",
+ "_originalSource": "angular-mocks"
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/LICENSE.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/LICENSE.md
new file mode 100644
index 0000000000..2c395eef1b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Angular
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/README.md
new file mode 100644
index 0000000000..61b9d8c083
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/README.md
@@ -0,0 +1,63 @@
+# packaged angular-mocks
+
+This repo is for distribution on `npm` and `bower`. The source for this module is in the
+[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngMock).
+Please file issues and pull requests against that repo.
+
+## Install
+
+You can install this package either with `npm` or with `bower`.
+
+### npm
+
+```shell
+npm install angular-mocks
+```
+
+You can `require` ngMock modules:
+
+```js
+var angular = require('angular');
+angular.module('myMod', [
+ require('angular-animate'),
+ require('angular-mocks/ngMock'),
+ require('angular-mocks/ngAnimateMock')
+]);
+```
+
+### bower
+
+```shell
+bower install angular-mocks
+```
+
+The mocks are then available at `bower_components/angular-mocks/angular-mocks.js`.
+
+## Documentation
+
+Documentation is available on the
+[AngularJS docs site](https://docs.angularjs.org/guide/unit-testing).
+
+## License
+
+The MIT License
+
+Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/angular-mocks.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/angular-mocks.js
new file mode 100644
index 0000000000..42f19b7aea
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/angular-mocks.js
@@ -0,0 +1,3126 @@
+/**
+ * @license AngularJS v1.5.8
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular) {
+
+'use strict';
+
+/**
+ * @ngdoc object
+ * @name angular.mock
+ * @description
+ *
+ * Namespace from 'angular-mocks.js' which contains testing related code.
+ *
+ */
+angular.mock = {};
+
+/**
+ * ! This is a private undocumented service !
+ *
+ * @name $browser
+ *
+ * @description
+ * This service is a mock implementation of {@link ng.$browser}. It provides fake
+ * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
+ * cookies, etc.
+ *
+ * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
+ * that there are several helper methods available which can be used in tests.
+ */
+angular.mock.$BrowserProvider = function() {
+ this.$get = function() {
+ return new angular.mock.$Browser();
+ };
+};
+
+angular.mock.$Browser = function() {
+ var self = this;
+
+ this.isMock = true;
+ self.$$url = "http://server/";
+ self.$$lastUrl = self.$$url; // used by url polling fn
+ self.pollFns = [];
+
+ // TODO(vojta): remove this temporary api
+ self.$$completeOutstandingRequest = angular.noop;
+ self.$$incOutstandingRequestCount = angular.noop;
+
+
+ // register url polling fn
+
+ self.onUrlChange = function(listener) {
+ self.pollFns.push(
+ function() {
+ if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) {
+ self.$$lastUrl = self.$$url;
+ self.$$lastState = self.$$state;
+ listener(self.$$url, self.$$state);
+ }
+ }
+ );
+
+ return listener;
+ };
+
+ self.$$applicationDestroyed = angular.noop;
+ self.$$checkUrlChange = angular.noop;
+
+ self.deferredFns = [];
+ self.deferredNextId = 0;
+
+ self.defer = function(fn, delay) {
+ delay = delay || 0;
+ self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
+ self.deferredFns.sort(function(a, b) { return a.time - b.time;});
+ return self.deferredNextId++;
+ };
+
+
+ /**
+ * @name $browser#defer.now
+ *
+ * @description
+ * Current milliseconds mock time.
+ */
+ self.defer.now = 0;
+
+
+ self.defer.cancel = function(deferId) {
+ var fnIndex;
+
+ angular.forEach(self.deferredFns, function(fn, index) {
+ if (fn.id === deferId) fnIndex = index;
+ });
+
+ if (angular.isDefined(fnIndex)) {
+ self.deferredFns.splice(fnIndex, 1);
+ return true;
+ }
+
+ return false;
+ };
+
+
+ /**
+ * @name $browser#defer.flush
+ *
+ * @description
+ * Flushes all pending requests and executes the defer callbacks.
+ *
+ * @param {number=} number of milliseconds to flush. See {@link #defer.now}
+ */
+ self.defer.flush = function(delay) {
+ var nextTime;
+
+ if (angular.isDefined(delay)) {
+ // A delay was passed so compute the next time
+ nextTime = self.defer.now + delay;
+ } else {
+ if (self.deferredFns.length) {
+ // No delay was passed so set the next time so that it clears the deferred queue
+ nextTime = self.deferredFns[self.deferredFns.length - 1].time;
+ } else {
+ // No delay passed, but there are no deferred tasks so flush - indicates an error!
+ throw new Error('No deferred tasks to be flushed');
+ }
+ }
+
+ while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
+ // Increment the time and call the next deferred function
+ self.defer.now = self.deferredFns[0].time;
+ self.deferredFns.shift().fn();
+ }
+
+ // Ensure that the current time is correct
+ self.defer.now = nextTime;
+ };
+
+ self.$$baseHref = '/';
+ self.baseHref = function() {
+ return this.$$baseHref;
+ };
+};
+angular.mock.$Browser.prototype = {
+
+ /**
+ * @name $browser#poll
+ *
+ * @description
+ * run all fns in pollFns
+ */
+ poll: function poll() {
+ angular.forEach(this.pollFns, function(pollFn) {
+ pollFn();
+ });
+ },
+
+ url: function(url, replace, state) {
+ if (angular.isUndefined(state)) {
+ state = null;
+ }
+ if (url) {
+ this.$$url = url;
+ // Native pushState serializes & copies the object; simulate it.
+ this.$$state = angular.copy(state);
+ return this;
+ }
+
+ return this.$$url;
+ },
+
+ state: function() {
+ return this.$$state;
+ },
+
+ notifyWhenNoOutstandingRequests: function(fn) {
+ fn();
+ }
+};
+
+
+/**
+ * @ngdoc provider
+ * @name $exceptionHandlerProvider
+ *
+ * @description
+ * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
+ * passed to the `$exceptionHandler`.
+ */
+
+/**
+ * @ngdoc service
+ * @name $exceptionHandler
+ *
+ * @description
+ * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
+ * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
+ * information.
+ *
+ *
+ * ```js
+ * describe('$exceptionHandlerProvider', function() {
+ *
+ * it('should capture log messages and exceptions', function() {
+ *
+ * module(function($exceptionHandlerProvider) {
+ * $exceptionHandlerProvider.mode('log');
+ * });
+ *
+ * inject(function($log, $exceptionHandler, $timeout) {
+ * $timeout(function() { $log.log(1); });
+ * $timeout(function() { $log.log(2); throw 'banana peel'; });
+ * $timeout(function() { $log.log(3); });
+ * expect($exceptionHandler.errors).toEqual([]);
+ * expect($log.assertEmpty());
+ * $timeout.flush();
+ * expect($exceptionHandler.errors).toEqual(['banana peel']);
+ * expect($log.log.logs).toEqual([[1], [2], [3]]);
+ * });
+ * });
+ * });
+ * ```
+ */
+
+angular.mock.$ExceptionHandlerProvider = function() {
+ var handler;
+
+ /**
+ * @ngdoc method
+ * @name $exceptionHandlerProvider#mode
+ *
+ * @description
+ * Sets the logging mode.
+ *
+ * @param {string} mode Mode of operation, defaults to `rethrow`.
+ *
+ * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
+ * mode stores an array of errors in `$exceptionHandler.errors`, to allow later assertion of
+ * them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
+ * {@link ngMock.$log#reset reset()}.
+ * - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
+ * is a bug in the application or test, so this mock will make these tests fail. For any
+ * implementations that expect exceptions to be thrown, the `rethrow` mode will also maintain
+ * a log of thrown errors in `$exceptionHandler.errors`.
+ */
+ this.mode = function(mode) {
+
+ switch (mode) {
+ case 'log':
+ case 'rethrow':
+ var errors = [];
+ handler = function(e) {
+ if (arguments.length == 1) {
+ errors.push(e);
+ } else {
+ errors.push([].slice.call(arguments, 0));
+ }
+ if (mode === "rethrow") {
+ throw e;
+ }
+ };
+ handler.errors = errors;
+ break;
+ default:
+ throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
+ }
+ };
+
+ this.$get = function() {
+ return handler;
+ };
+
+ this.mode('rethrow');
+};
+
+
+/**
+ * @ngdoc service
+ * @name $log
+ *
+ * @description
+ * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
+ * (one array per logging level). These arrays are exposed as `logs` property of each of the
+ * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
+ *
+ */
+angular.mock.$LogProvider = function() {
+ var debug = true;
+
+ function concat(array1, array2, index) {
+ return array1.concat(Array.prototype.slice.call(array2, index));
+ }
+
+ this.debugEnabled = function(flag) {
+ if (angular.isDefined(flag)) {
+ debug = flag;
+ return this;
+ } else {
+ return debug;
+ }
+ };
+
+ this.$get = function() {
+ var $log = {
+ log: function() { $log.log.logs.push(concat([], arguments, 0)); },
+ warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
+ info: function() { $log.info.logs.push(concat([], arguments, 0)); },
+ error: function() { $log.error.logs.push(concat([], arguments, 0)); },
+ debug: function() {
+ if (debug) {
+ $log.debug.logs.push(concat([], arguments, 0));
+ }
+ }
+ };
+
+ /**
+ * @ngdoc method
+ * @name $log#reset
+ *
+ * @description
+ * Reset all of the logging arrays to empty.
+ */
+ $log.reset = function() {
+ /**
+ * @ngdoc property
+ * @name $log#log.logs
+ *
+ * @description
+ * Array of messages logged using {@link ng.$log#log `log()`}.
+ *
+ * @example
+ * ```js
+ * $log.log('Some Log');
+ * var first = $log.log.logs.unshift();
+ * ```
+ */
+ $log.log.logs = [];
+ /**
+ * @ngdoc property
+ * @name $log#info.logs
+ *
+ * @description
+ * Array of messages logged using {@link ng.$log#info `info()`}.
+ *
+ * @example
+ * ```js
+ * $log.info('Some Info');
+ * var first = $log.info.logs.unshift();
+ * ```
+ */
+ $log.info.logs = [];
+ /**
+ * @ngdoc property
+ * @name $log#warn.logs
+ *
+ * @description
+ * Array of messages logged using {@link ng.$log#warn `warn()`}.
+ *
+ * @example
+ * ```js
+ * $log.warn('Some Warning');
+ * var first = $log.warn.logs.unshift();
+ * ```
+ */
+ $log.warn.logs = [];
+ /**
+ * @ngdoc property
+ * @name $log#error.logs
+ *
+ * @description
+ * Array of messages logged using {@link ng.$log#error `error()`}.
+ *
+ * @example
+ * ```js
+ * $log.error('Some Error');
+ * var first = $log.error.logs.unshift();
+ * ```
+ */
+ $log.error.logs = [];
+ /**
+ * @ngdoc property
+ * @name $log#debug.logs
+ *
+ * @description
+ * Array of messages logged using {@link ng.$log#debug `debug()`}.
+ *
+ * @example
+ * ```js
+ * $log.debug('Some Error');
+ * var first = $log.debug.logs.unshift();
+ * ```
+ */
+ $log.debug.logs = [];
+ };
+
+ /**
+ * @ngdoc method
+ * @name $log#assertEmpty
+ *
+ * @description
+ * Assert that all of the logging methods have no logged messages. If any messages are present,
+ * an exception is thrown.
+ */
+ $log.assertEmpty = function() {
+ var errors = [];
+ angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
+ angular.forEach($log[logLevel].logs, function(log) {
+ angular.forEach(log, function(logItem) {
+ errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
+ (logItem.stack || ''));
+ });
+ });
+ });
+ if (errors.length) {
+ errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
+ "an expected log message was not checked and removed:");
+ errors.push('');
+ throw new Error(errors.join('\n---------\n'));
+ }
+ };
+
+ $log.reset();
+ return $log;
+ };
+};
+
+
+/**
+ * @ngdoc service
+ * @name $interval
+ *
+ * @description
+ * Mock implementation of the $interval service.
+ *
+ * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
+ * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
+ * time.
+ *
+ * @param {function()} fn A function that should be called repeatedly.
+ * @param {number} delay Number of milliseconds between each function call.
+ * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
+ * indefinitely.
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
+ * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
+ * @param {...*=} Pass additional parameters to the executed function.
+ * @returns {promise} A promise which will be notified on each iteration.
+ */
+angular.mock.$IntervalProvider = function() {
+ this.$get = ['$browser', '$rootScope', '$q', '$$q',
+ function($browser, $rootScope, $q, $$q) {
+ var repeatFns = [],
+ nextRepeatId = 0,
+ now = 0;
+
+ var $interval = function(fn, delay, count, invokeApply) {
+ var hasParams = arguments.length > 4,
+ args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
+ iteration = 0,
+ skipApply = (angular.isDefined(invokeApply) && !invokeApply),
+ deferred = (skipApply ? $$q : $q).defer(),
+ promise = deferred.promise;
+
+ count = (angular.isDefined(count)) ? count : 0;
+ promise.then(null, null, (!hasParams) ? fn : function() {
+ fn.apply(null, args);
+ });
+
+ promise.$$intervalId = nextRepeatId;
+
+ function tick() {
+ deferred.notify(iteration++);
+
+ if (count > 0 && iteration >= count) {
+ var fnIndex;
+ deferred.resolve(iteration);
+
+ angular.forEach(repeatFns, function(fn, index) {
+ if (fn.id === promise.$$intervalId) fnIndex = index;
+ });
+
+ if (angular.isDefined(fnIndex)) {
+ repeatFns.splice(fnIndex, 1);
+ }
+ }
+
+ if (skipApply) {
+ $browser.defer.flush();
+ } else {
+ $rootScope.$apply();
+ }
+ }
+
+ repeatFns.push({
+ nextTime:(now + delay),
+ delay: delay,
+ fn: tick,
+ id: nextRepeatId,
+ deferred: deferred
+ });
+ repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
+
+ nextRepeatId++;
+ return promise;
+ };
+ /**
+ * @ngdoc method
+ * @name $interval#cancel
+ *
+ * @description
+ * Cancels a task associated with the `promise`.
+ *
+ * @param {promise} promise A promise from calling the `$interval` function.
+ * @returns {boolean} Returns `true` if the task was successfully cancelled.
+ */
+ $interval.cancel = function(promise) {
+ if (!promise) return false;
+ var fnIndex;
+
+ angular.forEach(repeatFns, function(fn, index) {
+ if (fn.id === promise.$$intervalId) fnIndex = index;
+ });
+
+ if (angular.isDefined(fnIndex)) {
+ repeatFns[fnIndex].deferred.reject('canceled');
+ repeatFns.splice(fnIndex, 1);
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * @ngdoc method
+ * @name $interval#flush
+ * @description
+ *
+ * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
+ *
+ * @param {number=} millis maximum timeout amount to flush up until.
+ *
+ * @return {number} The amount of time moved forward.
+ */
+ $interval.flush = function(millis) {
+ now += millis;
+ while (repeatFns.length && repeatFns[0].nextTime <= now) {
+ var task = repeatFns[0];
+ task.fn();
+ task.nextTime += task.delay;
+ repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
+ }
+ return millis;
+ };
+
+ return $interval;
+ }];
+};
+
+
+/* jshint -W101 */
+/* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
+ * This directive should go inside the anonymous function but a bug in JSHint means that it would
+ * not be enacted early enough to prevent the warning.
+ */
+var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
+
+function jsonStringToDate(string) {
+ var match;
+ if (match = string.match(R_ISO8061_STR)) {
+ var date = new Date(0),
+ tzHour = 0,
+ tzMin = 0;
+ if (match[9]) {
+ tzHour = toInt(match[9] + match[10]);
+ tzMin = toInt(match[9] + match[11]);
+ }
+ date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
+ date.setUTCHours(toInt(match[4] || 0) - tzHour,
+ toInt(match[5] || 0) - tzMin,
+ toInt(match[6] || 0),
+ toInt(match[7] || 0));
+ return date;
+ }
+ return string;
+}
+
+function toInt(str) {
+ return parseInt(str, 10);
+}
+
+function padNumberInMock(num, digits, trim) {
+ var neg = '';
+ if (num < 0) {
+ neg = '-';
+ num = -num;
+ }
+ num = '' + num;
+ while (num.length < digits) num = '0' + num;
+ if (trim) {
+ num = num.substr(num.length - digits);
+ }
+ return neg + num;
+}
+
+
+/**
+ * @ngdoc type
+ * @name angular.mock.TzDate
+ * @description
+ *
+ * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
+ *
+ * Mock of the Date type which has its timezone specified via constructor arg.
+ *
+ * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
+ * offset, so that we can test code that depends on local timezone settings without dependency on
+ * the time zone settings of the machine where the code is running.
+ *
+ * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
+ * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
+ *
+ * @example
+ * !!!! WARNING !!!!!
+ * This is not a complete Date object so only methods that were implemented can be called safely.
+ * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
+ *
+ * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
+ * incomplete we might be missing some non-standard methods. This can result in errors like:
+ * "Date.prototype.foo called on incompatible Object".
+ *
+ * ```js
+ * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
+ * newYearInBratislava.getTimezoneOffset() => -60;
+ * newYearInBratislava.getFullYear() => 2010;
+ * newYearInBratislava.getMonth() => 0;
+ * newYearInBratislava.getDate() => 1;
+ * newYearInBratislava.getHours() => 0;
+ * newYearInBratislava.getMinutes() => 0;
+ * newYearInBratislava.getSeconds() => 0;
+ * ```
+ *
+ */
+angular.mock.TzDate = function(offset, timestamp) {
+ var self = new Date(0);
+ if (angular.isString(timestamp)) {
+ var tsStr = timestamp;
+
+ self.origDate = jsonStringToDate(timestamp);
+
+ timestamp = self.origDate.getTime();
+ if (isNaN(timestamp)) {
+ throw {
+ name: "Illegal Argument",
+ message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
+ };
+ }
+ } else {
+ self.origDate = new Date(timestamp);
+ }
+
+ var localOffset = new Date(timestamp).getTimezoneOffset();
+ self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
+ self.date = new Date(timestamp + self.offsetDiff);
+
+ self.getTime = function() {
+ return self.date.getTime() - self.offsetDiff;
+ };
+
+ self.toLocaleDateString = function() {
+ return self.date.toLocaleDateString();
+ };
+
+ self.getFullYear = function() {
+ return self.date.getFullYear();
+ };
+
+ self.getMonth = function() {
+ return self.date.getMonth();
+ };
+
+ self.getDate = function() {
+ return self.date.getDate();
+ };
+
+ self.getHours = function() {
+ return self.date.getHours();
+ };
+
+ self.getMinutes = function() {
+ return self.date.getMinutes();
+ };
+
+ self.getSeconds = function() {
+ return self.date.getSeconds();
+ };
+
+ self.getMilliseconds = function() {
+ return self.date.getMilliseconds();
+ };
+
+ self.getTimezoneOffset = function() {
+ return offset * 60;
+ };
+
+ self.getUTCFullYear = function() {
+ return self.origDate.getUTCFullYear();
+ };
+
+ self.getUTCMonth = function() {
+ return self.origDate.getUTCMonth();
+ };
+
+ self.getUTCDate = function() {
+ return self.origDate.getUTCDate();
+ };
+
+ self.getUTCHours = function() {
+ return self.origDate.getUTCHours();
+ };
+
+ self.getUTCMinutes = function() {
+ return self.origDate.getUTCMinutes();
+ };
+
+ self.getUTCSeconds = function() {
+ return self.origDate.getUTCSeconds();
+ };
+
+ self.getUTCMilliseconds = function() {
+ return self.origDate.getUTCMilliseconds();
+ };
+
+ self.getDay = function() {
+ return self.date.getDay();
+ };
+
+ // provide this method only on browsers that already have it
+ if (self.toISOString) {
+ self.toISOString = function() {
+ return padNumberInMock(self.origDate.getUTCFullYear(), 4) + '-' +
+ padNumberInMock(self.origDate.getUTCMonth() + 1, 2) + '-' +
+ padNumberInMock(self.origDate.getUTCDate(), 2) + 'T' +
+ padNumberInMock(self.origDate.getUTCHours(), 2) + ':' +
+ padNumberInMock(self.origDate.getUTCMinutes(), 2) + ':' +
+ padNumberInMock(self.origDate.getUTCSeconds(), 2) + '.' +
+ padNumberInMock(self.origDate.getUTCMilliseconds(), 3) + 'Z';
+ };
+ }
+
+ //hide all methods not implemented in this mock that the Date prototype exposes
+ var unimplementedMethods = ['getUTCDay',
+ 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
+ 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
+ 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
+ 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
+ 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
+
+ angular.forEach(unimplementedMethods, function(methodName) {
+ self[methodName] = function() {
+ throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
+ };
+ });
+
+ return self;
+};
+
+//make "tzDateInstance instanceof Date" return true
+angular.mock.TzDate.prototype = Date.prototype;
+/* jshint +W101 */
+
+
+/**
+ * @ngdoc service
+ * @name $animate
+ *
+ * @description
+ * Mock implementation of the {@link ng.$animate `$animate`} service. Exposes two additional methods
+ * for testing animations.
+ *
+ * You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
+ */
+angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
+
+ .config(['$provide', function($provide) {
+
+ $provide.factory('$$forceReflow', function() {
+ function reflowFn() {
+ reflowFn.totalReflows++;
+ }
+ reflowFn.totalReflows = 0;
+ return reflowFn;
+ });
+
+ $provide.factory('$$animateAsyncRun', function() {
+ var queue = [];
+ var queueFn = function() {
+ return function(fn) {
+ queue.push(fn);
+ };
+ };
+ queueFn.flush = function() {
+ if (queue.length === 0) return false;
+
+ for (var i = 0; i < queue.length; i++) {
+ queue[i]();
+ }
+ queue = [];
+
+ return true;
+ };
+ return queueFn;
+ });
+
+ $provide.decorator('$$animateJs', ['$delegate', function($delegate) {
+ var runners = [];
+
+ var animateJsConstructor = function() {
+ var animator = $delegate.apply($delegate, arguments);
+ // If no javascript animation is found, animator is undefined
+ if (animator) {
+ runners.push(animator);
+ }
+ return animator;
+ };
+
+ animateJsConstructor.$closeAndFlush = function() {
+ runners.forEach(function(runner) {
+ runner.end();
+ });
+ runners = [];
+ };
+
+ return animateJsConstructor;
+ }]);
+
+ $provide.decorator('$animateCss', ['$delegate', function($delegate) {
+ var runners = [];
+
+ var animateCssConstructor = function(element, options) {
+ var animator = $delegate(element, options);
+ runners.push(animator);
+ return animator;
+ };
+
+ animateCssConstructor.$closeAndFlush = function() {
+ runners.forEach(function(runner) {
+ runner.end();
+ });
+ runners = [];
+ };
+
+ return animateCssConstructor;
+ }]);
+
+ $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$animateCss', '$$animateJs',
+ '$$forceReflow', '$$animateAsyncRun', '$rootScope',
+ function($delegate, $timeout, $browser, $$rAF, $animateCss, $$animateJs,
+ $$forceReflow, $$animateAsyncRun, $rootScope) {
+ var animate = {
+ queue: [],
+ cancel: $delegate.cancel,
+ on: $delegate.on,
+ off: $delegate.off,
+ pin: $delegate.pin,
+ get reflows() {
+ return $$forceReflow.totalReflows;
+ },
+ enabled: $delegate.enabled,
+ /**
+ * @ngdoc method
+ * @name $animate#closeAndFlush
+ * @description
+ *
+ * This method will close all pending animations (both {@link ngAnimate#javascript-based-animations Javascript}
+ * and {@link ngAnimate.$animateCss CSS}) and it will also flush any remaining animation frames and/or callbacks.
+ */
+ closeAndFlush: function() {
+ // we allow the flush command to swallow the errors
+ // because depending on whether CSS or JS animations are
+ // used, there may not be a RAF flush. The primary flush
+ // at the end of this function must throw an exception
+ // because it will track if there were pending animations
+ this.flush(true);
+ $animateCss.$closeAndFlush();
+ $$animateJs.$closeAndFlush();
+ this.flush();
+ },
+ /**
+ * @ngdoc method
+ * @name $animate#flush
+ * @description
+ *
+ * This method is used to flush the pending callbacks and animation frames to either start
+ * an animation or conclude an animation. Note that this will not actually close an
+ * actively running animation (see {@link ngMock.$animate#closeAndFlush `closeAndFlush()`} for that).
+ */
+ flush: function(hideErrors) {
+ $rootScope.$digest();
+
+ var doNextRun, somethingFlushed = false;
+ do {
+ doNextRun = false;
+
+ if ($$rAF.queue.length) {
+ $$rAF.flush();
+ doNextRun = somethingFlushed = true;
+ }
+
+ if ($$animateAsyncRun.flush()) {
+ doNextRun = somethingFlushed = true;
+ }
+ } while (doNextRun);
+
+ if (!somethingFlushed && !hideErrors) {
+ throw new Error('No pending animations ready to be closed or flushed');
+ }
+
+ $rootScope.$digest();
+ }
+ };
+
+ angular.forEach(
+ ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
+ animate[method] = function() {
+ animate.queue.push({
+ event: method,
+ element: arguments[0],
+ options: arguments[arguments.length - 1],
+ args: arguments
+ });
+ return $delegate[method].apply($delegate, arguments);
+ };
+ });
+
+ return animate;
+ }]);
+
+ }]);
+
+
+/**
+ * @ngdoc function
+ * @name angular.mock.dump
+ * @description
+ *
+ * *NOTE*: This is not an injectable instance, just a globally available function.
+ *
+ * Method for serializing common angular objects (scope, elements, etc..) into strings.
+ * It is useful for logging objects to the console when debugging.
+ *
+ * @param {*} object - any object to turn into string.
+ * @return {string} a serialized string of the argument
+ */
+angular.mock.dump = function(object) {
+ return serialize(object);
+
+ function serialize(object) {
+ var out;
+
+ if (angular.isElement(object)) {
+ object = angular.element(object);
+ out = angular.element('');
+ angular.forEach(object, function(element) {
+ out.append(angular.element(element).clone());
+ });
+ out = out.html();
+ } else if (angular.isArray(object)) {
+ out = [];
+ angular.forEach(object, function(o) {
+ out.push(serialize(o));
+ });
+ out = '[ ' + out.join(', ') + ' ]';
+ } else if (angular.isObject(object)) {
+ if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
+ out = serializeScope(object);
+ } else if (object instanceof Error) {
+ out = object.stack || ('' + object.name + ': ' + object.message);
+ } else {
+ // TODO(i): this prevents methods being logged,
+ // we should have a better way to serialize objects
+ out = angular.toJson(object, true);
+ }
+ } else {
+ out = String(object);
+ }
+
+ return out;
+ }
+
+ function serializeScope(scope, offset) {
+ offset = offset || ' ';
+ var log = [offset + 'Scope(' + scope.$id + '): {'];
+ for (var key in scope) {
+ if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
+ log.push(' ' + key + ': ' + angular.toJson(scope[key]));
+ }
+ }
+ var child = scope.$$childHead;
+ while (child) {
+ log.push(serializeScope(child, offset + ' '));
+ child = child.$$nextSibling;
+ }
+ log.push('}');
+ return log.join('\n' + offset);
+ }
+};
+
+/**
+ * @ngdoc service
+ * @name $httpBackend
+ * @description
+ * Fake HTTP backend implementation suitable for unit testing applications that use the
+ * {@link ng.$http $http service}.
+ *
+ *
+ * **Note**: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
+ * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
+ *
+ *
+ * During unit testing, we want our unit tests to run quickly and have no external dependencies so
+ * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
+ * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
+ * to verify whether a certain request has been sent or not, or alternatively just let the
+ * application make requests, respond with pre-trained responses and assert that the end result is
+ * what we expect it to be.
+ *
+ * This mock implementation can be used to respond with static or dynamic responses via the
+ * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
+ *
+ * When an Angular application needs some data from a server, it calls the $http service, which
+ * sends the request to a real server using $httpBackend service. With dependency injection, it is
+ * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
+ * the requests and respond with some testing data without sending a request to a real server.
+ *
+ * There are two ways to specify what test data should be returned as http responses by the mock
+ * backend when the code under test makes http requests:
+ *
+ * - `$httpBackend.expect` - specifies a request expectation
+ * - `$httpBackend.when` - specifies a backend definition
+ *
+ *
+ * ## Request Expectations vs Backend Definitions
+ *
+ * Request expectations provide a way to make assertions about requests made by the application and
+ * to define responses for those requests. The test will fail if the expected requests are not made
+ * or they are made in the wrong order.
+ *
+ * Backend definitions allow you to define a fake backend for your application which doesn't assert
+ * if a particular request was made or not, it just returns a trained response if a request is made.
+ * The test will pass whether or not the request gets made during testing.
+ *
+ *
+ *
+ *
Request expectations
Backend definitions
+ *
+ *
Syntax
+ *
.expect(...).respond(...)
+ *
.when(...).respond(...)
+ *
+ *
+ *
Typical usage
+ *
strict unit tests
+ *
loose (black-box) unit testing
+ *
+ *
+ *
Fulfills multiple requests
+ *
NO
+ *
YES
+ *
+ *
+ *
Order of requests matters
+ *
YES
+ *
NO
+ *
+ *
+ *
Request required
+ *
YES
+ *
NO
+ *
+ *
+ *
Response required
+ *
optional (see below)
+ *
YES
+ *
+ *
+ *
+ * In cases where both backend definitions and request expectations are specified during unit
+ * testing, the request expectations are evaluated first.
+ *
+ * If a request expectation has no response specified, the algorithm will search your backend
+ * definitions for an appropriate response.
+ *
+ * If a request didn't match any expectation or if the expectation doesn't have the response
+ * defined, the backend definitions are evaluated in sequential order to see if any of them match
+ * the request. The response from the first matched definition is returned.
+ *
+ *
+ * ## Flushing HTTP requests
+ *
+ * The $httpBackend used in production always responds to requests asynchronously. If we preserved
+ * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
+ * to follow and to maintain. But neither can the testing mock respond synchronously; that would
+ * change the execution of the code under test. For this reason, the mock $httpBackend has a
+ * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
+ * the async api of the backend, while allowing the test to execute synchronously.
+ *
+ *
+ * ## Unit testing with mock $httpBackend
+ * The following code shows how to setup and use the mock backend when unit testing a controller.
+ * First we create the controller under test:
+ *
+ ```js
+ // The module code
+ angular
+ .module('MyApp', [])
+ .controller('MyController', MyController);
+
+ // The controller code
+ function MyController($scope, $http) {
+ var authToken;
+
+ $http.get('/auth.py').then(function(response) {
+ authToken = response.headers('A-Token');
+ $scope.user = response.data;
+ });
+
+ $scope.saveMessage = function(message) {
+ var headers = { 'Authorization': authToken };
+ $scope.status = 'Saving...';
+
+ $http.post('/add-msg.py', message, { headers: headers } ).then(function(response) {
+ $scope.status = '';
+ }).catch(function() {
+ $scope.status = 'Failed...';
+ });
+ };
+ }
+ ```
+ *
+ * Now we setup the mock backend and create the test specs:
+ *
+ ```js
+ // testing controller
+ describe('MyController', function() {
+ var $httpBackend, $rootScope, createController, authRequestHandler;
+
+ // Set up the module
+ beforeEach(module('MyApp'));
+
+ beforeEach(inject(function($injector) {
+ // Set up the mock http service responses
+ $httpBackend = $injector.get('$httpBackend');
+ // backend definition common for all tests
+ authRequestHandler = $httpBackend.when('GET', '/auth.py')
+ .respond({userId: 'userX'}, {'A-Token': 'xxx'});
+
+ // Get hold of a scope (i.e. the root scope)
+ $rootScope = $injector.get('$rootScope');
+ // The $controller service is used to create instances of controllers
+ var $controller = $injector.get('$controller');
+
+ createController = function() {
+ return $controller('MyController', {'$scope' : $rootScope });
+ };
+ }));
+
+
+ afterEach(function() {
+ $httpBackend.verifyNoOutstandingExpectation();
+ $httpBackend.verifyNoOutstandingRequest();
+ });
+
+
+ it('should fetch authentication token', function() {
+ $httpBackend.expectGET('/auth.py');
+ var controller = createController();
+ $httpBackend.flush();
+ });
+
+
+ it('should fail authentication', function() {
+
+ // Notice how you can change the response even after it was set
+ authRequestHandler.respond(401, '');
+
+ $httpBackend.expectGET('/auth.py');
+ var controller = createController();
+ $httpBackend.flush();
+ expect($rootScope.status).toBe('Failed...');
+ });
+
+
+ it('should send msg to server', function() {
+ var controller = createController();
+ $httpBackend.flush();
+
+ // now you don’t care about the authentication, but
+ // the controller will still send the request and
+ // $httpBackend will respond without you having to
+ // specify the expectation and response for this request
+
+ $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
+ $rootScope.saveMessage('message content');
+ expect($rootScope.status).toBe('Saving...');
+ $httpBackend.flush();
+ expect($rootScope.status).toBe('');
+ });
+
+
+ it('should send auth header', function() {
+ var controller = createController();
+ $httpBackend.flush();
+
+ $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
+ // check if the header was sent, if it wasn't the expectation won't
+ // match the request and the test will fail
+ return headers['Authorization'] == 'xxx';
+ }).respond(201, '');
+
+ $rootScope.saveMessage('whatever');
+ $httpBackend.flush();
+ });
+ });
+ ```
+ *
+ * ## Dynamic responses
+ *
+ * You define a response to a request by chaining a call to `respond()` onto a definition or expectation.
+ * If you provide a **callback** as the first parameter to `respond(callback)` then you can dynamically generate
+ * a response based on the properties of the request.
+ *
+ * The `callback` function should be of the form `function(method, url, data, headers, params)`.
+ *
+ * ### Query parameters
+ *
+ * By default, query parameters on request URLs are parsed into the `params` object. So a request URL
+ * of `/list?q=searchstr&orderby=-name` would set `params` to be `{q: 'searchstr', orderby: '-name'}`.
+ *
+ * ### Regex parameter matching
+ *
+ * If an expectation or definition uses a **regex** to match the URL, you can provide an array of **keys** via a
+ * `params` argument. The index of each **key** in the array will match the index of a **group** in the
+ * **regex**.
+ *
+ * The `params` object in the **callback** will now have properties with these keys, which hold the value of the
+ * corresponding **group** in the **regex**.
+ *
+ * This also applies to the `when` and `expect` shortcut methods.
+ *
+ *
+ * ```js
+ * $httpBackend.expect('GET', /\/user\/(.+)/, undefined, undefined, ['id'])
+ * .respond(function(method, url, data, headers, params) {
+ * // for requested url of '/user/1234' params is {id: '1234'}
+ * });
+ *
+ * $httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article'])
+ * .respond(function(method, url, data, headers, params) {
+ * // for url of '/user/1234/article/567' params is {user: '1234', article: '567'}
+ * });
+ * ```
+ *
+ * ## Matching route requests
+ *
+ * For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
+ * delimited matching of the url path, ignoring the query string. This allows declarations
+ * similar to how application routes are configured with `$routeProvider`. Because these methods convert
+ * the definition url to regex, declaration order is important. Combined with query parameter parsing,
+ * the following is possible:
+ *
+ ```js
+ $httpBackend.whenRoute('GET', '/users/:id')
+ .respond(function(method, url, data, headers, params) {
+ return [200, MockUserList[Number(params.id)]];
+ });
+
+ $httpBackend.whenRoute('GET', '/users')
+ .respond(function(method, url, data, headers, params) {
+ var userList = angular.copy(MockUserList),
+ defaultSort = 'lastName',
+ count, pages, isPrevious, isNext;
+
+ // paged api response '/v1/users?page=2'
+ params.page = Number(params.page) || 1;
+
+ // query for last names '/v1/users?q=Archer'
+ if (params.q) {
+ userList = $filter('filter')({lastName: params.q});
+ }
+
+ pages = Math.ceil(userList.length / pagingLength);
+ isPrevious = params.page > 1;
+ isNext = params.page < pages;
+
+ return [200, {
+ count: userList.length,
+ previous: isPrevious,
+ next: isNext,
+ // sort field -> '/v1/users?sortBy=firstName'
+ results: $filter('orderBy')(userList, params.sortBy || defaultSort)
+ .splice((params.page - 1) * pagingLength, pagingLength)
+ }];
+ });
+ ```
+ */
+angular.mock.$HttpBackendProvider = function() {
+ this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
+};
+
+/**
+ * General factory function for $httpBackend mock.
+ * Returns instance for unit testing (when no arguments specified):
+ * - passing through is disabled
+ * - auto flushing is disabled
+ *
+ * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
+ * - passing through (delegating request to real backend) is enabled
+ * - auto flushing is enabled
+ *
+ * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
+ * @param {Object=} $browser Auto-flushing enabled if specified
+ * @return {Object} Instance of $httpBackend mock
+ */
+function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
+ var definitions = [],
+ expectations = [],
+ responses = [],
+ responsesPush = angular.bind(responses, responses.push),
+ copy = angular.copy;
+
+ function createResponse(status, data, headers, statusText) {
+ if (angular.isFunction(status)) return status;
+
+ return function() {
+ return angular.isNumber(status)
+ ? [status, data, headers, statusText]
+ : [200, status, data, headers];
+ };
+ }
+
+ // TODO(vojta): change params to: method, url, data, headers, callback
+ function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
+
+ var xhr = new MockXhr(),
+ expectation = expectations[0],
+ wasExpected = false;
+
+ xhr.$$events = eventHandlers;
+ xhr.upload.$$events = uploadEventHandlers;
+
+ function prettyPrint(data) {
+ return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
+ ? data
+ : angular.toJson(data);
+ }
+
+ function wrapResponse(wrapped) {
+ if (!$browser && timeout) {
+ timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
+ }
+
+ return handleResponse;
+
+ function handleResponse() {
+ var response = wrapped.response(method, url, data, headers, wrapped.params(url));
+ xhr.$$respHeaders = response[2];
+ callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
+ copy(response[3] || ''));
+ }
+
+ function handleTimeout() {
+ for (var i = 0, ii = responses.length; i < ii; i++) {
+ if (responses[i] === handleResponse) {
+ responses.splice(i, 1);
+ callback(-1, undefined, '');
+ break;
+ }
+ }
+ }
+ }
+
+ if (expectation && expectation.match(method, url)) {
+ if (!expectation.matchData(data)) {
+ throw new Error('Expected ' + expectation + ' with different data\n' +
+ 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
+ }
+
+ if (!expectation.matchHeaders(headers)) {
+ throw new Error('Expected ' + expectation + ' with different headers\n' +
+ 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
+ prettyPrint(headers));
+ }
+
+ expectations.shift();
+
+ if (expectation.response) {
+ responses.push(wrapResponse(expectation));
+ return;
+ }
+ wasExpected = true;
+ }
+
+ var i = -1, definition;
+ while ((definition = definitions[++i])) {
+ if (definition.match(method, url, data, headers || {})) {
+ if (definition.response) {
+ // if $browser specified, we do auto flush all requests
+ ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
+ } else if (definition.passThrough) {
+ $delegate(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
+ } else throw new Error('No response defined !');
+ return;
+ }
+ }
+ throw wasExpected ?
+ new Error('No response defined !') :
+ new Error('Unexpected request: ' + method + ' ' + url + '\n' +
+ (expectation ? 'Expected ' + expectation : 'No more request expected'));
+ }
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#when
+ * @description
+ * Creates a new backend definition.
+ *
+ * @param {string} method HTTP method.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
+ * data string and returns true if the data is as expected.
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
+ * object and returns true if the headers match the current definition.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ *
+ * - respond –
+ * ```js
+ * {function([status,] data[, headers, statusText])
+ * | function(function(method, url, data, headers, params)}
+ * ```
+ * – The respond method takes a set of static data to be returned or a function that can
+ * return an array containing response status (number), response data (Array|Object|string),
+ * response headers (Object), and the text for the status (string). The respond method returns
+ * the `requestHandler` object for possible overrides.
+ */
+ $httpBackend.when = function(method, url, data, headers, keys) {
+ var definition = new MockHttpExpectation(method, url, data, headers, keys),
+ chain = {
+ respond: function(status, data, headers, statusText) {
+ definition.passThrough = undefined;
+ definition.response = createResponse(status, data, headers, statusText);
+ return chain;
+ }
+ };
+
+ if ($browser) {
+ chain.passThrough = function() {
+ definition.response = undefined;
+ definition.passThrough = true;
+ return chain;
+ };
+ }
+
+ definitions.push(definition);
+ return chain;
+ };
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#whenGET
+ * @description
+ * Creates a new backend definition for GET requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#whenHEAD
+ * @description
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#whenDELETE
+ * @description
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#whenPOST
+ * @description
+ * Creates a new backend definition for POST requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
+ * data string and returns true if the data is as expected.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#whenPUT
+ * @description
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
+ * data string and returns true if the data is as expected.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#whenJSONP
+ * @description
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+ createShortMethods('when');
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#whenRoute
+ * @description
+ * Creates a new backend definition that compares only with the requested route.
+ *
+ * @param {string} method HTTP method.
+ * @param {string} url HTTP url string that supports colon param matching.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled. See #when for more info.
+ */
+ $httpBackend.whenRoute = function(method, url) {
+ var pathObj = parseRoute(url);
+ return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys);
+ };
+
+ function parseRoute(url) {
+ var ret = {
+ regexp: url
+ },
+ keys = ret.keys = [];
+
+ if (!url || !angular.isString(url)) return ret;
+
+ url = url
+ .replace(/([().])/g, '\\$1')
+ .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
+ var optional = option === '?' ? option : null;
+ var star = option === '*' ? option : null;
+ keys.push({ name: key, optional: !!optional });
+ slash = slash || '';
+ return ''
+ + (optional ? '' : slash)
+ + '(?:'
+ + (optional ? slash : '')
+ + (star && '(.+?)' || '([^/]+)')
+ + (optional || '')
+ + ')'
+ + (optional || '');
+ })
+ .replace(/([\/$\*])/g, '\\$1');
+
+ ret.regexp = new RegExp('^' + url, 'i');
+ return ret;
+ }
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expect
+ * @description
+ * Creates a new request expectation.
+ *
+ * @param {string} method HTTP method.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+ * receives data string and returns true if the data is as expected, or Object if request body
+ * is in JSON format.
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
+ * object and returns true if the headers match the current expectation.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ *
+ * - respond –
+ * ```
+ * { function([status,] data[, headers, statusText])
+ * | function(function(method, url, data, headers, params)}
+ * ```
+ * – The respond method takes a set of static data to be returned or a function that can
+ * return an array containing response status (number), response data (Array|Object|string),
+ * response headers (Object), and the text for the status (string). The respond method returns
+ * the `requestHandler` object for possible overrides.
+ */
+ $httpBackend.expect = function(method, url, data, headers, keys) {
+ var expectation = new MockHttpExpectation(method, url, data, headers, keys),
+ chain = {
+ respond: function(status, data, headers, statusText) {
+ expectation.response = createResponse(status, data, headers, statusText);
+ return chain;
+ }
+ };
+
+ expectations.push(expectation);
+ return chain;
+ };
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectGET
+ * @description
+ * Creates a new request expectation for GET requests. For more info see `expect()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {Object=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled. See #expect for more info.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectHEAD
+ * @description
+ * Creates a new request expectation for HEAD requests. For more info see `expect()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {Object=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectDELETE
+ * @description
+ * Creates a new request expectation for DELETE requests. For more info see `expect()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {Object=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectPOST
+ * @description
+ * Creates a new request expectation for POST requests. For more info see `expect()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+ * receives data string and returns true if the data is as expected, or Object if request body
+ * is in JSON format.
+ * @param {Object=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectPUT
+ * @description
+ * Creates a new request expectation for PUT requests. For more info see `expect()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+ * receives data string and returns true if the data is as expected, or Object if request body
+ * is in JSON format.
+ * @param {Object=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectPATCH
+ * @description
+ * Creates a new request expectation for PATCH requests. For more info see `expect()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
+ * receives data string and returns true if the data is as expected, or Object if request body
+ * is in JSON format.
+ * @param {Object=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectJSONP
+ * @description
+ * Creates a new request expectation for JSONP requests. For more info see `expect()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
+ * and returns true if the url matches the current definition.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled.
+ */
+ createShortMethods('expect');
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#expectRoute
+ * @description
+ * Creates a new request expectation that compares only with the requested route.
+ *
+ * @param {string} method HTTP method.
+ * @param {string} url HTTP url string that supports colon param matching.
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
+ * request is handled. You can save this object for later use and invoke `respond` again in
+ * order to change how a matched request is handled. See #expect for more info.
+ */
+ $httpBackend.expectRoute = function(method, url) {
+ var pathObj = parseRoute(url);
+ return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys);
+ };
+
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#flush
+ * @description
+ * Flushes all pending requests using the trained responses.
+ *
+ * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
+ * all pending requests will be flushed. If there are no pending requests when the flush method
+ * is called an exception is thrown (as this typically a sign of programming error).
+ */
+ $httpBackend.flush = function(count, digest) {
+ if (digest !== false) $rootScope.$digest();
+ if (!responses.length) throw new Error('No pending request to flush !');
+
+ if (angular.isDefined(count) && count !== null) {
+ while (count--) {
+ if (!responses.length) throw new Error('No more pending request to flush !');
+ responses.shift()();
+ }
+ } else {
+ while (responses.length) {
+ responses.shift()();
+ }
+ }
+ $httpBackend.verifyNoOutstandingExpectation(digest);
+ };
+
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#verifyNoOutstandingExpectation
+ * @description
+ * Verifies that all of the requests defined via the `expect` api were made. If any of the
+ * requests were not made, verifyNoOutstandingExpectation throws an exception.
+ *
+ * Typically, you would call this method following each test case that asserts requests using an
+ * "afterEach" clause.
+ *
+ * ```js
+ * afterEach($httpBackend.verifyNoOutstandingExpectation);
+ * ```
+ */
+ $httpBackend.verifyNoOutstandingExpectation = function(digest) {
+ if (digest !== false) $rootScope.$digest();
+ if (expectations.length) {
+ throw new Error('Unsatisfied requests: ' + expectations.join(', '));
+ }
+ };
+
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#verifyNoOutstandingRequest
+ * @description
+ * Verifies that there are no outstanding requests that need to be flushed.
+ *
+ * Typically, you would call this method following each test case that asserts requests using an
+ * "afterEach" clause.
+ *
+ * ```js
+ * afterEach($httpBackend.verifyNoOutstandingRequest);
+ * ```
+ */
+ $httpBackend.verifyNoOutstandingRequest = function() {
+ if (responses.length) {
+ throw new Error('Unflushed requests: ' + responses.length);
+ }
+ };
+
+
+ /**
+ * @ngdoc method
+ * @name $httpBackend#resetExpectations
+ * @description
+ * Resets all request expectations, but preserves all backend definitions. Typically, you would
+ * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
+ * $httpBackend mock.
+ */
+ $httpBackend.resetExpectations = function() {
+ expectations.length = 0;
+ responses.length = 0;
+ };
+
+ return $httpBackend;
+
+
+ function createShortMethods(prefix) {
+ angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
+ $httpBackend[prefix + method] = function(url, headers, keys) {
+ return $httpBackend[prefix](method, url, undefined, headers, keys);
+ };
+ });
+
+ angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
+ $httpBackend[prefix + method] = function(url, data, headers, keys) {
+ return $httpBackend[prefix](method, url, data, headers, keys);
+ };
+ });
+ }
+}
+
+function MockHttpExpectation(method, url, data, headers, keys) {
+
+ function getUrlParams(u) {
+ var params = u.slice(u.indexOf('?') + 1).split('&');
+ return params.sort();
+ }
+
+ function compareUrl(u) {
+ return (url.slice(0, url.indexOf('?')) == u.slice(0, u.indexOf('?')) && getUrlParams(url).join() == getUrlParams(u).join());
+ }
+
+ this.data = data;
+ this.headers = headers;
+
+ this.match = function(m, u, d, h) {
+ if (method != m) return false;
+ if (!this.matchUrl(u)) return false;
+ if (angular.isDefined(d) && !this.matchData(d)) return false;
+ if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
+ return true;
+ };
+
+ this.matchUrl = function(u) {
+ if (!url) return true;
+ if (angular.isFunction(url.test)) return url.test(u);
+ if (angular.isFunction(url)) return url(u);
+ return (url == u || compareUrl(u));
+ };
+
+ this.matchHeaders = function(h) {
+ if (angular.isUndefined(headers)) return true;
+ if (angular.isFunction(headers)) return headers(h);
+ return angular.equals(headers, h);
+ };
+
+ this.matchData = function(d) {
+ if (angular.isUndefined(data)) return true;
+ if (data && angular.isFunction(data.test)) return data.test(d);
+ if (data && angular.isFunction(data)) return data(d);
+ if (data && !angular.isString(data)) {
+ return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
+ }
+ return data == d;
+ };
+
+ this.toString = function() {
+ return method + ' ' + url;
+ };
+
+ this.params = function(u) {
+ return angular.extend(parseQuery(), pathParams());
+
+ function pathParams() {
+ var keyObj = {};
+ if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
+
+ var m = url.exec(u);
+ if (!m) return keyObj;
+ for (var i = 1, len = m.length; i < len; ++i) {
+ var key = keys[i - 1];
+ var val = m[i];
+ if (key && val) {
+ keyObj[key.name || key] = val;
+ }
+ }
+
+ return keyObj;
+ }
+
+ function parseQuery() {
+ var obj = {}, key_value, key,
+ queryStr = u.indexOf('?') > -1
+ ? u.substring(u.indexOf('?') + 1)
+ : "";
+
+ angular.forEach(queryStr.split('&'), function(keyValue) {
+ if (keyValue) {
+ key_value = keyValue.replace(/\+/g,'%20').split('=');
+ key = tryDecodeURIComponent(key_value[0]);
+ if (angular.isDefined(key)) {
+ var val = angular.isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
+ if (!hasOwnProperty.call(obj, key)) {
+ obj[key] = val;
+ } else if (angular.isArray(obj[key])) {
+ obj[key].push(val);
+ } else {
+ obj[key] = [obj[key],val];
+ }
+ }
+ }
+ });
+ return obj;
+ }
+ function tryDecodeURIComponent(value) {
+ try {
+ return decodeURIComponent(value);
+ } catch (e) {
+ // Ignore any invalid uri component
+ }
+ }
+ };
+}
+
+function createMockXhr() {
+ return new MockXhr();
+}
+
+function MockXhr() {
+
+ // hack for testing $http, $httpBackend
+ MockXhr.$$lastInstance = this;
+
+ this.open = function(method, url, async) {
+ this.$$method = method;
+ this.$$url = url;
+ this.$$async = async;
+ this.$$reqHeaders = {};
+ this.$$respHeaders = {};
+ };
+
+ this.send = function(data) {
+ this.$$data = data;
+ };
+
+ this.setRequestHeader = function(key, value) {
+ this.$$reqHeaders[key] = value;
+ };
+
+ this.getResponseHeader = function(name) {
+ // the lookup must be case insensitive,
+ // that's why we try two quick lookups first and full scan last
+ var header = this.$$respHeaders[name];
+ if (header) return header;
+
+ name = angular.lowercase(name);
+ header = this.$$respHeaders[name];
+ if (header) return header;
+
+ header = undefined;
+ angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
+ if (!header && angular.lowercase(headerName) == name) header = headerVal;
+ });
+ return header;
+ };
+
+ this.getAllResponseHeaders = function() {
+ var lines = [];
+
+ angular.forEach(this.$$respHeaders, function(value, key) {
+ lines.push(key + ': ' + value);
+ });
+ return lines.join('\n');
+ };
+
+ this.abort = angular.noop;
+
+ // This section simulates the events on a real XHR object (and the upload object)
+ // When we are testing $httpBackend (inside the angular project) we make partial use of this
+ // but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
+ this.$$events = {};
+ this.addEventListener = function(name, listener) {
+ if (angular.isUndefined(this.$$events[name])) this.$$events[name] = [];
+ this.$$events[name].push(listener);
+ };
+
+ this.upload = {
+ $$events: {},
+ addEventListener: this.addEventListener
+ };
+}
+
+
+/**
+ * @ngdoc service
+ * @name $timeout
+ * @description
+ *
+ * This service is just a simple decorator for {@link ng.$timeout $timeout} service
+ * that adds a "flush" and "verifyNoPendingTasks" methods.
+ */
+
+angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
+
+ /**
+ * @ngdoc method
+ * @name $timeout#flush
+ * @description
+ *
+ * Flushes the queue of pending tasks.
+ *
+ * @param {number=} delay maximum timeout amount to flush up until
+ */
+ $delegate.flush = function(delay) {
+ $browser.defer.flush(delay);
+ };
+
+ /**
+ * @ngdoc method
+ * @name $timeout#verifyNoPendingTasks
+ * @description
+ *
+ * Verifies that there are no pending tasks that need to be flushed.
+ */
+ $delegate.verifyNoPendingTasks = function() {
+ if ($browser.deferredFns.length) {
+ throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
+ formatPendingTasksAsString($browser.deferredFns));
+ }
+ };
+
+ function formatPendingTasksAsString(tasks) {
+ var result = [];
+ angular.forEach(tasks, function(task) {
+ result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
+ });
+
+ return result.join(', ');
+ }
+
+ return $delegate;
+}];
+
+angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
+ var rafFn = function(fn) {
+ var index = rafFn.queue.length;
+ rafFn.queue.push(fn);
+ return function() {
+ rafFn.queue.splice(index, 1);
+ };
+ };
+
+ rafFn.queue = [];
+ rafFn.supported = $delegate.supported;
+
+ rafFn.flush = function() {
+ if (rafFn.queue.length === 0) {
+ throw new Error('No rAF callbacks present');
+ }
+
+ var length = rafFn.queue.length;
+ for (var i = 0; i < length; i++) {
+ rafFn.queue[i]();
+ }
+
+ rafFn.queue = rafFn.queue.slice(i);
+ };
+
+ return rafFn;
+}];
+
+/**
+ *
+ */
+var originalRootElement;
+angular.mock.$RootElementProvider = function() {
+ this.$get = ['$injector', function($injector) {
+ originalRootElement = angular.element('').data('$injector', $injector);
+ return originalRootElement;
+ }];
+};
+
+/**
+ * @ngdoc service
+ * @name $controller
+ * @description
+ * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
+ * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
+ *
+ *
+ * ## Example
+ *
+ * ```js
+ *
+ * // Directive definition ...
+ *
+ * myMod.directive('myDirective', {
+ * controller: 'MyDirectiveController',
+ * bindToController: {
+ * name: '@'
+ * }
+ * });
+ *
+ *
+ * // Controller definition ...
+ *
+ * myMod.controller('MyDirectiveController', ['$log', function($log) {
+ * $log.info(this.name);
+ * }]);
+ *
+ *
+ * // In a test ...
+ *
+ * describe('myDirectiveController', function() {
+ * it('should write the bound name to the log', inject(function($controller, $log) {
+ * var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
+ * expect(ctrl.name).toEqual('Clark Kent');
+ * expect($log.info.logs).toEqual(['Clark Kent']);
+ * }));
+ * });
+ *
+ * ```
+ *
+ * @param {Function|string} constructor If called with a function then it's considered to be the
+ * controller constructor function. Otherwise it's considered to be a string which is used
+ * to retrieve the controller constructor using the following steps:
+ *
+ * * check if a controller with given name is registered via `$controllerProvider`
+ * * check if evaluating the string on the current scope returns a constructor
+ * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
+ * `window` object (not recommended)
+ *
+ * The string can use the `controller as property` syntax, where the controller instance is published
+ * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
+ * to work correctly.
+ *
+ * @param {Object} locals Injection locals for Controller.
+ * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
+ * to simulate the `bindToController` feature and simplify certain kinds of tests.
+ * @return {Object} Instance of given controller.
+ */
+angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
+ return function(expression, locals, later, ident) {
+ if (later && typeof later === 'object') {
+ var instantiate = $delegate(expression, locals, true, ident);
+ angular.extend(instantiate.instance, later);
+
+ var instance = instantiate();
+ if (instance !== instantiate.instance) {
+ angular.extend(instance, later);
+ }
+
+ return instance;
+ }
+ return $delegate(expression, locals, later, ident);
+ };
+}];
+
+/**
+ * @ngdoc service
+ * @name $componentController
+ * @description
+ * A service that can be used to create instances of component controllers.
+ *
+ * Be aware that the controller will be instantiated and attached to the scope as specified in
+ * the component definition object. If you do not provide a `$scope` object in the `locals` param
+ * then the helper will create a new isolated scope as a child of `$rootScope`.
+ *
+ * @param {string} componentName the name of the component whose controller we want to instantiate
+ * @param {Object} locals Injection locals for Controller.
+ * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
+ * to simulate the `bindToController` feature and simplify certain kinds of tests.
+ * @param {string=} ident Override the property name to use when attaching the controller to the scope.
+ * @return {Object} Instance of requested controller.
+ */
+angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
+ this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
+ return function $componentController(componentName, locals, bindings, ident) {
+ // get all directives associated to the component name
+ var directives = $injector.get(componentName + 'Directive');
+ // look for those directives that are components
+ var candidateDirectives = directives.filter(function(directiveInfo) {
+ // components have controller, controllerAs and restrict:'E'
+ return directiveInfo.controller && directiveInfo.controllerAs && directiveInfo.restrict === 'E';
+ });
+ // check if valid directives found
+ if (candidateDirectives.length === 0) {
+ throw new Error('No component found');
+ }
+ if (candidateDirectives.length > 1) {
+ throw new Error('Too many components found');
+ }
+ // get the info of the component
+ var directiveInfo = candidateDirectives[0];
+ // create a scope if needed
+ locals = locals || {};
+ locals.$scope = locals.$scope || $rootScope.$new(true);
+ return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
+ };
+ }];
+}];
+
+
+/**
+ * @ngdoc module
+ * @name ngMock
+ * @packageName angular-mocks
+ * @description
+ *
+ * # ngMock
+ *
+ * The `ngMock` module provides support to inject and mock Angular services into unit tests.
+ * In addition, ngMock also extends various core ng services such that they can be
+ * inspected and controlled in a synchronous manner within test code.
+ *
+ *
+ *
+ *
+ * @installation
+ *
+ * First, download the file:
+ * * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
+ * `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
+ * * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
+ * * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
+ * * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
+ * `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
+ *
+ * where X.Y.Z is the AngularJS version you are running.
+ *
+ * Then, configure your test runner to load `angular-mocks.js` after `angular.js`.
+ * This example uses Karma:
+ *
+ * ```
+ * config.set({
+ * files: [
+ * 'build/angular.js', // and other module files you need
+ * 'build/angular-mocks.js',
+ * '',
+ * ''
+ * ]
+ * });
+ * ```
+ *
+ * Including the `angular-mocks.js` file automatically adds the `ngMock` module, so your tests
+ * are ready to go!
+ */
+angular.module('ngMock', ['ng']).provider({
+ $browser: angular.mock.$BrowserProvider,
+ $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
+ $log: angular.mock.$LogProvider,
+ $interval: angular.mock.$IntervalProvider,
+ $httpBackend: angular.mock.$HttpBackendProvider,
+ $rootElement: angular.mock.$RootElementProvider,
+ $componentController: angular.mock.$ComponentControllerProvider
+}).config(['$provide', function($provide) {
+ $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
+ $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
+ $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
+ $provide.decorator('$controller', angular.mock.$ControllerDecorator);
+}]);
+
+/**
+ * @ngdoc module
+ * @name ngMockE2E
+ * @module ngMockE2E
+ * @packageName angular-mocks
+ * @description
+ *
+ * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
+ * Currently there is only one mock present in this module -
+ * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
+ */
+angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
+ $provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
+ $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
+}]);
+
+/**
+ * @ngdoc service
+ * @name $httpBackend
+ * @module ngMockE2E
+ * @description
+ * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
+ * applications that use the {@link ng.$http $http service}.
+ *
+ *
+ * **Note**: For fake http backend implementation suitable for unit testing please see
+ * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
+ *
+ *
+ * This implementation can be used to respond with static or dynamic responses via the `when` api
+ * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
+ * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
+ * templates from a webserver).
+ *
+ * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
+ * is being developed with the real backend api replaced with a mock, it is often desirable for
+ * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
+ * templates or static files from the webserver). To configure the backend with this behavior
+ * use the `passThrough` request handler of `when` instead of `respond`.
+ *
+ * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
+ * testing. For this reason the e2e $httpBackend flushes mocked out requests
+ * automatically, closely simulating the behavior of the XMLHttpRequest object.
+ *
+ * To setup the application to run with this http backend, you have to create a module that depends
+ * on the `ngMockE2E` and your application modules and defines the fake backend:
+ *
+ * ```js
+ * var myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
+ * myAppDev.run(function($httpBackend) {
+ * var phones = [{name: 'phone1'}, {name: 'phone2'}];
+ *
+ * // returns the current list of phones
+ * $httpBackend.whenGET('/phones').respond(phones);
+ *
+ * // adds a new phone to the phones array
+ * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
+ * var phone = angular.fromJson(data);
+ * phones.push(phone);
+ * return [200, phone, {}];
+ * });
+ * $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for templare are handled by the real server
+ * //...
+ * });
+ * ```
+ *
+ * Afterwards, bootstrap your app with this new module.
+ *
+ * ## Example
+ *
+ *
+ * var myApp = angular.module('myApp', []);
+ *
+ * myApp.controller('main', function($http) {
+ * var ctrl = this;
+ *
+ * ctrl.phones = [];
+ * ctrl.newPhone = {
+ * name: ''
+ * };
+ *
+ * ctrl.getPhones = function() {
+ * $http.get('/phones').then(function(response) {
+ * ctrl.phones = response.data;
+ * });
+ * };
+ *
+ * ctrl.addPhone = function(phone) {
+ * $http.post('/phones', phone).then(function() {
+ * ctrl.newPhone = {name: ''};
+ * return ctrl.getPhones();
+ * });
+ * };
+ *
+ * ctrl.getPhones();
+ * });
+ *
+ *
+ * var myAppDev = angular.module('myAppE2E', ['myApp', 'ngMockE2E']);
+ *
+ * myAppDev.run(function($httpBackend) {
+ * var phones = [{name: 'phone1'}, {name: 'phone2'}];
+ *
+ * // returns the current list of phones
+ * $httpBackend.whenGET('/phones').respond(phones);
+ *
+ * // adds a new phone to the phones array
+ * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
+ * var phone = angular.fromJson(data);
+ * phones.push(phone);
+ * return [200, phone, {}];
+ * });
+ * });
+ *
+ *
+ *
+ *
+ *
Phones
+ *
+ *
{{phone.name}}
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#when
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition.
+ *
+ * @param {string} method HTTP method.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp)=} data HTTP request body.
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
+ * object and returns true if the headers match the current definition.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ *
+ * - respond –
+ * ```
+ * { function([status,] data[, headers, statusText])
+ * | function(function(method, url, data, headers, params)}
+ * ```
+ * – The respond method takes a set of static data to be returned or a function that can return
+ * an array containing response status (number), response data (Array|Object|string), response
+ * headers (Object), and the text for the status (string).
+ * - passThrough – `{function()}` – Any request matching a backend definition with
+ * `passThrough` handler will be passed through to the real backend (an XHR request will be made
+ * to the server.)
+ * - Both methods return the `requestHandler` object for possible overrides.
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenGET
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition for GET requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenHEAD
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenDELETE
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenPOST
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition for POST requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp)=} data HTTP request body.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenPUT
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp)=} data HTTP request body.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenPATCH
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition for PATCH requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(string|RegExp)=} data HTTP request body.
+ * @param {(Object|function(Object))=} headers HTTP headers.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenJSONP
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
+ *
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * and returns true if the url matches the current definition.
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
+ * {@link ngMock.$httpBackend $httpBackend mock}.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+/**
+ * @ngdoc method
+ * @name $httpBackend#whenRoute
+ * @module ngMockE2E
+ * @description
+ * Creates a new backend definition that compares only with the requested route.
+ *
+ * @param {string} method HTTP method.
+ * @param {string} url HTTP url string that supports colon param matching.
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
+ * control how a matched request is handled. You can save this object for later use and invoke
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
+ */
+angular.mock.e2e = {};
+angular.mock.e2e.$httpBackendDecorator =
+ ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
+
+
+/**
+ * @ngdoc type
+ * @name $rootScope.Scope
+ * @module ngMock
+ * @description
+ * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
+ * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
+ * `ngMock` module is loaded.
+ *
+ * In addition to all the regular `Scope` methods, the following helper methods are available:
+ */
+angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
+
+ var $rootScopePrototype = Object.getPrototypeOf($delegate);
+
+ $rootScopePrototype.$countChildScopes = countChildScopes;
+ $rootScopePrototype.$countWatchers = countWatchers;
+
+ return $delegate;
+
+ // ------------------------------------------------------------------------------------------ //
+
+ /**
+ * @ngdoc method
+ * @name $rootScope.Scope#$countChildScopes
+ * @module ngMock
+ * @description
+ * Counts all the direct and indirect child scopes of the current scope.
+ *
+ * The current scope is excluded from the count. The count includes all isolate child scopes.
+ *
+ * @returns {number} Total number of child scopes.
+ */
+ function countChildScopes() {
+ // jshint validthis: true
+ var count = 0; // exclude the current scope
+ var pendingChildHeads = [this.$$childHead];
+ var currentScope;
+
+ while (pendingChildHeads.length) {
+ currentScope = pendingChildHeads.shift();
+
+ while (currentScope) {
+ count += 1;
+ pendingChildHeads.push(currentScope.$$childHead);
+ currentScope = currentScope.$$nextSibling;
+ }
+ }
+
+ return count;
+ }
+
+
+ /**
+ * @ngdoc method
+ * @name $rootScope.Scope#$countWatchers
+ * @module ngMock
+ * @description
+ * Counts all the watchers of direct and indirect child scopes of the current scope.
+ *
+ * The watchers of the current scope are included in the count and so are all the watchers of
+ * isolate child scopes.
+ *
+ * @returns {number} Total number of watchers.
+ */
+ function countWatchers() {
+ // jshint validthis: true
+ var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
+ var pendingChildHeads = [this.$$childHead];
+ var currentScope;
+
+ while (pendingChildHeads.length) {
+ currentScope = pendingChildHeads.shift();
+
+ while (currentScope) {
+ count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
+ pendingChildHeads.push(currentScope.$$childHead);
+ currentScope = currentScope.$$nextSibling;
+ }
+ }
+
+ return count;
+ }
+}];
+
+
+!(function(jasmineOrMocha) {
+
+ if (!jasmineOrMocha) {
+ return;
+ }
+
+ var currentSpec = null,
+ injectorState = new InjectorState(),
+ annotatedFunctions = [],
+ wasInjectorCreated = function() {
+ return !!currentSpec;
+ };
+
+ angular.mock.$$annotate = angular.injector.$$annotate;
+ angular.injector.$$annotate = function(fn) {
+ if (typeof fn === 'function' && !fn.$inject) {
+ annotatedFunctions.push(fn);
+ }
+ return angular.mock.$$annotate.apply(this, arguments);
+ };
+
+ /**
+ * @ngdoc function
+ * @name angular.mock.module
+ * @description
+ *
+ * *NOTE*: This function is also published on window for easy access.
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
+ *
+ * This function registers a module configuration code. It collects the configuration information
+ * which will be used when the injector is created by {@link angular.mock.inject inject}.
+ *
+ * See {@link angular.mock.inject inject} for usage example
+ *
+ * @param {...(string|Function|Object)} fns any number of modules which are represented as string
+ * aliases or as anonymous module initialization functions. The modules are used to
+ * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
+ * object literal is passed each key-value pair will be registered on the module via
+ * {@link auto.$provide $provide}.value, the key being the string name (or token) to associate
+ * with the value on the injector.
+ */
+ var module = window.module = angular.mock.module = function() {
+ var moduleFns = Array.prototype.slice.call(arguments, 0);
+ return wasInjectorCreated() ? workFn() : workFn;
+ /////////////////////
+ function workFn() {
+ if (currentSpec.$injector) {
+ throw new Error('Injector already created, can not register a module!');
+ } else {
+ var fn, modules = currentSpec.$modules || (currentSpec.$modules = []);
+ angular.forEach(moduleFns, function(module) {
+ if (angular.isObject(module) && !angular.isArray(module)) {
+ fn = ['$provide', function($provide) {
+ angular.forEach(module, function(value, key) {
+ $provide.value(key, value);
+ });
+ }];
+ } else {
+ fn = module;
+ }
+ if (currentSpec.$providerInjector) {
+ currentSpec.$providerInjector.invoke(fn);
+ } else {
+ modules.push(fn);
+ }
+ });
+ }
+ }
+ };
+
+ module.$$beforeAllHook = (window.before || window.beforeAll);
+ module.$$afterAllHook = (window.after || window.afterAll);
+
+ // purely for testing ngMock itself
+ module.$$currentSpec = function(to) {
+ if (arguments.length === 0) return to;
+ currentSpec = to;
+ };
+
+ /**
+ * @ngdoc function
+ * @name angular.mock.module.sharedInjector
+ * @description
+ *
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
+ *
+ * This function ensures a single injector will be used for all tests in a given describe context.
+ * This contrasts with the default behaviour where a new injector is created per test case.
+ *
+ * Use sharedInjector when you want to take advantage of Jasmine's `beforeAll()`, or mocha's
+ * `before()` methods. Call `module.sharedInjector()` before you setup any other hooks that
+ * will create (i.e call `module()`) or use (i.e call `inject()`) the injector.
+ *
+ * You cannot call `sharedInjector()` from within a context already using `sharedInjector()`.
+ *
+ * ##Â Example
+ *
+ * Typically beforeAll is used to make many assertions about a single operation. This can
+ * cut down test run-time as the test setup doesn't need to be re-run, and enabling focussed
+ * tests each with a single assertion.
+ *
+ * ```js
+ * describe("Deep Thought", function() {
+ *
+ * module.sharedInjector();
+ *
+ * beforeAll(module("UltimateQuestion"));
+ *
+ * beforeAll(inject(function(DeepThought) {
+ * expect(DeepThought.answer).toBeUndefined();
+ * DeepThought.generateAnswer();
+ * }));
+ *
+ * it("has calculated the answer correctly", inject(function(DeepThought) {
+ * // Because of sharedInjector, we have access to the instance of the DeepThought service
+ * // that was provided to the beforeAll() hook. Therefore we can test the generated answer
+ * expect(DeepThought.answer).toBe(42);
+ * }));
+ *
+ * it("has calculated the answer within the expected time", inject(function(DeepThought) {
+ * expect(DeepThought.runTimeMillennia).toBeLessThan(8000);
+ * }));
+ *
+ * it("has double checked the answer", inject(function(DeepThought) {
+ * expect(DeepThought.absolutelySureItIsTheRightAnswer).toBe(true);
+ * }));
+ *
+ * });
+ *
+ * ```
+ */
+ module.sharedInjector = function() {
+ if (!(module.$$beforeAllHook && module.$$afterAllHook)) {
+ throw Error("sharedInjector() cannot be used unless your test runner defines beforeAll/afterAll");
+ }
+
+ var initialized = false;
+
+ module.$$beforeAllHook(function() {
+ if (injectorState.shared) {
+ injectorState.sharedError = Error("sharedInjector() cannot be called inside a context that has already called sharedInjector()");
+ throw injectorState.sharedError;
+ }
+ initialized = true;
+ currentSpec = this;
+ injectorState.shared = true;
+ });
+
+ module.$$afterAllHook(function() {
+ if (initialized) {
+ injectorState = new InjectorState();
+ module.$$cleanup();
+ } else {
+ injectorState.sharedError = null;
+ }
+ });
+ };
+
+ module.$$beforeEach = function() {
+ if (injectorState.shared && currentSpec && currentSpec != this) {
+ var state = currentSpec;
+ currentSpec = this;
+ angular.forEach(["$injector","$modules","$providerInjector", "$injectorStrict"], function(k) {
+ currentSpec[k] = state[k];
+ state[k] = null;
+ });
+ } else {
+ currentSpec = this;
+ originalRootElement = null;
+ annotatedFunctions = [];
+ }
+ };
+
+ module.$$afterEach = function() {
+ if (injectorState.cleanupAfterEach()) {
+ module.$$cleanup();
+ }
+ };
+
+ module.$$cleanup = function() {
+ var injector = currentSpec.$injector;
+
+ annotatedFunctions.forEach(function(fn) {
+ delete fn.$inject;
+ });
+
+ angular.forEach(currentSpec.$modules, function(module) {
+ if (module && module.$$hashKey) {
+ module.$$hashKey = undefined;
+ }
+ });
+
+ currentSpec.$injector = null;
+ currentSpec.$modules = null;
+ currentSpec.$providerInjector = null;
+ currentSpec = null;
+
+ if (injector) {
+ // Ensure `$rootElement` is instantiated, before checking `originalRootElement`
+ var $rootElement = injector.get('$rootElement');
+ var rootNode = $rootElement && $rootElement[0];
+ var cleanUpNodes = !originalRootElement ? [] : [originalRootElement[0]];
+ if (rootNode && (!originalRootElement || rootNode !== originalRootElement[0])) {
+ cleanUpNodes.push(rootNode);
+ }
+ angular.element.cleanData(cleanUpNodes);
+
+ // Ensure `$destroy()` is available, before calling it
+ // (a mocked `$rootScope` might not implement it (or not even be an object at all))
+ var $rootScope = injector.get('$rootScope');
+ if ($rootScope && $rootScope.$destroy) $rootScope.$destroy();
+ }
+
+ // clean up jquery's fragment cache
+ angular.forEach(angular.element.fragments, function(val, key) {
+ delete angular.element.fragments[key];
+ });
+
+ MockXhr.$$lastInstance = null;
+
+ angular.forEach(angular.callbacks, function(val, key) {
+ delete angular.callbacks[key];
+ });
+ angular.callbacks.$$counter = 0;
+ };
+
+ (window.beforeEach || window.setup)(module.$$beforeEach);
+ (window.afterEach || window.teardown)(module.$$afterEach);
+
+ /**
+ * @ngdoc function
+ * @name angular.mock.inject
+ * @description
+ *
+ * *NOTE*: This function is also published on window for easy access.
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
+ *
+ * The inject function wraps a function into an injectable function. The inject() creates new
+ * instance of {@link auto.$injector $injector} per test, which is then used for
+ * resolving references.
+ *
+ *
+ * ## Resolving References (Underscore Wrapping)
+ * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
+ * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
+ * that is declared in the scope of the `describe()` block. Since we would, most likely, want
+ * the variable to have the same name of the reference we have a problem, since the parameter
+ * to the `inject()` function would hide the outer variable.
+ *
+ * To help with this, the injected parameters can, optionally, be enclosed with underscores.
+ * These are ignored by the injector when the reference name is resolved.
+ *
+ * For example, the parameter `_myService_` would be resolved as the reference `myService`.
+ * Since it is available in the function body as _myService_, we can then assign it to a variable
+ * defined in an outer scope.
+ *
+ * ```
+ * // Defined out reference variable outside
+ * var myService;
+ *
+ * // Wrap the parameter in underscores
+ * beforeEach( inject( function(_myService_){
+ * myService = _myService_;
+ * }));
+ *
+ * // Use myService in a series of tests.
+ * it('makes use of myService', function() {
+ * myService.doStuff();
+ * });
+ *
+ * ```
+ *
+ * See also {@link angular.mock.module angular.mock.module}
+ *
+ * ## Example
+ * Example of what a typical jasmine tests looks like with the inject method.
+ * ```js
+ *
+ * angular.module('myApplicationModule', [])
+ * .value('mode', 'app')
+ * .value('version', 'v1.0.1');
+ *
+ *
+ * describe('MyApp', function() {
+ *
+ * // You need to load modules that you want to test,
+ * // it loads only the "ng" module by default.
+ * beforeEach(module('myApplicationModule'));
+ *
+ *
+ * // inject() is used to inject arguments of all given functions
+ * it('should provide a version', inject(function(mode, version) {
+ * expect(version).toEqual('v1.0.1');
+ * expect(mode).toEqual('app');
+ * }));
+ *
+ *
+ * // The inject and module method can also be used inside of the it or beforeEach
+ * it('should override a version and test the new version is injected', function() {
+ * // module() takes functions or strings (module aliases)
+ * module(function($provide) {
+ * $provide.value('version', 'overridden'); // override version here
+ * });
+ *
+ * inject(function(version) {
+ * expect(version).toEqual('overridden');
+ * });
+ * });
+ * });
+ *
+ * ```
+ *
+ * @param {...Function} fns any number of functions which will be injected using the injector.
+ */
+
+
+
+ var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
+ this.message = e.message;
+ this.name = e.name;
+ if (e.line) this.line = e.line;
+ if (e.sourceId) this.sourceId = e.sourceId;
+ if (e.stack && errorForStack)
+ this.stack = e.stack + '\n' + errorForStack.stack;
+ if (e.stackArray) this.stackArray = e.stackArray;
+ };
+ ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
+
+ window.inject = angular.mock.inject = function() {
+ var blockFns = Array.prototype.slice.call(arguments, 0);
+ var errorForStack = new Error('Declaration Location');
+ // IE10+ and PhanthomJS do not set stack trace information, until the error is thrown
+ if (!errorForStack.stack) {
+ try {
+ throw errorForStack;
+ } catch (e) {}
+ }
+ return wasInjectorCreated() ? workFn.call(currentSpec) : workFn;
+ /////////////////////
+ function workFn() {
+ var modules = currentSpec.$modules || [];
+ var strictDi = !!currentSpec.$injectorStrict;
+ modules.unshift(['$injector', function($injector) {
+ currentSpec.$providerInjector = $injector;
+ }]);
+ modules.unshift('ngMock');
+ modules.unshift('ng');
+ var injector = currentSpec.$injector;
+ if (!injector) {
+ if (strictDi) {
+ // If strictDi is enabled, annotate the providerInjector blocks
+ angular.forEach(modules, function(moduleFn) {
+ if (typeof moduleFn === "function") {
+ angular.injector.$$annotate(moduleFn);
+ }
+ });
+ }
+ injector = currentSpec.$injector = angular.injector(modules, strictDi);
+ currentSpec.$injectorStrict = strictDi;
+ }
+ for (var i = 0, ii = blockFns.length; i < ii; i++) {
+ if (currentSpec.$injectorStrict) {
+ // If the injector is strict / strictDi, and the spec wants to inject using automatic
+ // annotation, then annotate the function here.
+ injector.annotate(blockFns[i]);
+ }
+ try {
+ /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
+ injector.invoke(blockFns[i] || angular.noop, this);
+ /* jshint +W040 */
+ } catch (e) {
+ if (e.stack && errorForStack) {
+ throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
+ }
+ throw e;
+ } finally {
+ errorForStack = null;
+ }
+ }
+ }
+ };
+
+
+ angular.mock.inject.strictDi = function(value) {
+ value = arguments.length ? !!value : true;
+ return wasInjectorCreated() ? workFn() : workFn;
+
+ function workFn() {
+ if (value !== currentSpec.$injectorStrict) {
+ if (currentSpec.$injector) {
+ throw new Error('Injector already created, can not modify strict annotations');
+ } else {
+ currentSpec.$injectorStrict = value;
+ }
+ }
+ }
+ };
+
+ function InjectorState() {
+ this.shared = false;
+ this.sharedError = null;
+
+ this.cleanupAfterEach = function() {
+ return !this.shared || this.sharedError;
+ };
+ }
+})(window.jasmine || window.mocha);
+
+
+})(window, window.angular);
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/bower.json
new file mode 100644
index 0000000000..3a3d60a6b0
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/bower.json
@@ -0,0 +1,10 @@
+{
+ "name": "angular-mocks",
+ "version": "1.5.8",
+ "license": "MIT",
+ "main": "./angular-mocks.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.5.8"
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngAnimateMock.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngAnimateMock.js
new file mode 100644
index 0000000000..6f99e62ef6
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngAnimateMock.js
@@ -0,0 +1,2 @@
+require('./angular-mocks');
+module.exports = 'ngAnimateMock';
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngMock.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngMock.js
new file mode 100644
index 0000000000..7944de7d5b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngMock.js
@@ -0,0 +1,2 @@
+require('./angular-mocks');
+module.exports = 'ngMock';
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngMockE2E.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngMockE2E.js
new file mode 100644
index 0000000000..fc2e539dbd
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/ngMockE2E.js
@@ -0,0 +1,2 @@
+require('./angular-mocks');
+module.exports = 'ngMockE2E';
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/package.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/package.json
new file mode 100644
index 0000000000..631e187985
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-mocks/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "angular-mocks",
+ "version": "1.5.8",
+ "description": "AngularJS mocks for testing",
+ "main": "angular-mocks.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/angular/angular.js.git"
+ },
+ "keywords": [
+ "angular",
+ "framework",
+ "browser",
+ "mocks",
+ "testing",
+ "client-side"
+ ],
+ "author": "Angular Core Team ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/angular/angular.js/issues"
+ },
+ "homepage": "http://angularjs.org",
+ "jspm": {
+ "shim": {
+ "angular-mocks": {
+ "deps": ["angular"]
+ }
+ }
+ }
+}
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/.bower.json b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/.bower.json
new file mode 100644
index 0000000000..74c6ac5972
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/.bower.json
@@ -0,0 +1,20 @@
+{
+ "name": "angular-resource",
+ "version": "1.5.8",
+ "license": "MIT",
+ "main": "./angular-resource.js",
+ "ignore": [],
+ "dependencies": {
+ "angular": "1.5.8"
+ },
+ "homepage": "https://github.com/angular/bower-angular-resource",
+ "_release": "1.5.8",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.5.8",
+ "commit": "26254617a7f9716d80bc2e32117585f33480a4e3"
+ },
+ "_source": "https://github.com/angular/bower-angular-resource.git",
+ "_target": "1.5.8",
+ "_originalSource": "angular-resource"
+}
\ No newline at end of file
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/LICENSE.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/LICENSE.md
new file mode 100644
index 0000000000..2c395eef1b
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Angular
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/README.md b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/README.md
new file mode 100644
index 0000000000..f3bd119ce2
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/README.md
@@ -0,0 +1,68 @@
+# packaged angular-resource
+
+This repo is for distribution on `npm` and `bower`. The source for this module is in the
+[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngResource).
+Please file issues and pull requests against that repo.
+
+## Install
+
+You can install this package either with `npm` or with `bower`.
+
+### npm
+
+```shell
+npm install angular-resource
+```
+
+Then add `ngResource` as a dependency for your app:
+
+```javascript
+angular.module('myApp', [require('angular-resource')]);
+```
+
+### bower
+
+```shell
+bower install angular-resource
+```
+
+Add a `
+```
+
+Then add `ngResource` as a dependency for your app:
+
+```javascript
+angular.module('myApp', ['ngResource']);
+```
+
+## Documentation
+
+Documentation is available on the
+[AngularJS docs site](http://docs.angularjs.org/api/ngResource).
+
+## License
+
+The MIT License
+
+Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/angular-resource.js b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/angular-resource.js
new file mode 100644
index 0000000000..e8bb301465
--- /dev/null
+++ b/jhipster/jhipster-microservice/gateway-app/src/main/webapp/bower_components/angular-resource/angular-resource.js
@@ -0,0 +1,863 @@
+/**
+ * @license AngularJS v1.5.8
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular) {'use strict';
+
+var $resourceMinErr = angular.$$minErr('$resource');
+
+// Helper functions and regex to lookup a dotted path on an object
+// stopping at undefined/null. The path must be composed of ASCII
+// identifiers (just like $parse)
+var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
+
+function isValidDottedPath(path) {
+ return (path != null && path !== '' && path !== 'hasOwnProperty' &&
+ MEMBER_NAME_REGEX.test('.' + path));
+}
+
+function lookupDottedPath(obj, path) {
+ if (!isValidDottedPath(path)) {
+ throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
+ }
+ var keys = path.split('.');
+ for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
+ var key = keys[i];
+ obj = (obj !== null) ? obj[key] : undefined;
+ }
+ return obj;
+}
+
+/**
+ * Create a shallow copy of an object and clear other fields from the destination
+ */
+function shallowClearAndCopy(src, dst) {
+ dst = dst || {};
+
+ angular.forEach(dst, function(value, key) {
+ delete dst[key];
+ });
+
+ for (var key in src) {
+ if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
+ dst[key] = src[key];
+ }
+ }
+
+ return dst;
+}
+
+/**
+ * @ngdoc module
+ * @name ngResource
+ * @description
+ *
+ * # ngResource
+ *
+ * The `ngResource` module provides interaction support with RESTful services
+ * via the $resource service.
+ *
+ *
+ *
+ *
+ * See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
+ */
+
+/**
+ * @ngdoc provider
+ * @name $resourceProvider
+ *
+ * @description
+ *
+ * Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
+ * service.
+ *
+ * ## Dependencies
+ * Requires the {@link ngResource } module to be installed.
+ *
+ */
+
+/**
+ * @ngdoc service
+ * @name $resource
+ * @requires $http
+ * @requires ng.$log
+ * @requires $q
+ * @requires ng.$timeout
+ *
+ * @description
+ * A factory which creates a resource object that lets you interact with
+ * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
+ *
+ * The returned resource object has action methods which provide high-level behaviors without
+ * the need to interact with the low level {@link ng.$http $http} service.
+ *
+ * Requires the {@link ngResource `ngResource`} module to be installed.
+ *
+ * By default, trailing slashes will be stripped from the calculated URLs,
+ * which can pose problems with server backends that do not expect that
+ * behavior. This can be disabled by configuring the `$resourceProvider` like
+ * this:
+ *
+ * ```js
+ app.config(['$resourceProvider', function($resourceProvider) {
+ // Don't strip trailing slashes from calculated URLs
+ $resourceProvider.defaults.stripTrailingSlashes = false;
+ }]);
+ * ```
+ *
+ * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
+ * `/user/:username`. If you are using a URL with a port number (e.g.
+ * `http://example.com:8080/api`), it will be respected.
+ *
+ * If you are using a url with a suffix, just add the suffix, like this:
+ * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
+ * or even `$resource('http://example.com/resource/:resource_id.:format')`
+ * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
+ * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
+ * can escape it with `/\.`.
+ *
+ * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
+ * `actions` methods. If a parameter value is a function, it will be called every time
+ * a param value needs to be obtained for a request (unless the param was overridden). The function
+ * will be passed the current data value as an argument.
+ *
+ * Each key value in the parameter object is first bound to url template if present and then any
+ * excess keys are appended to the url search query after the `?`.
+ *
+ * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
+ * URL `/path/greet?salutation=Hello`.
+ *
+ * If the parameter value is prefixed with `@`, then the value for that parameter will be
+ * extracted from the corresponding property on the `data` object (provided when calling a
+ * "non-GET" action method).
+ * For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
+ * `someParam` will be `data.someProp`.
+ * Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
+ * method that does not accept a request body)
+ *
+ * @param {Object.