diff --git a/.gitignore b/.gitignore index a61d2be..ef70fbb 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ shardis-api/target/ shardis-ui/target/ ### node +npm-debug.log shardis-ui/etc/ shardis-ui/node/ shardis-ui/node_modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 70a5056..c2af248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,20 @@ -## Changelog +## CHANGELOG: + +### 1.8.0 (30.06.2016) +* Better directory structure for angular components +* Dependency updates +* Updated Angular to rc.2 +* Migrate awesome-ts-loader to ts-loader due to problems with karma +* Migrate to router 3 +* Lazy routes with modified version of AngularClass webpack resolver +* Updated Angular to rc.3 +* Added 404 page +* Much cleaner webpack configuration +* Role based visibility of menu items +* CRUD example for authenticated users +* Route access for selected roles (guards) +* Switched to new Angular Forms +* Updated angular2-material to 2.0.0-alpha.6 ### 1.7.0 (08.06.2016) * Added npm-check-updates as dev dependency diff --git a/README.md b/README.md index 1ba2d26..4295803 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,22 @@ -# Spring Boot Angular 2 starter +# SPRING BOOT ANGULAR 2 STARTER + +## PROJECT DESCRIPTION: + +This sample project demonstrates how to integrate Spring Boot application with Angular 2. +It could be used as base for your future Angular2 Java projects. +Node and npm are installed as part of maven build. Also webpack build and karma tests runs with maven. +It also uses Spring Cloud microservices and Spring Security with Oauth2 JWT tokens. -Originally based on great [AngularClass](https://github.com/AngularClass) webpack starter ## FEATURES: * Build and testing process integrated with maven * Spring cloud microservices with Zull and Eureka * Spring security Oauth2 integration * Spring Boot application in development mode use resources from webpack dev server -* scss support -* Angular material -* font-awesome - +* Scss support +* Angular material integration +* Font-awesome integration +* Lazy loaded routes ## PRODUCTION USAGE: @@ -29,28 +35,28 @@ Then build and run discovery server: ``` cd shardis-discovery/ mvn clean package -java -jar ./target/shardis-discovery-1.7.0.jar +java -jar ./target/shardis-discovery-1.8.0.jar ``` Then build and run api server: ``` cd shardis-api/ mvn clean package -java -jar ./target/shardis-api-1.7.0.jar +java -jar ./target/shardis-api-1.8.0.jar ``` Then build and run auth server: ``` cd shardis-auth/ mvn clean package -java -jar ./target/shardis-auth-1.7.0.jar +java -jar ./target/shardis-auth-1.8.0.jar ``` Then build and run ui server: ``` cd shardis-api/ mvn clean package -java -jar ./target/shardis-ui-1.7.0.jar +java -jar ./target/shardis-ui-1.8.0.jar ``` @@ -112,7 +118,23 @@ cd shardis-ui npm run e2e ``` -## Changelog +## CHANGELOG: + +### 1.8.0 (30.06.2016) +* Better directory structure for angular components +* Dependency updates +* Updated Angular to rc.2 +* Migrate awesome-ts-loader to ts-loader due to problems with karma +* Migrate to router 3 +* Lazy routes with modified version of AngularClass webpack resolver +* Updated Angular to rc.3 +* Added 404 page +* Much cleaner webpack configuration +* Role based visibility of menu items +* CRUD example for authenticated users +* Route access for selected roles (guards) +* Switched to new Angular Forms +* Updated angular2-material to 2.0.0-alpha.6 ### 1.7.0 (08.06.2016) * Added npm-check-updates as dev dependency @@ -141,10 +163,4 @@ npm run e2e * Fixed webpack validator custom schema config * Some fixes from AngularClass starter -### 1.5.0 (13.05.2016) -* Updated Spring Boot to 1.3.4 -* Updated Spring Boot to 1.3.5 -* Added Spring IO Platform dependency -* Various bugfixes - [show full changelog](CHANGELOG.md) diff --git a/pom.xml b/pom.xml index 59ec938..b7962e3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,16 +5,16 @@ com.shardis shardis - 1.7.0 + 1.8.0 pom shardis shardis - shardis-parent + shardis-parent shardis-common - shardis-discovery + shardis-discovery shardis-auth shardis-ui shardis-api diff --git a/shardis-api/pom.xml b/shardis-api/pom.xml index 717fb8f..a710e25 100644 --- a/shardis-api/pom.xml +++ b/shardis-api/pom.xml @@ -3,8 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + com.shardis shardis-api - 1.7.0 + 1.8.0 jar shardis-api @@ -13,7 +14,7 @@ com.shardis shardis-parent - 1.7.0 + 1.8.0 diff --git a/shardis-api/src/main/java/com/shardis/ShardisApiApplication.java b/shardis-api/src/main/java/com/shardis/ShardisApiApplication.java index 983f1de..c79c0ab 100644 --- a/shardis-api/src/main/java/com/shardis/ShardisApiApplication.java +++ b/shardis-api/src/main/java/com/shardis/ShardisApiApplication.java @@ -4,6 +4,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; /** diff --git a/shardis-api/src/main/java/com/shardis/api/config/JpaConfig.java b/shardis-api/src/main/java/com/shardis/api/config/JpaConfig.java new file mode 100644 index 0000000..94c950b --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/config/JpaConfig.java @@ -0,0 +1,19 @@ +package com.shardis.api.config; + +import com.shardis.api.domain.user.User; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.AuditorAware; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +/** + * Created by Tomasz Kucharzyk + */ +@Configuration +@EnableJpaAuditing(auditorAwareRef = "auditorProvider") +public class JpaConfig { + @Bean(name = "auditorProvider" ) + public AuditorAware auditorProvider() { + return () -> null; + } +} diff --git a/shardis-api/src/main/java/com/shardis/api/controllers/rest/BlogPostRestController.java b/shardis-api/src/main/java/com/shardis/api/controllers/rest/BlogPostRestController.java new file mode 100644 index 0000000..c75d956 --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/controllers/rest/BlogPostRestController.java @@ -0,0 +1,61 @@ +package com.shardis.api.controllers.rest; + + +import com.google.common.collect.Lists; +import com.shardis.api.domain.blog.BlogPost; +import com.shardis.api.domain.blog.BlogPostRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.PostConstruct; +import java.util.List; + +/** + * Created by Tomasz Kucharzyk + */ + +@Transactional +@RestController +@RequestMapping("/posts") +@PreAuthorize(value = "hasRole('ROLE_USER')") +@Slf4j +public class BlogPostRestController { + + @Autowired + private BlogPostRepository blogPostRepository; + + @PostConstruct + public void fillData() { + List posts = this.getPosts(); + if (posts.isEmpty()) { + for (int i = 1; i <= 10; i++) { + BlogPost post = new BlogPost("Sample blog post title #" + i, "Sample blog post content #" + i); + blogPostRepository.save(post); + log.warn(post.toString()); + } + } + } + + @RequestMapping(value = "/", method = RequestMethod.GET) + public List getPosts() { + return Lists.newArrayList(blogPostRepository.findAll()); + } + + @RequestMapping(value = "/", method = RequestMethod.POST) + public BlogPost savePost(@RequestBody BlogPost blogPost) { + return blogPostRepository.save(blogPost); + } + + @RequestMapping(value = "/{postId}", method = RequestMethod.DELETE) + public BlogPost deletePost(@PathVariable("postId") Long postId) { + BlogPost blogPost = blogPostRepository.findOne(postId); + if (blogPost != null) { + blogPostRepository.delete(blogPost); + } + return blogPost; + } + +} diff --git a/shardis-api/src/main/java/com/shardis/api/domain/base/AuditedEntity.java b/shardis-api/src/main/java/com/shardis/api/domain/base/AuditedEntity.java new file mode 100644 index 0000000..9a4e4eb --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/domain/base/AuditedEntity.java @@ -0,0 +1,46 @@ +package com.shardis.api.domain.base; + +import com.shardis.api.domain.user.User; +import lombok.Data; +import lombok.EqualsAndHashCode; +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 javax.persistence.EntityListeners; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; +import javax.persistence.Version; +import java.time.LocalDateTime; + +/** + * Created by Tomasz Kucharzyk + */ +@Audited +@Data +@EqualsAndHashCode(callSuper = true) +@EntityListeners(AuditingEntityListener.class) +@MappedSuperclass +public class AuditedEntity extends BaseEntity { + + @CreatedDate + protected LocalDateTime createdDate; + + @CreatedBy + @ManyToOne + protected User createdBy; + + @LastModifiedDate + protected LocalDateTime updatedDate; + + @LastModifiedBy + @ManyToOne + protected User updatedBy; + + @Version + protected Long version; + +} diff --git a/shardis-api/src/main/java/com/shardis/api/domain/base/BaseEntity.java b/shardis-api/src/main/java/com/shardis/api/domain/base/BaseEntity.java new file mode 100644 index 0000000..7d3921d --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/domain/base/BaseEntity.java @@ -0,0 +1,21 @@ +package com.shardis.api.domain.base; + +import lombok.Data; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +/** + * Created by Tomasz Kucharzyk + */ +@Data +@MappedSuperclass +public class BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + protected Long id; + +} diff --git a/shardis-api/src/main/java/com/shardis/api/domain/blog/BlogPost.java b/shardis-api/src/main/java/com/shardis/api/domain/blog/BlogPost.java new file mode 100644 index 0000000..dad0159 --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/domain/blog/BlogPost.java @@ -0,0 +1,44 @@ +package com.shardis.api.domain.blog; + +import com.shardis.api.domain.base.AuditedEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * Created by Tomasz Kucharzyk + */ + +@Entity +@Table(name = "BLOG_POST") +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class BlogPost extends AuditedEntity { + + + private String title; + private String content; + + public BlogPost(String title, String content) { + this.title = title; + this.content = content; + } + + @Override + public String toString() { + return "BlogPost{" + + "id=" + id + + ", createdDate=" + createdDate + + ", createdBy=" + createdBy + + ", updatedDate=" + updatedDate + + ", updatedBy=" + updatedBy + + ", version=" + version + + ", title='" + title + '\'' + + ", content='" + content + '\'' + + '}'; + } +} diff --git a/shardis-api/src/main/java/com/shardis/api/domain/blog/BlogPostRepository.java b/shardis-api/src/main/java/com/shardis/api/domain/blog/BlogPostRepository.java new file mode 100644 index 0000000..ca09b44 --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/domain/blog/BlogPostRepository.java @@ -0,0 +1,11 @@ +package com.shardis.api.domain.blog; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by Tomasz Kucharzyk + */ +@Repository +public interface BlogPostRepository extends CrudRepository { +} diff --git a/shardis-api/src/main/java/com/shardis/api/domain/user/User.java b/shardis-api/src/main/java/com/shardis/api/domain/user/User.java new file mode 100644 index 0000000..d75a862 --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/domain/user/User.java @@ -0,0 +1,23 @@ +package com.shardis.api.domain.user; + +import com.shardis.api.domain.base.AuditedEntity; +import com.shardis.api.domain.base.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.hibernate.envers.Audited; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; + +/** + * Created by Tomasz Kucharzyk + */ +@Entity +@Table(name = "USER") +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class User extends AuditedEntity { + +} diff --git a/shardis-api/src/main/java/com/shardis/api/domain/user/UserRepository.java b/shardis-api/src/main/java/com/shardis/api/domain/user/UserRepository.java new file mode 100644 index 0000000..2f29628 --- /dev/null +++ b/shardis-api/src/main/java/com/shardis/api/domain/user/UserRepository.java @@ -0,0 +1,11 @@ +package com.shardis.api.domain.user; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by Tomasz Kucharzyk + */ +@Repository +public interface UserRepository extends CrudRepository { +} diff --git a/shardis-api/src/main/resources/application.yml b/shardis-api/src/main/resources/application.yml index cdd7103..41fc984 100644 --- a/shardis-api/src/main/resources/application.yml +++ b/shardis-api/src/main/resources/application.yml @@ -7,6 +7,9 @@ server: spring: application.name: shardis-api profiles.active: prod +# jacksonm + jackson.serialization: + write_dates_as_timestamps: false # EUREKA eureka: instance: diff --git a/shardis-auth/pom.xml b/shardis-auth/pom.xml index 8bdaced..1a66622 100644 --- a/shardis-auth/pom.xml +++ b/shardis-auth/pom.xml @@ -3,8 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + com.shardis shardis-auth - 1.7.0 + 1.8.0 jar shardis-auth @@ -13,7 +14,7 @@ com.shardis shardis-parent - 1.7.0 + 1.8.0 diff --git a/shardis-auth/src/main/java/com/shardis/auth/config/OAuth2AuthorizationConfig.java b/shardis-auth/src/main/java/com/shardis/auth/config/OAuth2AuthorizationConfig.java index a3c4f30..77bba93 100644 --- a/shardis-auth/src/main/java/com/shardis/auth/config/OAuth2AuthorizationConfig.java +++ b/shardis-auth/src/main/java/com/shardis/auth/config/OAuth2AuthorizationConfig.java @@ -41,8 +41,8 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception { .withClient("acme") .secret("acmesecret") .authorities("ROLE_TRUSTED_CLIENT") - .authorizedGrantTypes("implicit","authorization_code", "refresh_token","password") - .accessTokenValiditySeconds(360) + .authorizedGrantTypes("implicit", "authorization_code", "refresh_token", "password") + .accessTokenValiditySeconds(600) .scopes("openid") .autoApprove(true); } diff --git a/shardis-common/pom.xml b/shardis-common/pom.xml index dd41db4..0868823 100644 --- a/shardis-common/pom.xml +++ b/shardis-common/pom.xml @@ -3,8 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + com.shardis shardis-common - 1.7.0 + 1.8.0 jar shardis-common @@ -13,15 +14,27 @@ com.shardis shardis-parent - 1.7.0 + 1.8.0 + + org.hibernate + hibernate-envers + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + org.springframework.boot spring-boot-starter-data-jpa + + org.springframework.boot + spring-boot-starter-logging + org.springframework.boot spring-boot-starter-web diff --git a/shardis-discovery/pom.xml b/shardis-discovery/pom.xml index 669286e..08093dd 100644 --- a/shardis-discovery/pom.xml +++ b/shardis-discovery/pom.xml @@ -3,8 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + com.shardis shardis-discovery - 1.7.0 + 1.8.0 jar shardis-discovery @@ -13,7 +14,7 @@ com.shardis shardis-parent - 1.7.0 + 1.8.0 diff --git a/shardis-parent/pom.xml b/shardis-parent/pom.xml index 82be3c3..60381cf 100644 --- a/shardis-parent/pom.xml +++ b/shardis-parent/pom.xml @@ -5,7 +5,7 @@ com.shardis shardis-parent - 1.7.0 + 1.8.0 pom shardis-parent @@ -22,6 +22,7 @@ 1.4.1 Brixton.RELEASE 2.0.5.RELEASE + 1.3.5.RELEASE @@ -127,6 +128,11 @@ + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + diff --git a/shardis-ui/config/webpack.commons.js b/shardis-ui/config/webpack.commons.js new file mode 100644 index 0000000..5e7849b --- /dev/null +++ b/shardis-ui/config/webpack.commons.js @@ -0,0 +1,128 @@ +var path = require('path'); +var autoprefixer = require('autoprefixer'); +var validateWebpackConfig = require('webpack-validator'); +var Joi = require('joi'); + +// Environments +const ENV = { + DEV: 'development', + PROD: 'production', + TEST: 'test' +}; + +var absolutePath = function (args) { + args = Array.prototype.slice.call(arguments, 0); + return path.join.apply(path, [__dirname].concat(args)); +}; + +function validateConfig(config) { + var customSchema = Joi.object({ + // this would just allow the property and doesn't perform any additional validation + sassLoader: Joi.any(), + htmlLoader: Joi.any() + }); + + return validateWebpackConfig(config, {schemaExtension: customSchema}); +} + +// Base configuration +var baseWebpackConfig = { + + // static data for index.html + metadata: { + baseUrl: '/' + }, + + resolve: { + extensions: ['', '.ts', '.js'], + cache: true, + root: absolutePath('../src/main/frontend') + }, + + cache: true, + debug: false, + + tslint: { + emitErrors: false, + failOnHint: false, + resourcePath: './src/main/frontend' + }, + + module: { + preLoaders: [ + { + test: /\.ts$/, + loader: 'tslint-loader', + exclude: [ + absolutePath('../node_modules') + ] + }, + { + test: /\.js$/, + loader: "source-map-loader", + exclude: [ + absolutePath('../node_modules/rxjs'), + absolutePath('../node_modules/@angular'), + absolutePath('../node_modules/@angular2-material'), + absolutePath('../node_modules/ng2-webstorage') + ] + } + ], + loaders: [ + { + test: /\.ts$/, + loader: 'ts' + }, + + { + test: /\.json$/, + loader: 'json' + }, + + { + test: /\.css$/, + loader: 'raw' + }, + + { + test: /\.html$/, + loader: 'raw', + exclude: [ + absolutePath('../src/main/frontend/index.html') + ] + }, + + { + test: /\.(png|jpg|gif|svg)/, + loader: 'url', + query: { + limit: 10000, + name: '[name].[ext]?[hash]' + } + }, + + { + test: /\.(woff|eot|ttf|woff(2)?|otf)/i, + loader: 'file-loader?[name].[ext]?[hash]' + } + + ] + }, + sassLoader: { + includePaths: [ + './node_modules/font-awesome/scss', + './node_modules/normalize.css' + ] + }, + postcss: [ + autoprefixer({browsers: ['last 2 versions'], remove: false}) + ], + plugins: [] +}; + +module.exports = { + absolutePath: absolutePath, + ENV: ENV, + baseWebpackConfig: baseWebpackConfig, + validateConfig: validateConfig +}; diff --git a/shardis-ui/config/webpack.config.factory.js b/shardis-ui/config/webpack.config.factory.js deleted file mode 100644 index 1801056..0000000 --- a/shardis-ui/config/webpack.config.factory.js +++ /dev/null @@ -1,394 +0,0 @@ -var path = require('path'); -var fs = require('fs'); -var webpack = require('webpack'); - -var validateWebpackConfig = require('webpack-validator'); -var Joi = require('joi'); - -var ProvidePlugin = require('webpack/lib/ProvidePlugin'); -var DefinePlugin = require('webpack/lib/DefinePlugin'); -var OccurenceOrderPlugin = require('webpack/lib/optimize/OccurenceOrderPlugin'); -var DedupePlugin = require('webpack/lib/optimize/DedupePlugin'); -var UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin'); -var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); -var WebpackMd5Hash = require('webpack-md5-hash'); -var CopyWebpackPlugin = require('copy-webpack-plugin'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin; -var ExtractTextPlugin = require("extract-text-webpack-plugin"); - -var autoprefixer = require('autoprefixer'); - -// Environments -const ENV = { - DEV: 'development', - PROD: 'production', - TEST: 'test' -}; - -// Base configuration -var baseWebpackConfig = { - - resolve: { - extensions: ['', '.ts', '.js'], - cache: true - }, - - cache: true, - debug: false, - - devtool: 'source-map', - - tslint: { - emitErrors: false, - failOnHint: false, - resourcePath: './src/main/frontend' - }, - - module: { - preLoaders: [ - { - test: /\.ts$/, - loader: 'tslint-loader', - exclude: [ - fullPathTo('../node_modules') - ] - }, - { - test: /\.js$/, - loader: "source-map-loader", - exclude: [ - fullPathTo('../node_modules/rxjs'), - fullPathTo('../node_modules/@angular'), - fullPathTo('../node_modules/@angular2-material'), - fullPathTo('../node_modules/ng2-webstorage') - ] - } - ], - loaders: [ - { - test: /\.ts$/, - loader: 'awesome-typescript' - }, - - { - test: /\.json$/, - loader: 'json' - }, - - { - test: /\.css$/, - loader: 'raw' - }, - - { - test: /\.html$/, - loader: 'raw', - exclude: [ - fullPathTo('../src/main/frontend/index.html') - ] - }, - - { - test: /\.(png|jpg|gif|svg)/, - loader: 'url', - query: { - limit: 10000, - name: '[name].[ext]?[hash]' - } - }, - - { - test: /\.(woff|eot|ttf|woff(2)?|otf)/i, - loader: 'file-loader?[name].[ext]?[hash]' - } - - ] - }, - sassLoader: { - includePaths: [ - './node_modules/font-awesome/scss', - './node_modules/normalize.css' - ] - }, - postcss: [ - autoprefixer({browsers: ['last 2 versions'], remove: false}) - ], - plugins: [ - new ForkCheckerPlugin() - ] -}; - -// Returns full path to given relative directory -function fullPathTo(args) { - args = Array.prototype.slice.call(arguments, 0); - return path.join.apply(path, [__dirname].concat(args)); -} - - -// Customize config for dev environment -function customizeForDev(config) { - - config.metadata.host = 'localhost'; - config.metadata.port = 3000; - - config.entry = { - 'polyfills': './src/main/frontend/polyfills.ts', - 'vendor': './src/main/frontend/vendor.ts', - 'main': './src/main/frontend/main.ts' - }; - - config.output = { - path: fullPathTo('../src/main/resources/static/'), - filename: '[name].bundle.js', - sourceMapFilename: '[name].map', - chunkFilename: '[id].chunk.js', - publicPath: 'http://localhost:3000/' - }; - - // Webpack Development Server config - config.devServer = { - port: config.metadata.port, - host: config.metadata.host, - contentBase: './src/main/frontend', - outputPath: './tmp/', - historyApiFallback: true, - watchOptions: { - aggregateTimeout: 300, - poll: 1000 - }, - quiet: false, - noInfo: false, - proxy: { - '/api/*': 'http://localhost:8080', - '/auth/*': 'http://localhost:8080' - } - }; - - // Saas config - config.module.loaders.push({ - test: /main\.scss$/, - loader: 'style!css?sourceMap!postcss!sass?sourceMap' - }); - - config.module.loaders.push({ - test: /\.scss$/, - loader: 'raw!postcss!sass', - exclude: [ - fullPathTo('../src/main/frontend/scss/main.scss') - ] - }); - - // Additional plugins - config.plugins.push(new webpack.optimize.OccurenceOrderPlugin(true)); - config.plugins.push(new CommonsChunkPlugin({ - name: ['main', 'vendor', 'polyfills'], - minChunks: Infinity - })); - config.plugins.push(new CopyWebpackPlugin([ - { - from: './src/main/frontend/assets', - to: 'assets' - } - ])); - var inlinedCss = fs.readFileSync('./src/main/frontend/css/inline.css', {encoding: 'utf8'}); - config.plugins.push(new HtmlWebpackPlugin({ - template: './src/main/frontend/index.html', - chunksSortMode: 'dependency', - minify: false, - inlineCss: '' - })); - -} - -// Customize config for production environment -function customizeForProd(config) { - config.resolve.cache = false; - config.cache = false; - - config.entry = { - 'polyfills': './src/main/frontend/polyfills.ts', - 'vendor': './src/main/frontend/vendor.ts', - 'main': './src/main/frontend/main.ts' - }; - - config.output = { - path: fullPathTo('../src/main/resources/static'), - filename: '[name].[chunkhash].bundle.js', - sourceMapFilename: '[name].[chunkhash].bundle.map', - chunkFilename: '[id].[chunkhash].chunk.js', - publicPath: '/' - }; - - config.tslint.emitErrors = true; - - // Saas config - - var extractCSS = new ExtractTextPlugin('[name].[contenthash].css'); - - config.module.loaders.push({ - test: /main\.scss$/, - loader: extractCSS.extract(['css?sourceMap', 'postcss', 'sass?sourceMap']) - }); - - config.module.loaders.push({ - test: /\.scss$/, - loader: 'raw!postcss!sass', - exclude: [ - fullPathTo('../src/main/frontend/scss/main.scss') - ] - }); - - config.plugins.push(extractCSS); - - - // additional plugins - config.plugins.push(new WebpackMd5Hash()); - config.plugins.push(new DedupePlugin()); - config.plugins.push(new OccurenceOrderPlugin(true)); - config.plugins.push(new CommonsChunkPlugin({ - name: ['main', 'vendor', 'polyfills'], - minChunks: Infinity - })); - config.plugins.push(new CopyWebpackPlugin([ - { - from: './src/main/frontend/assets', - to: 'assets' - } - ])); - var inlinedCss = fs.readFileSync('./src/main/frontend/css/inline.css', {encoding: 'utf8'}); - config.plugins.push(new HtmlWebpackPlugin({ - template: './src/main/frontend/index.html', - chunksSortMode: 'dependency', - minify: {minimize: true, removeComments: true, preserveLineBreaks: true, collapseWhitespace: true}, - inlineCss: '' - })); - config.plugins.push(new UglifyJsPlugin({ - // beautify: true, //debug - // mangle: false, //debug - // dead_code: false, //debug - // unused: false, //debug - // deadCode: false, //debug - // compress: { - // screw_ie8: true, - // keep_fnames: true, - // drop_debugger: false, - // dead_code: false, - // unused: false - // }, // debug - // comments: true, //debug - - beautify: false, //prod - - mangle: { - screw_ie8: true, - keep_fnames: true - }, //prod - - compress: { - screw_ie8: true - }, //prod - - comments: false //prod - })); - -// Teach html-minifier about Angular 2 syntax - config.htmlLoader = { - minimize: true, - removeAttributeQuotes: false, - caseSensitive: true, - customAttrSurround: [ - [/#/, /(?:)/], - [/\*/, /(?:)/], - [/\[?\(?/, /(?:)/] - ], - customAttrAssign: [/\)?\]?=/], - removeComments: true, - preserveLineBreaks: true, - collapseWhitespace: true - }; -} - -// Customize config for test environment -function customizeForTest(config) { - - config.devtool = 'inline-source-map'; - - config.stats = { - colors: true, - reasons: true - }; - - // Saas config - config.module.loaders.push({ - test: /main\.scss$/, - loader: 'style!css!sass' - }); - - config.module.loaders.push({ - test: /\.scss$/, - loader: 'raw!sass', - exclude: [ - fullPathTo('../src/main/frontend/scss/main.scss') - ] - }); - - config.module.postLoaders = [ - // instrument only testing sources with Istanbul - { - test: /\.(js|ts)$/, - include: fullPathTo('../src/main/frontend'), - loader: 'istanbul-instrumenter-loader', - exclude: [ - /\.(e2e|spec)\.ts$/, - /node_modules/ - ] - } - ]; - -} - -function customWebpackConfigValidation(config) { - var customSchema = Joi.object({ - // this would just allow the property and doesn't perform any additional validation - sassLoader: Joi.any(), - htmlLoader: Joi.any() - }); - - return validateWebpackConfig(config, {schemaExtension: customSchema}); -} - -// Create webpack config of given type -function createConfig(environment) { - - process.env.NODE_ENV = process.env.ENV = environment; - - var config = baseWebpackConfig; - - // static data for index.html - config.metadata = { - baseUrl: '/', - ENV: environment - }; - - config.plugins.push(new DefinePlugin({ - 'ENV': JSON.stringify(environment), - 'HMR': (ENV.DEV === environment) - })); - - switch (environment) { - case ENV.DEV: - customizeForDev(config); - break; - case ENV.PROD: - customizeForProd(config); - break; - case ENV.TEST: - customizeForTest(config); - break; - } - - return customWebpackConfigValidation(config); -} - -exports.createConfig = createConfig; diff --git a/shardis-ui/config/webpack.dev.config.js b/shardis-ui/config/webpack.dev.config.js index 7f2201c..d42ff5a 100644 --- a/shardis-ui/config/webpack.dev.config.js +++ b/shardis-ui/config/webpack.dev.config.js @@ -1,3 +1,97 @@ -var webpackConfigFactory = require('./webpack.config.factory.js'); +const fs = require('fs'); -module.exports = webpackConfigFactory.createConfig('development'); +const webpackMerge = require('webpack-merge'); +const DefinePlugin = require('webpack/lib/DefinePlugin'); +const webpack = require('webpack'); +const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +const {ENV, validateConfig, baseWebpackConfig, absolutePath} = require('./webpack.commons.js'); + + +var environment = process.env.NODE_ENV = process.env.ENV = ENV.DEV; + +var inlinedCss = fs.readFileSync('./src/main/frontend/css/inline.css', {encoding: 'utf8'}); + +module.exports = validateConfig(webpackMerge(baseWebpackConfig, { + devtool: 'cheap-module-eval-source-map', + metadata: { + ENV: environment, + host: 'localhost', + port: 3000 + }, + entry: { + 'polyfills': './src/main/frontend/polyfills.ts', + 'vendor': './src/main/frontend/vendor.ts', + 'main': './src/main/frontend/main.ts' + }, + output: { + path: absolutePath('../src/main/resources/static/'), + filename: '[name].bundle.js', + sourceMapFilename: '[name].map', + chunkFilename: '[id].chunk.js', + publicPath: 'http://localhost:3000/' + }, + // Webpack Development Server config + devServer: { + port: 3000, + host: 'localhost', + contentBase: './src/main/frontend', + outputPath: './tmp/', + historyApiFallback: true, + watchOptions: { + aggregateTimeout: 300, + poll: 1000 + }, + quiet: false, + noInfo: true, + proxy: { + '/api/*': 'http://localhost:8080', + '/auth/*': 'http://localhost:8080' + } + }, + module: { + loaders: [ + // Saas config + { + test: /main\.scss$/, + loader: 'style!css?sourceMap!postcss!sass?sourceMap' + }, + { + test: /\.scss$/, + loader: 'raw!postcss!sass', + exclude: [ + absolutePath('../src/main/frontend/scss/main.scss') + ] + } + ] + }, + tslint: { + emitErrors: false, + failOnHint: false + }, + plugins: [ + new DefinePlugin({ + 'ENV': JSON.stringify(environment), + 'HMR': (ENV.DEV === environment) + }), + new webpack.optimize.OccurenceOrderPlugin(true), + new CommonsChunkPlugin({ + name: ['vendor', 'polyfills'], + minChunks: Infinity + }), + new CopyWebpackPlugin([ + { + from: './src/main/frontend/assets', + to: 'assets' + } + ]), + new HtmlWebpackPlugin({ + template: './src/main/frontend/index.html', + chunksSortMode: 'dependency', + minify: false, + inlineCss: '' + }) + ] +})); diff --git a/shardis-ui/config/webpack.prod.config.js b/shardis-ui/config/webpack.prod.config.js index 985ebf2..d90e991 100644 --- a/shardis-ui/config/webpack.prod.config.js +++ b/shardis-ui/config/webpack.prod.config.js @@ -1,3 +1,115 @@ -var webpackConfigFactory = require('./webpack.config.factory.js'); +const fs = require('fs'); -module.exports = webpackConfigFactory.createConfig('production'); +const webpack = require('webpack'); +const ProvidePlugin = require('webpack/lib/ProvidePlugin'); +const DedupePlugin = require('webpack/lib/optimize/DedupePlugin'); +const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin'); +const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); +const WebpackMd5Hash = require('webpack-md5-hash'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const webpackMerge = require('webpack-merge'); +const DefinePlugin = require('webpack/lib/DefinePlugin'); + +const {ENV, validateConfig, baseWebpackConfig, absolutePath} = require('./webpack.commons.js'); + +var environment = process.env.NODE_ENV = process.env.ENV = ENV.PROD; + +var inlinedCss = fs.readFileSync('./src/main/frontend/css/inline.css', {encoding: 'utf8'}); + +var extractCSS = new ExtractTextPlugin('[name].[contenthash].css'); + +module.exports = validateConfig(webpackMerge(baseWebpackConfig, { + devtool: 'source-map', + cache: false, + metadata: { + ENV: environment + }, + resolve: { + cache: false + }, + entry: { + 'polyfills': './src/main/frontend/polyfills.ts', + 'vendor': './src/main/frontend/vendor.ts', + 'main': './src/main/frontend/main.ts' + }, + output: { + path: absolutePath('../src/main/resources/static'), + filename: '[name].[chunkhash].bundle.js', + sourceMapFilename: '[name].[chunkhash].bundle.map', + chunkFilename: '[id].[chunkhash].chunk.js', + publicPath: '/' + }, + module: { + loaders: [ + { + test: /main\.scss$/, + loader: extractCSS.extract(['css?sourceMap', 'postcss', 'sass?sourceMap']) + }, + { + test: /\.scss$/, + loader: 'raw!postcss!sass', + exclude: [ + absolutePath('../src/main/frontend/scss/main.scss') + ] + } + ] + }, + tslint: { + emitErrors: true, + failOnHint: true + }, + plugins: [ + new DefinePlugin({ + 'ENV': JSON.stringify(environment), + 'HMR': (ENV.DEV === environment) + }), + extractCSS, + new WebpackMd5Hash(), + new DedupePlugin(), + new webpack.optimize.OccurenceOrderPlugin(true), + new CommonsChunkPlugin({ + name: ['vendor', 'polyfills'], + minChunks: Infinity + }), + new CopyWebpackPlugin([ + { + from: './src/main/frontend/assets', + to: 'assets' + } + ]), + new HtmlWebpackPlugin({ + template: './src/main/frontend/index.html', + chunksSortMode: 'dependency', + minify: {minimize: true, removeComments: true, preserveLineBreaks: true, collapseWhitespace: true}, + inlineCss: '' + }), + new UglifyJsPlugin({ + beautify: false, + mangle: { + screw_ie8: true, + keep_fnames: true + }, + compress: { + screw_ie8: true + }, + comments: false + }) + ], + // Teach html-minifier about Angular 2 syntax + htmlLoader: { + minimize: true, + removeAttributeQuotes: false, + caseSensitive: true, + customAttrSurround: [ + [/#/, /(?:)/], + [/\*/, /(?:)/], + [/\[?\(?/, /(?:)/] + ], + customAttrAssign: [/\)?\]?=/], + removeComments: true, + preserveLineBreaks: true, + collapseWhitespace: true + } +})); diff --git a/shardis-ui/config/webpack.test.config.js b/shardis-ui/config/webpack.test.config.js index 63b92b2..2e1c1b8 100644 --- a/shardis-ui/config/webpack.test.config.js +++ b/shardis-ui/config/webpack.test.config.js @@ -1,3 +1,50 @@ -var webpackConfigFactory = require('./webpack.config.factory.js'); +const webpackMerge = require('webpack-merge'); +const DefinePlugin = require('webpack/lib/DefinePlugin'); -module.exports = webpackConfigFactory.createConfig('test'); +const {ENV, validateConfig, baseWebpackConfig, absolutePath} = require('./webpack.commons.js'); + +var environment = process.env.NODE_ENV = process.env.ENV = ENV.TEST; + +module.exports = validateConfig(webpackMerge(baseWebpackConfig, { + devtool: 'inline-source-map', + stats: { + colors: true, + reasons: true + }, + metadata: { + ENV: environment + }, + module: { + loaders: [ + { + test: /main\.scss$/, + loader: 'style!css!sass' + }, + { + test: /\.scss$/, + loader: 'raw!sass', + exclude: [ + absolutePath('../src/main/frontend/scss/main.scss') + ] + } + ], + postLoaders: [ + // instrument only testing sources with Istanbul + { + test: /\.(js|ts)$/, + include: absolutePath('../src/main/frontend'), + loader: 'istanbul-instrumenter-loader', + exclude: [ + /\.(e2e|spec)\.ts$/, + /node_modules/ + ] + } + ] + }, + plugins: [ + new DefinePlugin({ + 'ENV': JSON.stringify(environment), + 'HMR': (ENV.DEV === environment) + }) + ] +})); diff --git a/shardis-ui/npm b/shardis-ui/npm old mode 100644 new mode 100755 diff --git a/shardis-ui/package.json b/shardis-ui/package.json index 6732c93..d1b91c0 100644 --- a/shardis-ui/package.json +++ b/shardis-ui/package.json @@ -13,64 +13,60 @@ }, "private": true, "dependencies": { - "@angular/common": "2.0.0-rc.1", - "@angular/compiler": "2.0.0-rc.1", - "@angular/core": "2.0.0-rc.1", - "@angular/http": "2.0.0-rc.1", - "@angular/platform-browser": "2.0.0-rc.1", - "@angular/platform-browser-dynamic": "2.0.0-rc.1", - "@angular/platform-server": "2.0.0-rc.1", - "@angular/router": "2.0.0-rc.1", - "@angular/upgrade": "2.0.0-rc.1", - "@angular2-material/button": "2.0.0-alpha.5-2", - "@angular2-material/card": "2.0.0-alpha.5-2", - "@angular2-material/checkbox": "2.0.0-alpha.5-2", - "@angular2-material/core": "2.0.0-alpha.5-2", - "@angular2-material/grid-list": "2.0.0-alpha.5-2", - "@angular2-material/icon": "2.0.0-alpha.5-2", - "@angular2-material/input": "2.0.0-alpha.5-2", - "@angular2-material/list": "2.0.0-alpha.5-2", - "@angular2-material/progress-bar": "2.0.0-alpha.5-2", - "@angular2-material/progress-circle": "2.0.0-alpha.5-2", - "@angular2-material/radio": "2.0.0-alpha.5-2", - "@angular2-material/sidenav": "2.0.0-alpha.5-2", - "@angular2-material/slide-toggle": "2.0.0-alpha.5-2", - "@angular2-material/tabs": "2.0.0-alpha.5-2", - "@angular2-material/toolbar": "2.0.0-alpha.5-2", + "@angular/common": "2.0.0-rc.3", + "@angular/compiler": "2.0.0-rc.3", + "@angular/core": "2.0.0-rc.3", + "@angular/http": "2.0.0-rc.3", + "@angular/platform-browser": "2.0.0-rc.3", + "@angular/platform-browser-dynamic": "2.0.0-rc.3", + "@angular/router": "3.0.0-alpha.8", + "@angular/forms": "0.1.1", + "@angular2-material/button": "2.0.0-alpha.6", + "@angular2-material/card": "2.0.0-alpha.6", + "@angular2-material/checkbox": "2.0.0-alpha.6", + "@angular2-material/core": "2.0.0-alpha.6", + "@angular2-material/grid-list": "2.0.0-alpha.6", + "@angular2-material/icon": "2.0.0-alpha.6", + "@angular2-material/input": "2.0.0-alpha.6", + "@angular2-material/list": "2.0.0-alpha.6", + "@angular2-material/progress-bar": "2.0.0-alpha.6", + "@angular2-material/progress-circle": "2.0.0-alpha.6", + "@angular2-material/radio": "2.0.0-alpha.6", + "@angular2-material/sidenav": "2.0.0-alpha.6", + "@angular2-material/slide-toggle": "2.0.0-alpha.6", + "@angular2-material/tabs": "2.0.0-alpha.6", + "@angular2-material/toolbar": "2.0.0-alpha.6", "core-js": "2.4.0", "font-awesome": "4.6.3", "ng2-webstorage": "^1.0.2", - "normalize.css": "^4.1.1", + "normalize.css": "4.2.0", "rxjs": "5.0.0-beta.6", "zone.js": "0.6.12" }, "devDependencies": { "autoprefixer": "6.3.6", - "awesome-typescript-loader": "0.19.0", "copy-webpack-plugin": "3.0.1", "css-loader": "0.23.1", - "es6-promise": "3.2.1", - "es6-shim": "0.35.1", "es7-reflect-metadata": "1.6.0", "exports-loader": "0.6.3", "expose-loader": "0.7.1", "extract-text-webpack-plugin": "1.0.1", - "file-loader": "0.8.5", - "html-webpack-plugin": "2.19.0", + "file-loader": "0.9.0", + "html-webpack-plugin": "2.21.0", "http-server": "0.9.0", "imports-loader": "0.6.5", "istanbul-instrumenter-loader": "0.2.0", "json-loader": "0.5.4", - "karma": "0.13.22", + "karma": "1.1.0", "karma-chrome-launcher": "1.0.1", "karma-coverage": "1.0.0", "karma-jasmine": "1.0.2", - "karma-mocha-reporter": "2.0.3", - "karma-phantomjs-launcher": "1.0.0", + "karma-mocha-reporter": "2.0.4", + "karma-phantomjs-launcher": "1.0.1", "karma-sourcemap-loader": "0.3.7", "karma-webpack": "1.7.0", - "node-sass": "3.7.0", - "npm-check-updates": "^2.6.5", + "node-sass": "3.8.0", + "npm-check-updates": "2.6.8", "parse5": "2.1.5", "phantomjs-polyfill": "0.0.2", "phantomjs-prebuilt": "2.1.7", @@ -79,22 +75,24 @@ "raw-loader": "0.5.1", "remap-istanbul": "0.6.4", "rimraf": "2.5.2", - "sass-loader": "3.2.0", + "sass-loader": "4.0.0", "source-map-loader": "0.1.5", "style-loader": "0.13.1", "ts-helpers": "1.1.1", - "ts-node": "0.8.0", - "tslint": "3.10.2", - "tslint-loader": "2.1.4", - "typedoc": "0.4.1", + "ts-loader": "^0.8.2", + "ts-node": "0.9.3", + "tslint": "3.12.1", + "tslint-loader": "2.1.5", + "typedoc": "0.4.4", "typescript": "1.8.10", - "typings": "1.0.4", + "typings": "1.3.1", "url-loader": "0.5.7", "webpack": "1.13.1", "webpack-dev-server": "1.14.1", "webpack-load-plugins": "0.1.2", "webpack-md5-hash": "0.0.5", - "webpack-validator": "2.1.2" + "webpack-merge": "0.14.0", + "webpack-validator": "2.2.2" }, "scripts": { "clean": "npm cache clean && rimraf node node_modules doc typings coverage ./src/main/resources/static/*", @@ -116,7 +114,7 @@ "server:dev": "webpack-dev-server --config ./config/webpack.dev.config.js --hot --inline --progress --profile --colors --watch --display-error-details --display-cached ", "webdriver:update": "webdriver-manager update", "webdriver:start": "webdriver-manager start", - "lint": "tslint -t prose \"./src/main/frontend/**/*.ts\"", + "lint": "tslint --force -t prose \"./src/main/frontend/**/*.ts\"", "pree2e": "npm run webdriver:update -- --standalone", "e2e": "protractor ./config/protractor.conf.js", "e2e:live": "protractor --elementExplorer ./config/protractor.conf.js", @@ -130,5 +128,9 @@ "version": "npm run build", "postversion": "git push && git push --tags", "ncu": "ncu -a" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3" } } diff --git a/shardis-ui/pom.xml b/shardis-ui/pom.xml index be8c559..5689092 100644 --- a/shardis-ui/pom.xml +++ b/shardis-ui/pom.xml @@ -3,8 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + com.shardis shardis-ui - 1.7.0 + 1.8.0 jar shardis-ui @@ -13,7 +14,7 @@ com.shardis shardis-parent - 1.7.0 + 1.8.0 diff --git a/shardis-ui/src/main/frontend/app/views/about/about.component.html b/shardis-ui/src/main/frontend/app/+about/about.component.html similarity index 100% rename from shardis-ui/src/main/frontend/app/views/about/about.component.html rename to shardis-ui/src/main/frontend/app/+about/about.component.html diff --git a/shardis-ui/src/main/frontend/app/views/about/about.component.scss b/shardis-ui/src/main/frontend/app/+about/about.component.scss similarity index 68% rename from shardis-ui/src/main/frontend/app/views/about/about.component.scss rename to shardis-ui/src/main/frontend/app/+about/about.component.scss index d6e4c6c..f965b29 100644 --- a/shardis-ui/src/main/frontend/app/views/about/about.component.scss +++ b/shardis-ui/src/main/frontend/app/+about/about.component.scss @@ -1,4 +1,4 @@ -$font-stack: Helvetica, sans-serif; +$font-stack: Helvetica, sans-serif; $primary-color: #D40000; span { diff --git a/shardis-ui/src/main/frontend/app/views/about/about.component.spec.ts b/shardis-ui/src/main/frontend/app/+about/about.component.spec.ts similarity index 60% rename from shardis-ui/src/main/frontend/app/views/about/about.component.spec.ts rename to shardis-ui/src/main/frontend/app/+about/about.component.spec.ts index bfeb741..f083e03 100644 --- a/shardis-ui/src/main/frontend/app/views/about/about.component.spec.ts +++ b/shardis-ui/src/main/frontend/app/+about/about.component.spec.ts @@ -1,16 +1,7 @@ -import { - it, - inject, - async, - describe, - beforeEachProviders -} from '@angular/core/testing'; -import {TestComponentBuilder} from '@angular/compiler/testing'; - -import {Component, provide} from '@angular/core'; +import {it, inject, describe, beforeEachProviders} from '@angular/core/testing'; +import {About} from './about.component'; // Load the implementations that should be tested -import {About} from './about.component.ts'; describe('About', () => { // provide our implementations or mocks to the dependency injector diff --git a/shardis-ui/src/main/frontend/app/views/about/about.component.ts b/shardis-ui/src/main/frontend/app/+about/about.component.ts similarity index 100% rename from shardis-ui/src/main/frontend/app/views/about/about.component.ts rename to shardis-ui/src/main/frontend/app/+about/about.component.ts diff --git a/shardis-ui/src/main/frontend/app/+about/index.ts b/shardis-ui/src/main/frontend/app/+about/index.ts new file mode 100644 index 0000000..6234f86 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+about/index.ts @@ -0,0 +1 @@ +export * from './about.component' diff --git a/shardis-ui/src/main/frontend/app/+crud/crud.component.html b/shardis-ui/src/main/frontend/app/+crud/crud.component.html new file mode 100644 index 0000000..df4a4ca --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+crud/crud.component.html @@ -0,0 +1,41 @@ + + + + + + Editing post + +
+ + {{titleField.characterCount}} / 100 + + + {{contentField.characterCount}} / 255 + +
+
+ + + + +
+ + {{post.title}} + +

{{post.content}}

+ + +

id: {{post.id}}

+

createdDate: {{post.createdDate | date:"dd.MM.yyyy HH:mm"}}

+

updatedDate: {{post.createdDate | date:"dd.MM.yyyy HH:mm"}}

+

version: {{post.version}}

+
+
+
+ + + + +
diff --git a/shardis-ui/src/main/frontend/app/+crud/crud.component.scss b/shardis-ui/src/main/frontend/app/+crud/crud.component.scss new file mode 100644 index 0000000..acfe409 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+crud/crud.component.scss @@ -0,0 +1,11 @@ +.postcard { + margin: 10px 10px; +} + +form > md-input { + width: 100%; +} + +.md-hint { + height: 1.5em; +} diff --git a/shardis-ui/src/main/frontend/app/+crud/crud.component.spec.ts b/shardis-ui/src/main/frontend/app/+crud/crud.component.spec.ts new file mode 100644 index 0000000..8bd05ed --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+crud/crud.component.spec.ts @@ -0,0 +1,33 @@ +import {it, inject, beforeEachProviders} from '@angular/core/testing'; +import {BaseRequestOptions, Http} from '@angular/http'; +import {MockBackend} from '@angular/http/testing'; +import {AuthService} from '../shared'; +import {Crud} from './crud.component'; + +// Load the implementations that should be tested + +describe('Crud', () => { + // provide our implementations or mocks to the dependency injector + beforeEachProviders(() => [ + BaseRequestOptions, + MockBackend, + { + provide: Http, + useFactory: function (backend, defaultOptions) { + return new Http(backend, defaultOptions); + }, + deps: [MockBackend, BaseRequestOptions] + }, + AuthService, + Crud + ]); + + it('should log ngOnInit', inject([Crud], (crud) => { + spyOn(console, 'log'); + expect(console.log).not.toHaveBeenCalled(); + + crud.ngOnInit(); + expect(console.log).toHaveBeenCalled(); + })); + +}); diff --git a/shardis-ui/src/main/frontend/app/+crud/crud.component.ts b/shardis-ui/src/main/frontend/app/+crud/crud.component.ts new file mode 100644 index 0000000..2715c72 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+crud/crud.component.ts @@ -0,0 +1,143 @@ +import {Component, OnInit} from '@angular/core'; +import {AuthService} from '../shared/auth/auth.service'; +import {Http} from '@angular/http'; + +console.log('`Crud` component loaded asynchronously'); + +@Component({ + selector: 'crud', + styles: [require('./crud.component.scss')], + template: require('./crud.component.html') +}) +export class Crud implements OnInit { + + posts:BlogPost[] = []; + + editing:boolean = false; + editedPost:BlogPost = null; + + constructor(public http:Http, public authService:AuthService) { + } + + + ngOnInit():any { + console.log('hello `Crud` component'); + this.fetchPosts(); + } + + public editPost(post:BlogPost) { + this.editing = true; + this.editedPost = JSON.parse(JSON.stringify(post)); + this.scrollToTop(); + } + + public newPost() { + this.editing = true; + this.editedPost = { + id: null, + title: '', + content: '', + createdDate: null, + updatedDate: null, + version: null, + createdBy: null, + updatedBy: null, + }; + } + + public removePost(post:BlogPost) { + this.deletePost(post); + } + + public cancelEdit() { + this.editing = false; + this.editedPost = null; + this.scrollToTop(); + } + + public refresh() { + this.cancelEdit(); + this.fetchPosts(); + } + + private fetchPosts() { + this.http.get('/api/posts/', {headers: this.authService.getAuthorizationHeaders()}) + .subscribe( + data => { + this.posts = data.json(); + }, + err => console.log('Something went wrong') + ); + } + + private savePost(post:BlogPost) { + this.http.post(`/api/posts/`, post, {headers: this.authService.getAuthorizationHeaders()}) + .subscribe( + data => { + console.log('Saved', data.json()); + this.updateOrAddPostToList(data.json()); + }, + err => console.log('Something went wrong') + ); + } + + private deletePost(post:BlogPost) { + this.http.delete(`/api/posts/${post.id}`, {headers: this.authService.getAuthorizationHeaders()}) + .subscribe( + data => { + console.log('Removed', data.json()); + this.removePostFromList(post); + }, + err => console.log('Something went wrong') + ); + } + + private removePostFromList(post:BlogPost) { + this.posts = this.posts.filter((x, idx, obs) => x.id !== post.id); + this.cancelEdit(); + } + + private updateOrAddPostToList(post:BlogPost) { + var changedList:BlogPost[] = this.posts.filter((x, idx, obs) => x.id === post.id); + if (changedList.length === 0) { + this.posts.push(post); + } else { + changedList.forEach((x) => { + var index = this.posts.indexOf(x); + this.posts[index] = post; + }); + } + this.cancelEdit(); + } + + private scrollToTop() { + var contentEl = document.querySelector('md-sidenav-layout > md-content'); + if (contentEl) { + this.scrollTo(contentEl, 0, 100); + } + } + + private scrollTo(element, to, duration) { + if (duration <= 0) return; + var difference = to - element.scrollTop; + var perTick = difference / duration * 10; + + setTimeout(() => { + element.scrollTop = element.scrollTop + perTick; + if (element.scrollTop === to) return; + this.scrollTo(element, to, duration - 10); + }, 10); + } + +} + +interface BlogPost { + id:number; + title:string; + content:string; + createdDate:Date; + updatedDate:Date; + version:number; + createdBy:any; + updatedBy:any; +} diff --git a/shardis-ui/src/main/frontend/app/+crud/index.ts b/shardis-ui/src/main/frontend/app/+crud/index.ts new file mode 100644 index 0000000..56acda0 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+crud/index.ts @@ -0,0 +1 @@ +export * from './crud.component' diff --git a/shardis-ui/src/main/frontend/app/views/playground/heroes/app.component.scss b/shardis-ui/src/main/frontend/app/+playground/heroes/app.component.scss similarity index 99% rename from shardis-ui/src/main/frontend/app/views/playground/heroes/app.component.scss rename to shardis-ui/src/main/frontend/app/+playground/heroes/app.component.scss index cd111b9..e76b5a6 100644 --- a/shardis-ui/src/main/frontend/app/views/playground/heroes/app.component.scss +++ b/shardis-ui/src/main/frontend/app/+playground/heroes/app.component.scss @@ -2,12 +2,14 @@ background-color: #CFD8DC !important; color: white; } + .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 10em; } + .heroes li { cursor: pointer; position: relative; @@ -18,19 +20,23 @@ height: 1.6em; border-radius: 4px; } + .heroes li.selected:hover { background-color: #BBD8DC !important; color: white; } + .heroes li:hover { color: #607D8B; background-color: #DDD; left: .1em; } + .heroes .text { position: relative; top: -3px; } + .heroes .badge { display: inline-block; font-size: small; diff --git a/shardis-ui/src/main/frontend/app/views/playground/heroes/app.component.ts b/shardis-ui/src/main/frontend/app/+playground/heroes/app.component.ts similarity index 100% rename from shardis-ui/src/main/frontend/app/views/playground/heroes/app.component.ts rename to shardis-ui/src/main/frontend/app/+playground/heroes/app.component.ts diff --git a/shardis-ui/src/main/frontend/app/views/playground/heroes/hero-detail.component.ts b/shardis-ui/src/main/frontend/app/+playground/heroes/hero-detail.component.ts similarity index 97% rename from shardis-ui/src/main/frontend/app/views/playground/heroes/hero-detail.component.ts rename to shardis-ui/src/main/frontend/app/+playground/heroes/hero-detail.component.ts index c2ced4e..9077cb8 100644 --- a/shardis-ui/src/main/frontend/app/views/playground/heroes/hero-detail.component.ts +++ b/shardis-ui/src/main/frontend/app/+playground/heroes/hero-detail.component.ts @@ -18,5 +18,5 @@ import {Hero} from './hero'; inputs: ['hero'] }) export class HeroDetailComponent { - hero: Hero; + hero:Hero; } diff --git a/shardis-ui/src/main/frontend/app/views/playground/heroes/hero.service.ts b/shardis-ui/src/main/frontend/app/+playground/heroes/hero.service.ts similarity index 100% rename from shardis-ui/src/main/frontend/app/views/playground/heroes/hero.service.ts rename to shardis-ui/src/main/frontend/app/+playground/heroes/hero.service.ts diff --git a/shardis-ui/src/main/frontend/app/+playground/heroes/hero.ts b/shardis-ui/src/main/frontend/app/+playground/heroes/hero.ts new file mode 100644 index 0000000..b1f8c94 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+playground/heroes/hero.ts @@ -0,0 +1,4 @@ +export interface Hero { + id:number; + name:string; +} diff --git a/shardis-ui/src/main/frontend/app/views/playground/heroes/mock-heroes.ts b/shardis-ui/src/main/frontend/app/+playground/heroes/mock-heroes.ts similarity index 92% rename from shardis-ui/src/main/frontend/app/views/playground/heroes/mock-heroes.ts rename to shardis-ui/src/main/frontend/app/+playground/heroes/mock-heroes.ts index ed04b97..fd7f623 100644 --- a/shardis-ui/src/main/frontend/app/views/playground/heroes/mock-heroes.ts +++ b/shardis-ui/src/main/frontend/app/+playground/heroes/mock-heroes.ts @@ -1,6 +1,6 @@ import {Hero} from './hero'; -export var HEROES: Hero[] = [ +export var HEROES:Hero[] = [ {'id': 11, 'name': 'Mr. Nice'}, {'id': 12, 'name': 'Narco'}, {'id': 13, 'name': 'Bombasto'}, diff --git a/shardis-ui/src/main/frontend/app/+playground/index.ts b/shardis-ui/src/main/frontend/app/+playground/index.ts new file mode 100644 index 0000000..551f1d0 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/+playground/index.ts @@ -0,0 +1 @@ +export * from './playground.component' diff --git a/shardis-ui/src/main/frontend/app/views/playground/playground.component.ts b/shardis-ui/src/main/frontend/app/+playground/playground.component.ts similarity index 100% rename from shardis-ui/src/main/frontend/app/views/playground/playground.component.ts rename to shardis-ui/src/main/frontend/app/+playground/playground.component.ts diff --git a/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.html b/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.html new file mode 100644 index 0000000..1c76fa6 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.html @@ -0,0 +1,4 @@ + + Access Denied + Go away!!! + diff --git a/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.scss b/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.ts b/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.ts new file mode 100644 index 0000000..e593670 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/access-denied/access-denied.component.ts @@ -0,0 +1,18 @@ +import {Component, OnInit} from '@angular/core'; + +@Component({ + selector: 'access-denied', + styles: [require('./access-denied.component.scss')], + template: require('./access-denied.component.html') +}) +export class AccessDenied implements OnInit { + + constructor() { + } + + + ngOnInit():any { + console.log('access denied'); + } + +} diff --git a/shardis-ui/src/main/frontend/app/access-denied/index.ts b/shardis-ui/src/main/frontend/app/access-denied/index.ts new file mode 100644 index 0000000..3592a2c --- /dev/null +++ b/shardis-ui/src/main/frontend/app/access-denied/index.ts @@ -0,0 +1 @@ +export * from './access-denied.component'; diff --git a/shardis-ui/src/main/frontend/app/app.component.html b/shardis-ui/src/main/frontend/app/app.component.html index 240ede8..222847b 100644 --- a/shardis-ui/src/main/frontend/app/app.component.html +++ b/shardis-ui/src/main/frontend/app/app.component.html @@ -2,11 +2,14 @@ - - {{view.icon}} - {{view.name}} - {{view.description}} - + @@ -19,8 +22,15 @@ {{ name }} - Login - + + Login + + + + diff --git a/shardis-ui/src/main/frontend/app/app.component.scss b/shardis-ui/src/main/frontend/app/app.component.scss index e3de456..3db9626 100644 --- a/shardis-ui/src/main/frontend/app/app.component.scss +++ b/shardis-ui/src/main/frontend/app/app.component.scss @@ -1,7 +1,16 @@ +[hidden] { + display: none; +} + .fill { flex: 1 1 auto; } +.user-info { + margin-right: 10px; + font-size: 0.75em; +} + .footer { align-items: center; font-size: 1em; diff --git a/shardis-ui/src/main/frontend/app/app.component.ts b/shardis-ui/src/main/frontend/app/app.component.ts index 4b7e609..e68730a 100644 --- a/shardis-ui/src/main/frontend/app/app.component.ts +++ b/shardis-ui/src/main/frontend/app/app.component.ts @@ -1,77 +1,32 @@ import {Component, OnInit} from '@angular/core'; -import {Routes, Router} from '@angular/router'; -import {Home} from './views/home/home.component'; -import {AuthService} from './services/auth.service'; -import {About} from './views/about/about.component'; -import {Playground} from './views/playground/playground.component'; -import {Login} from './views/login/login.component'; +import {Router} from '@angular/router'; +import {AuthService} from './shared'; +import {APP_MENU, AppMenuItem} from './app.menu'; @Component({ selector: 'app', pipes: [], - providers: [AuthService], + providers: [], directives: [], styles: [require('./app.component.scss')], template: require('./app.component.html') }) -@Routes([ - { - path: '/', - component: Home as any - }, - { - path: '/about', - component: About as any//() => LazyLoader.lazyLoad(LazyModule.ABOUT).then(m => m['About']), - }, - { - path: '/playground', - component: Playground as any //() => LazyLoader.lazyLoad(LazyModule.PLAYGROUND).then(m => m['Playground']), - }, - { - path: '/login', - component: Login as any - }, - { - path: '*', - component: Home as any - } -]) export class App implements OnInit { name = 'Spring Boot Angular 2 Webpack Starter'; url = 'https://github.com/kucharzyk'; loading:boolean = false; - views:Object[] = [ - - { - name: 'Home', - description: 'Home page', - icon: 'public', - link: ['/'] - }, - { - name: 'Playground', - description: 'Playground page', - icon: 'casino', - link: ['/playground'] - }, - { - name: 'About', - description: 'About page', - icon: 'person', - link: ['/about'] - } - ]; + views:AppMenuItem[] = APP_MENU; - constructor(public authService: AuthService, public router: Router) { + constructor(public authService:AuthService, public router:Router) { } - logMeOut(): void { + logMeOut():void { this.authService.logout(); - this.router.navigate(['/']); + this.router.navigate(['']); } - ngOnInit(): void { + ngOnInit():void { console.log('app on init'); } diff --git a/shardis-ui/src/main/frontend/app/app.menu.ts b/shardis-ui/src/main/frontend/app/app.menu.ts new file mode 100644 index 0000000..4de4139 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/app.menu.ts @@ -0,0 +1,36 @@ +export const APP_MENU:AppMenuItem[] = [ + + { + name: 'Home', + description: 'Home page', + icon: 'public', + link: [''] + }, + { + name: 'Playground', + description: 'Playground page', + icon: 'casino', + link: ['playground'] + }, + { + name: 'Crud', + description: 'Simple crud', + icon: 'edit', + link: ['crud'], + roles: ['ROLE_ADMIN'] + }, + { + name: 'About', + description: 'About page', + icon: 'person', + link: ['about'] + } +]; + +export interface AppMenuItem { + name:string; + description:string; + icon:string; + link:string[]; + roles?:string[]; +} diff --git a/shardis-ui/src/main/frontend/app/app.routes.ts b/shardis-ui/src/main/frontend/app/app.routes.ts new file mode 100644 index 0000000..a839017 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/app.routes.ts @@ -0,0 +1,60 @@ +import {RouterConfig} from '@angular/router'; +import {Home} from './home'; +import {Login} from './login'; +import {ASYNC_ROUTES} from '../platform/browser/webpack/lazy-loader'; +import {NotFound} from './not-found'; +import {AccessDenied} from './access-denied/access-denied.component'; +import {AuthenticatedGuard, AdminGuard, UnauthenticatedGuard} from './shared/guards'; +import {AuthService} from './shared/auth/auth.service'; + +export const routes:RouterConfig = [ + { + path: '', + redirectTo: 'home' + }, + { + path: 'home', + component: Home + }, + { + path: 'about', + component: 'About' + }, + { + path: 'crud', + component: 'Crud', + canActivate: [AuthenticatedGuard, AdminGuard] + }, + { + path: 'playground', + component: 'Playground' + }, + { + path: 'login', + component: Login, + canActivate: [UnauthenticatedGuard] + }, + { + path: 'accessDenied', + component: AccessDenied + }, + { + path: '404', + component: NotFound + }, + { + path: '**', + redirectTo: '404', + terminal: true + }, +]; + +export const asyncRoutes = { + About: ASYNC_ROUTES.About, + Playground: ASYNC_ROUTES.Playground, + Crud: ASYNC_ROUTES.Crud +}; + +export const AUTH_PROVIDERS = [AuthService, AdminGuard, AuthenticatedGuard, UnauthenticatedGuard]; + + diff --git a/shardis-ui/src/main/frontend/app/app.spec.ts b/shardis-ui/src/main/frontend/app/app.spec.ts index bf878b0..1bc02be 100644 --- a/shardis-ui/src/main/frontend/app/app.spec.ts +++ b/shardis-ui/src/main/frontend/app/app.spec.ts @@ -1,17 +1,11 @@ -import { - it, - inject, - async, - beforeEachProviders -} from '@angular/core/testing'; - -// Load the implementations that should be tested +import {it, inject, beforeEachProviders} from '@angular/core/testing'; import {App} from './app.component'; -import {AuthService} from './services/auth.service'; +import {AuthService} from './shared'; import {BaseRequestOptions, Http} from '@angular/http'; import {MockBackend} from '@angular/http/testing/mock_backend'; -import {provide} from '@angular/core'; -import { ROUTER_FAKE_PROVIDERS } from '@angular/router/testing'; +import {TEST_ROUTER_PROVIDERS} from './shared/testing'; + +// Load the implementations that should be tested describe('App', () => { // provide our implementations or mocks to the dependency injector @@ -19,13 +13,14 @@ describe('App', () => { App, BaseRequestOptions, MockBackend, - ROUTER_FAKE_PROVIDERS, - provide(Http, { + ...TEST_ROUTER_PROVIDERS, + { + provide: Http, useFactory: function (backend, defaultOptions) { return new Http(backend, defaultOptions); }, deps: [MockBackend, BaseRequestOptions] - }), + }, AuthService, ]); diff --git a/shardis-ui/src/main/frontend/app/directives/router-active.directive.ts b/shardis-ui/src/main/frontend/app/directives/router-active.directive.ts deleted file mode 100644 index a39659b..0000000 --- a/shardis-ui/src/main/frontend/app/directives/router-active.directive.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {Router} from '@angular/router'; -import {isPresent} from '@angular/core/src/facade/lang'; -import {Directive, Query, QueryList, Attribute, ElementRef, Renderer, Optional, Input} from '@angular/core'; -import {RouterLink} from '@angular/router/src/directives/router_link'; - -/** - * RouterActive dynamically finds the first element with routerLink and toggles the active class - * - * ## Use - * - * ``` - *
  • Home
  • - *
  • Home
  • - * ``` - */ -@Directive({ - selector: '[router-active], [routerActive]' -}) -export class RouterActive { - @Input() routerActive:string = null; - routerActiveAttr:string = 'active'; - - constructor(public router:Router, - public element:ElementRef, - public renderer:Renderer, - @Query(RouterLink as any) public routerLink:QueryList, - @Optional() @Attribute('router-active') routerActiveAttr?:string) { - this.routerActiveAttr = this._defaultAttrValue(routerActiveAttr); - } - - ngOnInit() { - this.router.changes.subscribe(() => { - if (this.routerLink.first) { - this._updateClass(); - } - }); - } - - private _updateClass() { - let active = this.routerLink.first.isActive; - this.renderer.setElementClass(this.element.nativeElement, this._attrOrProp(), active); - } - - private _defaultAttrValue(attr?:string) { - return this.routerActiveAttr = attr || this.routerActiveAttr; - } - - private _attrOrProp() { - return isPresent(this.routerActive) ? this.routerActive : this.routerActiveAttr; - } -} diff --git a/shardis-ui/src/main/frontend/app/views/home/home.component.e2e.ts b/shardis-ui/src/main/frontend/app/home/home.component.e2e.ts similarity index 79% rename from shardis-ui/src/main/frontend/app/views/home/home.component.e2e.ts rename to shardis-ui/src/main/frontend/app/home/home.component.e2e.ts index fa6c310..38106e7 100644 --- a/shardis-ui/src/main/frontend/app/views/home/home.component.e2e.ts +++ b/shardis-ui/src/main/frontend/app/home/home.component.e2e.ts @@ -1,6 +1,3 @@ -/* - * TODO: ES5 for now until I make a webpack plugin for protractor - */ describe('App', () => { beforeEach(() => { @@ -15,5 +12,5 @@ describe('App', () => { expect(subject).toEqual(result); }); - + }); diff --git a/shardis-ui/src/main/frontend/app/views/home/home.component.html b/shardis-ui/src/main/frontend/app/home/home.component.html similarity index 98% rename from shardis-ui/src/main/frontend/app/views/home/home.component.html rename to shardis-ui/src/main/frontend/app/home/home.component.html index c4a7f6b..03be5f3 100644 --- a/shardis-ui/src/main/frontend/app/views/home/home.component.html +++ b/shardis-ui/src/main/frontend/app/home/home.component.html @@ -61,7 +61,8 @@ Content from server:
    - +
    this.data = {{ data | json }}
    diff --git a/shardis-ui/src/main/frontend/app/views/home/home.component.scss b/shardis-ui/src/main/frontend/app/home/home.component.scss similarity index 100% rename from shardis-ui/src/main/frontend/app/views/home/home.component.scss rename to shardis-ui/src/main/frontend/app/home/home.component.scss diff --git a/shardis-ui/src/main/frontend/app/views/home/home.spec.ts b/shardis-ui/src/main/frontend/app/home/home.component.spec.ts similarity index 66% rename from shardis-ui/src/main/frontend/app/views/home/home.spec.ts rename to shardis-ui/src/main/frontend/app/home/home.component.spec.ts index 62af81e..8d17093 100644 --- a/shardis-ui/src/main/frontend/app/views/home/home.spec.ts +++ b/shardis-ui/src/main/frontend/app/home/home.component.spec.ts @@ -1,37 +1,25 @@ -import { - it, - inject, - async, - describe, - beforeEachProviders -} from '@angular/core/testing'; - -import {Component, provide} from '@angular/core'; -import {BaseRequestOptions, Http} from '@angular/http'; +import {it, inject, describe, beforeEachProviders} from '@angular/core/testing'; +import {BaseRequestOptions, Http, Response, ResponseOptions} from '@angular/http'; import {MockBackend} from '@angular/http/testing'; +import {Home} from './home.component'; +import {Title} from './shared'; +import {AuthService} from '../shared'; // Load the implementations that should be tested -import {Home} from './home.component.ts'; -import {Title} from './services/title.service.ts'; -import {Response} from '@angular/http'; -import {ResponseOptions} from '@angular/http'; -import {MockConnection} from '@angular/http/testing/mock_backend'; -import {ResponseOptionsArgs} from '@angular/http'; -import {AuthService} from '../../services/auth.service'; describe('Home', () => { // provide our implementations or mocks to the dependency injector beforeEachProviders(() => [ BaseRequestOptions, MockBackend, - provide(Http, { + { + provide: Http, useFactory: function (backend, defaultOptions) { return new Http(backend, defaultOptions); }, deps: [MockBackend, BaseRequestOptions] - }), - + }, Title, Home, AuthService diff --git a/shardis-ui/src/main/frontend/app/views/home/home.component.ts b/shardis-ui/src/main/frontend/app/home/home.component.ts similarity index 75% rename from shardis-ui/src/main/frontend/app/views/home/home.component.ts rename to shardis-ui/src/main/frontend/app/home/home.component.ts index e7cfb61..1aebc1d 100644 --- a/shardis-ui/src/main/frontend/app/views/home/home.component.ts +++ b/shardis-ui/src/main/frontend/app/home/home.component.ts @@ -1,7 +1,6 @@ import {Component} from '@angular/core'; -import {Title} from './services/title.service.ts'; -import {XLarge} from './../../directives/x-large.directive.ts'; -import {AuthService} from '../../services/auth.service'; +import {Title} from './shared'; +import {XLarge, AuthService} from '../shared'; @Component({ selector: 'home', diff --git a/shardis-ui/src/main/frontend/app/home/index.ts b/shardis-ui/src/main/frontend/app/home/index.ts new file mode 100644 index 0000000..3c1cba0 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/home/index.ts @@ -0,0 +1 @@ +export * from './home.component' diff --git a/shardis-ui/src/main/frontend/app/home/shared/index.ts b/shardis-ui/src/main/frontend/app/home/shared/index.ts new file mode 100644 index 0000000..50c5d6d --- /dev/null +++ b/shardis-ui/src/main/frontend/app/home/shared/index.ts @@ -0,0 +1 @@ +export * from './title.service' diff --git a/shardis-ui/src/main/frontend/app/views/home/services/title.service.spec.ts b/shardis-ui/src/main/frontend/app/home/shared/title.service.spec.ts similarity index 73% rename from shardis-ui/src/main/frontend/app/views/home/services/title.service.spec.ts rename to shardis-ui/src/main/frontend/app/home/shared/title.service.spec.ts index 3ba37c0..e0e5f29 100644 --- a/shardis-ui/src/main/frontend/app/views/home/services/title.service.spec.ts +++ b/shardis-ui/src/main/frontend/app/home/shared/title.service.spec.ts @@ -1,30 +1,20 @@ -import { - it, - inject, - async, - beforeEachProviders -} from '@angular/core/testing'; - -import {Component, provide} from '@angular/core'; -import {BaseRequestOptions, Http} from '@angular/http'; +import {it, inject, async, beforeEachProviders} from '@angular/core/testing'; +import {BaseRequestOptions, Http, ResponseOptions, Response} from '@angular/http'; import {MockBackend} from '@angular/http/testing'; - - -import {Title} from './title.service.ts'; -import {ResponseOptions} from '@angular/http'; -import {Response} from '@angular/http'; -import {AuthService} from '../../../services/auth.service'; +import {Title} from './title.service'; +import {AuthService} from '../../shared'; describe('Title', () => { beforeEachProviders(() => [ BaseRequestOptions, MockBackend, - provide(Http, { + { + provide: Http, useFactory: function (backend, defaultOptions) { return new Http(backend, defaultOptions); }, deps: [MockBackend, BaseRequestOptions] - }), + }, Title, AuthService ]); diff --git a/shardis-ui/src/main/frontend/app/views/home/services/title.service.ts b/shardis-ui/src/main/frontend/app/home/shared/title.service.ts similarity index 87% rename from shardis-ui/src/main/frontend/app/views/home/services/title.service.ts rename to shardis-ui/src/main/frontend/app/home/shared/title.service.ts index 88a16f3..20f6bc4 100644 --- a/shardis-ui/src/main/frontend/app/views/home/services/title.service.ts +++ b/shardis-ui/src/main/frontend/app/home/shared/title.service.ts @@ -1,6 +1,6 @@ import {Injectable} from '@angular/core'; import {Http} from '@angular/http'; -import {AuthService} from '../../../services/auth.service'; +import {AuthService} from '../../shared'; import 'rxjs/Rx'; @Injectable() diff --git a/shardis-ui/src/main/frontend/app/index.ts b/shardis-ui/src/main/frontend/app/index.ts new file mode 100644 index 0000000..e1b5e78 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/index.ts @@ -0,0 +1 @@ +export * from './app.component' diff --git a/shardis-ui/src/main/frontend/app/login/index.ts b/shardis-ui/src/main/frontend/app/login/index.ts new file mode 100644 index 0000000..60c8483 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/login/index.ts @@ -0,0 +1 @@ +export * from './login.component' diff --git a/shardis-ui/src/main/frontend/app/views/login/login.component.html b/shardis-ui/src/main/frontend/app/login/login.component.html similarity index 73% rename from shardis-ui/src/main/frontend/app/views/login/login.component.html rename to shardis-ui/src/main/frontend/app/login/login.component.html index 0d6fedc..80ed6de 100644 --- a/shardis-ui/src/main/frontend/app/views/login/login.component.html +++ b/shardis-ui/src/main/frontend/app/login/login.component.html @@ -1,10 +1,10 @@ Login Page -
    +

    {{message}}

    - - + +
    diff --git a/shardis-ui/src/main/frontend/app/views/login/login.component.scss b/shardis-ui/src/main/frontend/app/login/login.component.scss similarity index 100% rename from shardis-ui/src/main/frontend/app/views/login/login.component.scss rename to shardis-ui/src/main/frontend/app/login/login.component.scss diff --git a/shardis-ui/src/main/frontend/app/views/login/login.component.ts b/shardis-ui/src/main/frontend/app/login/login.component.ts similarity index 89% rename from shardis-ui/src/main/frontend/app/views/login/login.component.ts rename to shardis-ui/src/main/frontend/app/login/login.component.ts index c673be6..b184fcb 100644 --- a/shardis-ui/src/main/frontend/app/views/login/login.component.ts +++ b/shardis-ui/src/main/frontend/app/login/login.component.ts @@ -1,6 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {Router} from '@angular/router'; -import {AuthService} from '../../services/auth.service'; +import {AuthService} from '../shared'; console.log('`Login` component loaded asynchronously'); @@ -25,7 +25,7 @@ export class Login implements OnInit { .catch(errorMessage => this.message = errorMessage) .then(() => { if (this.authService.isAuthenticated()) { - this.router.navigate(['/']); + this.router.navigate(['']); } }); diff --git a/shardis-ui/src/main/frontend/app/not-found/index.ts b/shardis-ui/src/main/frontend/app/not-found/index.ts new file mode 100644 index 0000000..d91ce3f --- /dev/null +++ b/shardis-ui/src/main/frontend/app/not-found/index.ts @@ -0,0 +1 @@ +export * from './not-found.component'; diff --git a/shardis-ui/src/main/frontend/app/not-found/not-found.component.html b/shardis-ui/src/main/frontend/app/not-found/not-found.component.html new file mode 100644 index 0000000..ec723da --- /dev/null +++ b/shardis-ui/src/main/frontend/app/not-found/not-found.component.html @@ -0,0 +1,4 @@ + + 404 + Page not found :( + diff --git a/shardis-ui/src/main/frontend/app/not-found/not-found.component.scss b/shardis-ui/src/main/frontend/app/not-found/not-found.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/shardis-ui/src/main/frontend/app/not-found/not-found.component.ts b/shardis-ui/src/main/frontend/app/not-found/not-found.component.ts new file mode 100644 index 0000000..7f8de81 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/not-found/not-found.component.ts @@ -0,0 +1,20 @@ +import {Component, OnInit} from '@angular/core'; + +console.log('`About` component loaded asynchronously'); + +@Component({ + selector: 'not-found', + styles: [require('./not-found.component.scss')], + template: require('./not-found.component.html') +}) +export class NotFound implements OnInit { + + constructor() { + } + + + ngOnInit():any { + console.log('route not found'); + } + +} diff --git a/shardis-ui/src/main/frontend/app/services/auth.service.ts b/shardis-ui/src/main/frontend/app/shared/auth/auth.service.ts similarity index 60% rename from shardis-ui/src/main/frontend/app/services/auth.service.ts rename to shardis-ui/src/main/frontend/app/shared/auth/auth.service.ts index 58dbb92..4b381dc 100644 --- a/shardis-ui/src/main/frontend/app/services/auth.service.ts +++ b/shardis-ui/src/main/frontend/app/shared/auth/auth.service.ts @@ -1,6 +1,7 @@ import {Injectable} from '@angular/core'; import {Http, Headers} from '@angular/http'; import {LocalStorage} from 'ng2-webstorage/index'; +import {AppMenuItem} from '../../app.menu'; @Injectable() export class AuthService { @@ -12,10 +13,14 @@ export class AuthService { @LocalStorage() private tokenData:Oauth2TokenData; + public static decodeAccessToken(access_token:string) { + return JSON.parse(window.atob(access_token.split('.')[1])); + } + constructor(public http:Http) { if (this.tokenData && this.tokenData.access_token) { this.authenticated = true; - this.userData = this.decodeAccessToken(this.tokenData.access_token); + this.userData = AuthService.decodeAccessToken(this.tokenData.access_token); this.tokenExpirationDate = new Date(this.userData.exp * 1000); if (this.tokenExpirationDate < new Date()) { console.log('Session timeout'); @@ -24,11 +29,15 @@ export class AuthService { } } - isAuthenticated():boolean { + public isAuthenticated():boolean { + if (this.tokenExpirationDate < new Date()) { + console.log('Session timeout'); + this.logout(); + } return this.authenticated; } - public authenticate(username: string, password: string) :Promise { + public authenticate(username:string, password:string):Promise { console.log('Authentication pending...'); @@ -57,7 +66,7 @@ export class AuthService { data => { this.tokenData = data.json(); this.authenticated = true; - this.userData = this.decodeAccessToken(this.tokenData.access_token); + this.userData = AuthService.decodeAccessToken(this.tokenData.access_token); this.tokenExpirationDate = new Date(this.userData.exp * 1000); resolve('OK'); }, @@ -70,6 +79,34 @@ export class AuthService { }); } + public refreshToken() { + if (this.isAuthenticated()) { + + var basicAuthHeader = btoa(`acme:acmesecret`); + + var headers = new Headers(); + headers.append('Authorization', `Basic ${basicAuthHeader}`); + headers.append('Accept', `application/json`); + headers.append('Content-Type', `application/x-www-form-urlencoded`); + + var data = 'grant_type=refresh_token&refresh_token=' + encodeURIComponent(this.tokenData.refresh_token); + + this.http + .post('/auth/oauth/token', data, {headers: headers}) + .subscribe( + data => { + this.tokenData = data.json(); + this.authenticated = true; + this.userData = AuthService.decodeAccessToken(this.tokenData.access_token); + this.tokenExpirationDate = new Date(this.userData.exp * 1000); + }, + err => { + console.log(err); + } + ); + } + } + public logout():any { this.tokenData = new Oauth2TokenData(); this.userData = null; @@ -84,6 +121,33 @@ export class AuthService { return this.tokenExpirationDate; } + public hasRole(role:string):boolean { + if (this.isAuthenticated()) { + return this.getUserData()['authorities'].indexOf(role) >= 0; + } + return false; + } + + public hasAnyRole(roles:string[]):boolean { + var ok = false; + roles.forEach(role => { + if (this.hasRole(role)) { + ok = true; + } + }); + return ok; + } + + public canView(view:AppMenuItem):boolean { + var ok = false; + if (!view.roles) { + ok = true; + } else { + ok = this.hasAnyRole(view.roles); + } + return ok; + } + public getAuthorizationHeaders():Headers { var authorizationHeaders = new Headers(); if (this.authenticated) { @@ -92,6 +156,7 @@ export class AuthService { return authorizationHeaders; } + private fetchUserData() { this.http.get('/api/user', {headers: this.getAuthorizationHeaders()}) .subscribe( @@ -102,9 +167,6 @@ export class AuthService { ); } - private decodeAccessToken(access_token:string) { - return JSON.parse(window.atob(access_token.split('.')[1])); - } } class Oauth2TokenData { diff --git a/shardis-ui/src/main/frontend/app/shared/auth/index.ts b/shardis-ui/src/main/frontend/app/shared/auth/index.ts new file mode 100644 index 0000000..cf52c9c --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/auth/index.ts @@ -0,0 +1 @@ +export * from './auth.service' diff --git a/shardis-ui/src/main/frontend/app/shared/guards/admin.guard.ts b/shardis-ui/src/main/frontend/app/shared/guards/admin.guard.ts new file mode 100644 index 0000000..03413df --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/guards/admin.guard.ts @@ -0,0 +1,17 @@ +import {Injectable} from '@angular/core'; +import {CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router'; +import {AuthService} from '../auth'; + +@Injectable() +export class AdminGuard implements CanActivate { + constructor(private authService:AuthService, private router:Router) { + } + + canActivate(next:ActivatedRouteSnapshot, state:RouterStateSnapshot) { + if (this.authService.hasRole('ROLE_ADMIN')) { + return true; + } + this.router.navigate(['accessDenied']); + return false; + } +} diff --git a/shardis-ui/src/main/frontend/app/shared/guards/authenticated.guard.ts b/shardis-ui/src/main/frontend/app/shared/guards/authenticated.guard.ts new file mode 100644 index 0000000..72dc03d --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/guards/authenticated.guard.ts @@ -0,0 +1,17 @@ +import {Injectable} from '@angular/core'; +import {CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router'; +import {AuthService} from '../auth'; + +@Injectable() +export class AuthenticatedGuard implements CanActivate { + constructor(private authService:AuthService, private router:Router) { + } + + canActivate(next:ActivatedRouteSnapshot, state:RouterStateSnapshot) { + if (this.authService.isAuthenticated()) { + return true; + } + this.router.navigate(['login']); + return false; + } +} diff --git a/shardis-ui/src/main/frontend/app/shared/guards/index.ts b/shardis-ui/src/main/frontend/app/shared/guards/index.ts new file mode 100644 index 0000000..53a3b6e --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/guards/index.ts @@ -0,0 +1,5 @@ +export * from './admin.guard' +export * from './authenticated.guard' +export * from './unauthenticated.guard' + + diff --git a/shardis-ui/src/main/frontend/app/shared/guards/unauthenticated.guard.ts b/shardis-ui/src/main/frontend/app/shared/guards/unauthenticated.guard.ts new file mode 100644 index 0000000..e1938b9 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/guards/unauthenticated.guard.ts @@ -0,0 +1,17 @@ +import {Injectable} from '@angular/core'; +import {CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router'; +import {AuthService} from '../auth'; + +@Injectable() +export class UnauthenticatedGuard implements CanActivate { + constructor(private authService:AuthService, private router:Router) { + } + + canActivate(next:ActivatedRouteSnapshot, state:RouterStateSnapshot) { + if (!this.authService.isAuthenticated()) { + return true; + } + this.router.navigate(['home']); + return false; + } +} diff --git a/shardis-ui/src/main/frontend/app/shared/index.ts b/shardis-ui/src/main/frontend/app/shared/index.ts new file mode 100644 index 0000000..8d6d6c0 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/index.ts @@ -0,0 +1,3 @@ +export * from './x-large' +export * from './auth' +export * from './guards' diff --git a/shardis-ui/src/main/frontend/app/shared/testing/index.ts b/shardis-ui/src/main/frontend/app/shared/testing/index.ts new file mode 100644 index 0000000..398cae7 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/testing/index.ts @@ -0,0 +1 @@ +export * from './router' diff --git a/shardis-ui/src/main/frontend/app/shared/testing/router.ts b/shardis-ui/src/main/frontend/app/shared/testing/router.ts new file mode 100644 index 0000000..c7104a3 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/testing/router.ts @@ -0,0 +1,16 @@ +import {Router, ActivatedRoute} from '@angular/router'; +class MockRouter { +} +class MockActivatedRoute { +} + +export const TEST_ROUTER_PROVIDERS = [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: ActivatedRoute, + useClass: MockActivatedRoute + } +]; diff --git a/shardis-ui/src/main/frontend/app/shared/x-large/index.ts b/shardis-ui/src/main/frontend/app/shared/x-large/index.ts new file mode 100644 index 0000000..406b414 --- /dev/null +++ b/shardis-ui/src/main/frontend/app/shared/x-large/index.ts @@ -0,0 +1 @@ +export * from './x-large.directive' diff --git a/shardis-ui/src/main/frontend/app/directives/x-large.directive.spec.ts b/shardis-ui/src/main/frontend/app/shared/x-large/x-large.directive.spec.ts similarity index 68% rename from shardis-ui/src/main/frontend/app/directives/x-large.directive.spec.ts rename to shardis-ui/src/main/frontend/app/shared/x-large/x-large.directive.spec.ts index 2bcb56d..e6bf507 100644 --- a/shardis-ui/src/main/frontend/app/directives/x-large.directive.spec.ts +++ b/shardis-ui/src/main/frontend/app/shared/x-large/x-large.directive.spec.ts @@ -1,18 +1,9 @@ -import { - it, - inject, - async, - describe, - beforeEachProviders, -} from '@angular/core/testing'; - -import {Component, provide} from '@angular/core'; -import {BaseRequestOptions, Http} from '@angular/http'; -import {MockBackend} from '@angular/http/testing'; +import {it, inject, async, describe} from '@angular/core/testing'; +import {Component} from '@angular/core'; import {TestComponentBuilder} from '@angular/compiler/testing'; +import {XLarge} from './x-large.directive.ts'; // Load the implementations that should be tested -import {XLarge} from './x-large.directive.ts'; describe('x-large directive', () => { // Create a test component to test directives @@ -25,7 +16,7 @@ describe('x-large directive', () => { it('should sent font-size to x-large', async(inject([TestComponentBuilder], (tcb) => { return tcb.overrideTemplate(TestComponent, '
    Content
    ') - .createAsync(TestComponent).then((fixture: any) => { + .createAsync(TestComponent).then((fixture:any) => { fixture.detectChanges(); let compiled = fixture.debugElement.nativeElement.children[0]; expect(compiled.style.fontSize).toBe('x-large'); diff --git a/shardis-ui/src/main/frontend/app/directives/x-large.directive.ts b/shardis-ui/src/main/frontend/app/shared/x-large/x-large.directive.ts similarity index 83% rename from shardis-ui/src/main/frontend/app/directives/x-large.directive.ts rename to shardis-ui/src/main/frontend/app/shared/x-large/x-large.directive.ts index c954584..89d05a1 100644 --- a/shardis-ui/src/main/frontend/app/directives/x-large.directive.ts +++ b/shardis-ui/src/main/frontend/app/shared/x-large/x-large.directive.ts @@ -1,4 +1,4 @@ -import {Directive, Component, ElementRef, Renderer} from '@angular/core'; +import {Directive, ElementRef, Renderer} from '@angular/core'; /* * Directive * XLarge is a simple directive to show how one is made @@ -7,7 +7,7 @@ import {Directive, Component, ElementRef, Renderer} from '@angular/core'; selector: '[x-large]' // using [ ] means selecting attributes }) export class XLarge { - constructor(element: ElementRef, renderer: Renderer) { + constructor(element:ElementRef, renderer:Renderer) { // simple DOM manipulation to set font size to x-large // `nativeElement` is the direct reference to the DOM element // element.nativeElement.style.fontSize = 'x-large'; diff --git a/shardis-ui/src/main/frontend/app/utils/lazy.loader.ts b/shardis-ui/src/main/frontend/app/utils/lazy.loader.ts deleted file mode 100644 index b9c682c..0000000 --- a/shardis-ui/src/main/frontend/app/utils/lazy.loader.ts +++ /dev/null @@ -1,29 +0,0 @@ -export enum LazyModule { - ABOUT, - PLAYGROUND, - KITCHENSINK -} - -export class LazyLoader { - - static lazyLoad(lazymodule:LazyModule):Promise { - - var resolver = (resolve, reject) => { - require.ensure([], (require) => { - switch (lazymodule) { - case LazyModule.ABOUT: - resolve(require('../views/about/about.component.ts')); - break; - case LazyModule.PLAYGROUND: - resolve(require('../views/playground/playground.component.ts')); - break; - case LazyModule.KITCHENSINK: - resolve(require('../views/kitchensink/kitchensink.component.ts')); - break; - } - }); - }; - - return new Promise(resolver); - } -} diff --git a/shardis-ui/src/main/frontend/app/views/playground/heroes/hero.ts b/shardis-ui/src/main/frontend/app/views/playground/heroes/hero.ts deleted file mode 100644 index a61b497..0000000 --- a/shardis-ui/src/main/frontend/app/views/playground/heroes/hero.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Hero { - id: number; - name: string; -} diff --git a/shardis-ui/src/main/frontend/typings/custom_typings.d.ts b/shardis-ui/src/main/frontend/custom_typings/custom_typings.d.ts similarity index 100% rename from shardis-ui/src/main/frontend/typings/custom_typings.d.ts rename to shardis-ui/src/main/frontend/custom_typings/custom_typings.d.ts diff --git a/shardis-ui/src/main/frontend/main.ts b/shardis-ui/src/main/frontend/main.ts index 9f4c6c8..4bd53ab 100644 --- a/shardis-ui/src/main/frontend/main.ts +++ b/shardis-ui/src/main/frontend/main.ts @@ -2,14 +2,14 @@ * Providers provided by Angular */ import {bootstrap} from '@angular/platform-browser-dynamic'; +import {DIRECTIVES, PIPES, PROVIDERS, ENV_PROVIDERS} from './platform'; +import {App} from './app'; +import {decorateComponentRef} from './platform/environment'; /* * App Component * our top level component that holds all of our components */ -import {DIRECTIVES, PIPES, PROVIDERS} from './platform/browser'; -import {ENV_PROVIDERS} from './platform/environment'; -import {App} from './app/app.component'; /* * Load styles @@ -29,6 +29,7 @@ export function main() { ...DIRECTIVES, ...PIPES ]) + .then(decorateComponentRef) //angular debug tools .catch(err => console.error(err)); } diff --git a/shardis-ui/src/main/frontend/platform/browser/directives.ts b/shardis-ui/src/main/frontend/platform/browser/directives.ts index feb7c0a..1121010 100644 --- a/shardis-ui/src/main/frontend/platform/browser/directives.ts +++ b/shardis-ui/src/main/frontend/platform/browser/directives.ts @@ -1,17 +1,19 @@ -import {provide, PLATFORM_DIRECTIVES} from '@angular/core'; +import {PLATFORM_DIRECTIVES} from '@angular/core'; +import {MATERIAL_DIRECTIVES} from './material2'; import {ROUTER_DIRECTIVES} from '@angular/router'; -import {MATERIAL_DIRECTIVES} from './material2/material2'; -import {RouterActive} from '../../app/directives/router-active.directive'; - -// Angular 2 Router +import {REACTIVE_FORM_DIRECTIVES} from '@angular/forms'; // application_directives: directives that are global through out the application export const APPLICATION_DIRECTIVES = [ - ...ROUTER_DIRECTIVES, ...MATERIAL_DIRECTIVES, - RouterActive + ...ROUTER_DIRECTIVES, + ...REACTIVE_FORM_DIRECTIVES ]; export const DIRECTIVES = [ - provide(PLATFORM_DIRECTIVES, {useValue: APPLICATION_DIRECTIVES, multi: true}) + { + provide: PLATFORM_DIRECTIVES, + useValue: APPLICATION_DIRECTIVES, + multi: true + } ]; diff --git a/shardis-ui/src/main/frontend/platform/browser/material2/index.ts b/shardis-ui/src/main/frontend/platform/browser/material2/index.ts new file mode 100644 index 0000000..7c40c96 --- /dev/null +++ b/shardis-ui/src/main/frontend/platform/browser/material2/index.ts @@ -0,0 +1 @@ +export * from './material2' diff --git a/shardis-ui/src/main/frontend/platform/browser/material2/material2.ts b/shardis-ui/src/main/frontend/platform/browser/material2/material2.ts index cb7d280..35d3097 100644 --- a/shardis-ui/src/main/frontend/platform/browser/material2/material2.ts +++ b/shardis-ui/src/main/frontend/platform/browser/material2/material2.ts @@ -7,11 +7,12 @@ import {MD_INPUT_DIRECTIVES} from '@angular2-material/input'; import {MD_LIST_DIRECTIVES} from '@angular2-material/list'; import {MdProgressBar} from '@angular2-material/progress-bar'; import {MdProgressCircle, MdSpinner} from '@angular2-material/progress-circle'; -import {MdRadioButton, MdRadioDispatcher, MdRadioGroup} from '@angular2-material/radio'; +import {MdRadioButton, MdRadioGroup} from '@angular2-material/radio'; import {MD_SIDENAV_DIRECTIVES} from '@angular2-material/sidenav'; import {MD_SLIDE_TOGGLE_DIRECTIVES} from '@angular2-material/slide-toggle'; import {MD_TABS_DIRECTIVES} from '@angular2-material/tabs'; import {MdToolbar} from '@angular2-material/toolbar'; +import {MdUniqueSelectionDispatcher} from '@angular2-material/core'; /* * we are grouping the module so we only need to manage the imports in one location */ @@ -42,5 +43,5 @@ export const MATERIAL_DIRECTIVES = [ export const MATERIAL_PROVIDERS = [ MdIconRegistry, - MdRadioDispatcher + MdUniqueSelectionDispatcher ]; diff --git a/shardis-ui/src/main/frontend/platform/browser/pipes.ts b/shardis-ui/src/main/frontend/platform/browser/pipes.ts index 3458f1c..a805209 100644 --- a/shardis-ui/src/main/frontend/platform/browser/pipes.ts +++ b/shardis-ui/src/main/frontend/platform/browser/pipes.ts @@ -1,10 +1,14 @@ -import {provide, PLATFORM_PIPES} from '@angular/core'; -import {MATERIAL_PIPES} from './material2/material2'; +import {PLATFORM_PIPES} from '@angular/core'; +import {MATERIAL_PIPES} from './material2'; // application_pipes: pipes that are global through out the application export const APPLICATION_PIPES = []; export const PIPES = [ ...MATERIAL_PIPES, - provide(PLATFORM_PIPES, {useValue: APPLICATION_PIPES, multi: true}) + { + provide: PLATFORM_PIPES, + useValue: APPLICATION_PIPES, + multi: true + } ]; diff --git a/shardis-ui/src/main/frontend/platform/browser/providers.ts b/shardis-ui/src/main/frontend/platform/browser/providers.ts index 31285f7..48c25b3 100644 --- a/shardis-ui/src/main/frontend/platform/browser/providers.ts +++ b/shardis-ui/src/main/frontend/platform/browser/providers.ts @@ -1,29 +1,30 @@ -import {provide} from '@angular/core'; -import {FORM_PROVIDERS} from '@angular/common'; +import {FORM_PROVIDERS, LocationStrategy, PathLocationStrategy} from '@angular/common'; import {HTTP_PROVIDERS} from '@angular/http'; -import {ROUTER_PROVIDERS} from '@angular/router'; -import {LocationStrategy} from '@angular/common/src/location/location_strategy'; -import {PathLocationStrategy} from '@angular/common/src/location/path_location_strategy'; -import {MATERIAL_PROVIDERS} from './material2/material2'; +import {MATERIAL_PROVIDERS} from './material2'; import {NG2_WEBSTORAGE} from 'ng2-webstorage'; - -// Angular 2 - -// Angular 2 Http -// Angular 2 Router -// ng2-webstorage +import {asyncRoutes, routes, AUTH_PROVIDERS} from '../../app/app.routes'; +import {provideWebpack} from './webpack/webpack-component-resolver'; +import {provideRouter} from '@angular/router'; +import {disableDeprecatedForms, provideForms} from '@angular/forms'; /* * Application Providers/Directives/Pipes * providers/directives/pipes that only live in our browser environment */ export const APPLICATION_PROVIDERS = [ - ...ROUTER_PROVIDERS, ...FORM_PROVIDERS, ...HTTP_PROVIDERS, ...MATERIAL_PROVIDERS, ...NG2_WEBSTORAGE, - provide(LocationStrategy, {useClass: PathLocationStrategy as any}), + disableDeprecatedForms(), + provideForms(), + provideRouter(routes), + ...AUTH_PROVIDERS, + provideWebpack(asyncRoutes), + { + provide: LocationStrategy, + useClass: PathLocationStrategy as any + }, ]; export const PROVIDERS = [ diff --git a/shardis-ui/src/main/frontend/platform/browser/webpack/index.ts b/shardis-ui/src/main/frontend/platform/browser/webpack/index.ts new file mode 100644 index 0000000..dee2c72 --- /dev/null +++ b/shardis-ui/src/main/frontend/platform/browser/webpack/index.ts @@ -0,0 +1 @@ +export * from './webpack-component-resolver'; diff --git a/shardis-ui/src/main/frontend/platform/browser/webpack/lazy-loader.ts b/shardis-ui/src/main/frontend/platform/browser/webpack/lazy-loader.ts new file mode 100644 index 0000000..03eaa75 --- /dev/null +++ b/shardis-ui/src/main/frontend/platform/browser/webpack/lazy-loader.ts @@ -0,0 +1,36 @@ +export const ASYNC_ROUTES = { + About: 'About', + Playground: 'Playground', + Crud: 'Crud' +}; + +export class LazyLoader { + + static lazyLoad(lazymodule:string):Promise { + + var resolver = (resolve, reject) => { + + switch (lazymodule) { + case ASYNC_ROUTES.About: + require.ensure([], (require) => { + resolve(require('../../../app/+about/index')); + }); + break; + case ASYNC_ROUTES.Playground: + require.ensure([], (require) => { + resolve(require('../../../app/+playground/index')); + }); + break; + case ASYNC_ROUTES.Crud: + require.ensure([], (require) => { + resolve(require('../../../app/+crud/index')); + }); + break; + default: + reject(); + } + }; + + return new Promise(resolver); + } +} diff --git a/shardis-ui/src/main/frontend/platform/browser/webpack/webpack-component-resolver.ts b/shardis-ui/src/main/frontend/platform/browser/webpack/webpack-component-resolver.ts new file mode 100644 index 0000000..8389bd6 --- /dev/null +++ b/shardis-ui/src/main/frontend/platform/browser/webpack/webpack-component-resolver.ts @@ -0,0 +1,56 @@ +import { ComponentResolver, Injectable, Inject } from '@angular/core'; +import { RuntimeCompiler } from '@angular/compiler'; +import {LazyLoader} from './lazy-loader'; + +export const AC_WEBPACK_ASYNC_MAP = {}; + + +export const ANGULARCLASS_WEBPACK_RUNTIME_PROVIDERS = [ + { provide: AC_WEBPACK_ASYNC_MAP, useValue: {} }, + { + provide: ComponentResolver, + useFactory: (resolver, asyncMap) => { + return new WebpackComponentResolver(resolver, asyncMap); + }, + deps: [RuntimeCompiler, AC_WEBPACK_ASYNC_MAP] + } +]; + +export function provideWebpack(config) { + return [ + ...ANGULARCLASS_WEBPACK_RUNTIME_PROVIDERS, + { provide: AC_WEBPACK_ASYNC_MAP, useValue: config }, + ]; +} + +@Injectable() +export class WebpackComponentResolver { + constructor( + private _resolver: ComponentResolver, + @Inject(AC_WEBPACK_ASYNC_MAP) private _asyncComponents: any) { + + } + + resolveComponent(componentType: any) { + if (typeof componentType === 'string' && this._asyncComponents[componentType]) { + return this._fetchComponent(componentType); + } + return this._resolver.resolveComponent(componentType); + } + + + prefetchComponent(componentType) { + return this._fetchComponent(componentType); + } + + + clearCache(): void {} + + private _fetchComponent(componentType) { + return LazyLoader.lazyLoad(this._asyncComponents[componentType]) + .then(m => m[componentType]) + .then(cmp => { + return this._resolver.resolveComponent(cmp); + }); + } +} diff --git a/shardis-ui/src/main/frontend/platform/environment.ts b/shardis-ui/src/main/frontend/platform/environment.ts index f156841..7a98763 100644 --- a/shardis-ui/src/main/frontend/platform/environment.ts +++ b/shardis-ui/src/main/frontend/platform/environment.ts @@ -1,11 +1,17 @@ // Angular 2 import {enableProdMode} from '@angular/core'; +import {disableDebugTools, enableDebugTools} from '@angular/platform-browser'; // Environment Providers var PROVIDERS = []; +// Angular debug tools in the dev console +// https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md +let _decorateComponentRef = function identity(value) { return value; }; + if ('production' === ENV) { // Production + disableDebugTools(); enableProdMode(); PROVIDERS = [ @@ -13,6 +19,9 @@ if ('production' === ENV) { ]; } else { + + _decorateComponentRef = (cmpRef) => enableDebugTools(cmpRef); + // Development PROVIDERS = [ ...PROVIDERS @@ -20,6 +29,7 @@ if ('production' === ENV) { } +export const decorateComponentRef = _decorateComponentRef; export const ENV_PROVIDERS = [ ...PROVIDERS diff --git a/shardis-ui/src/main/frontend/platform/index.ts b/shardis-ui/src/main/frontend/platform/index.ts new file mode 100644 index 0000000..d124f0f --- /dev/null +++ b/shardis-ui/src/main/frontend/platform/index.ts @@ -0,0 +1,2 @@ +export * from './browser' +export * from './environment' diff --git a/shardis-ui/src/main/frontend/polyfills.ts b/shardis-ui/src/main/frontend/polyfills.ts index a8bf4c5..cc4ab1e 100644 --- a/shardis-ui/src/main/frontend/polyfills.ts +++ b/shardis-ui/src/main/frontend/polyfills.ts @@ -1,12 +1,12 @@ // Ccre.js import 'core-js/es6'; import 'core-js/es7/reflect'; +import 'ts-helpers'; // Zone.js require('zone.js/dist/zone'); // Typescript emit helpers polyfill -import 'ts-helpers'; if ('production' !== ENV) { Error['stackTraceLimit'] = Infinity; diff --git a/shardis-ui/src/main/frontend/vendor.ts b/shardis-ui/src/main/frontend/vendor.ts index 40eeece..d3dd071 100644 --- a/shardis-ui/src/main/frontend/vendor.ts +++ b/shardis-ui/src/main/frontend/vendor.ts @@ -4,8 +4,8 @@ import '@angular/platform-browser-dynamic'; import '@angular/core'; import '@angular/common'; import '@angular/http'; +import '@angular/forms'; import '@angular/router'; -// Material import import '@angular2-material/button'; import '@angular2-material/card'; import '@angular2-material/checkbox'; @@ -21,8 +21,9 @@ import '@angular2-material/sidenav'; import '@angular2-material/slide-toggle'; import '@angular2-material/tabs'; import '@angular2-material/toolbar'; -// RxJS import 'rxjs/Rx'; +// Material import +// RxJS if ('production' === ENV) { // Production diff --git a/shardis-ui/tsconfig.json b/shardis-ui/tsconfig.json index 627d7d5..823362a 100644 --- a/shardis-ui/tsconfig.json +++ b/shardis-ui/tsconfig.json @@ -12,10 +12,6 @@ "noEmitHelpers": true, "noImplicitAny": false }, - "awesomeTypescriptLoaderOptions": { - "resolveGlobs": true, - "forkChecker": true - }, "exclude": [ "node_modules" ] diff --git a/shardis-ui/typings.json b/shardis-ui/typings.json index a947d94..a6350bc 100644 --- a/shardis-ui/typings.json +++ b/shardis-ui/typings.json @@ -1,14 +1,10 @@ { "globalDependencies": { "angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459", - "core-js": "registry:dt/core-js#0.0.0+20160317120654", - "hammerjs": "registry:dt/hammerjs#2.0.4+20160417130828", - "jasmine": "registry:dt/jasmine#2.2.0+20160505161446", - "node": "registry:dt/node#6.0.0+20160514165920", - "selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654", - "source-map": "registry:dt/source-map#0.0.0+20160317120654", - "uglify-js": "registry:dt/uglify-js#2.6.1+20160316155526", - "webpack": "registry:dt/webpack#1.12.9+20160321060707" + "core-js": "registry:dt/core-js#0.0.0+20160602141332", + "hammerjs": "registry:dt/hammerjs#2.0.4+20160622233604", + "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", + "node": "registry:dt/node#6.0.0+20160621231320", + "selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654" } - }