Compare commits

..

14 Commits

Author SHA1 Message Date
fabio.formosa
df59999034 moved admin credentials into application context 2019-04-26 12:31:10 +02:00
fabio.formosa
1e6f89678c minor commit 2019-04-26 12:18:21 +02:00
fabio.formosa
4103779e4e Merge branch 'master' of https://github.com/fabioformosa/quartz-manager.git 2019-04-26 11:53:03 +02:00
fabio.formosa
919393e58c fixed tests 2019-04-26 11:52:48 +02:00
Fabio Formosa
a1f279c161 Update README.md 2019-04-24 17:05:48 +02:00
Fabio Formosa
03a4d68993 Update README.md 2019-04-24 17:05:15 +02:00
fabio.formosa
cb6dd7933a Merge branch 'master' of https://github.com/fabioformosa/quartz-manager.git 2019-04-24 17:02:24 +02:00
fabio.formosa
bcff3ebce7 upgraded frontend to angular ver 7 2019-04-24 17:02:04 +02:00
Fabio Formosa
8ec980763b Update README.md 2019-04-22 20:30:41 +02:00
fabio.formosa
139e6871bf upgraded spring boot to ver 2.x 2019-04-22 20:28:48 +02:00
Fabio Formosa
bde642cf52 improved log pattern 2018-06-22 15:09:46 +02:00
Fabio Formosa
fc8101aa40 Update README.md 2018-06-22 14:40:26 +02:00
Fabio Formosa
8dfd627c23 Update README.md 2018-06-22 14:37:18 +02:00
Fabio Formosa
3199437c8c added screenshot 2018-06-22 14:34:55 +02:00
40 changed files with 392 additions and 268 deletions

View File

@@ -1,10 +1,10 @@
#QUARTZ MANAGER # QUARTZ MANAGER
GUI Manager for Quartz Scheduler. GUI Manager for Quartz Scheduler.
Through this webapp you can launch and control your scheduled job. The GUI Console is composed by a managament panel to set trigger, start/stop scheduler and a log panel with a progress bar to display the job output. Through this webapp you can launch and control your scheduled job. The GUI Console is composed by a managament panel to set trigger, start/stop scheduler and a log panel with a progress bar to display the job output.
## SCREENSHOT ## SCREENSHOT
![Alt text](http://www.fabioformosa.it/quartz-manager/quartz-manager-2-screenshot_800.png "Quartz Manager Screenshot") ![](https://github.com/fabioformosa/quartz-manager/blob/master/quartz-manager-backend/src/main/resources/quartz-manager-2-screenshot_800.PNG)
## HOW IT WORKS ## HOW IT WORKS
* Set up the trigger into the left sidebar in terms of: daily frequency and and max occurrences. * Set up the trigger into the left sidebar in terms of: daily frequency and and max occurrences.
@@ -51,17 +51,17 @@ Replace the dummy job (class: `it.fabioformosa.quartzmanager.jobs.SampleJob`) wi
## Tech Overview ## Tech Overview
**Backend Stack** Java 8, Spring Boot 1.5.9 (Spring MVC 4.3.13, Spring Security 4.2.3, Spring AOP 4.3.13), Quartz Scheduler 2.2.2 **Backend Stack** Java 8, Spring Boot 2.1.4 (Spring MVC 5.1.6, Spring Security 5.1.5, Spring AOP 5.1.6), Quartz Scheduler 2.3.1
**Application Server** Tomcat (embedded) **Application Server** Tomcat (embedded)
**Frontend** Angular 5.2.0, Web-Socket (stompjs 2.3.3) **Frontend** Angular 7.2.13, Web-Socket (stompjs 2.3.3)
**Style** angular material, FontAwesome 5 **Style** angular material, FontAwesome 5
From quartz manager ver 2.x.x, the new structure of project is: From quartz manager ver 2.x.x, the new structure of project is:
* REST backend (java based, using [http://www.quartz-scheduler.org/](http://www.quartz-scheduler.org/) * REST backend (java based, using [http://www.quartz-scheduler.org/](http://www.quartz-scheduler.org/)
* Single Page Application frontend (angular 5) * Single Page Application frontend (angular 7)
(The previous version of quartz manager was a monolithic backend that provided also frontend developed with angularjs 1.6.x. You can find it at the branch 1.x.x) (The previous version of quartz manager was a monolithic backend that provided also frontend developed with angularjs 1.6.x. You can find it at the branch 1.x.x)

View File

@@ -5,7 +5,7 @@
<groupId>it.fabioformosa</groupId> <groupId>it.fabioformosa</groupId>
<artifactId>quartz-manager</artifactId> <artifactId>quartz-manager</artifactId>
<version>2.0.1-SNAPSHOT</version> <version>2.1.1-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<name>quartz-manager</name> <name>quartz-manager</name>
@@ -13,8 +13,8 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-parent</artifactId>
<version>1.5.9.RELEASE</version> <version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
@@ -104,14 +104,12 @@
<dependency> <dependency>
<groupId>io.rest-assured</groupId> <groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId> <artifactId>spring-mock-mvc</artifactId>
<version>3.0.5</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.quartz-scheduler</groupId> <groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId> <artifactId>quartz</artifactId>
<version>2.2.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
@@ -127,15 +125,16 @@
<dependency> <dependency>
<groupId>io.projectreactor</groupId> <groupId>io.projectreactor</groupId>
<artifactId>reactor-net</artifactId> <artifactId>reactor-net</artifactId>
<version>2.0.8.RELEASE</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.projectreactor.spring</groupId> <groupId>io.projectreactor.spring</groupId>
<artifactId>reactor-spring-context</artifactId> <artifactId>reactor-spring-context</artifactId>
<version>2.0.7.RELEASE</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>4.0.31.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -163,25 +162,34 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.codehaus.gmavenplus</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>gmavenplus-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>1.5</version> <version>3.8.0</version>
<executions> <configuration>
<execution> <source>1.8</source>
<goals> <target>1.8</target>
<goal>addSources</goal> </configuration>
<goal>addTestSources</goal> </plugin>
<goal>generateStubs</goal> <!-- <plugin> -->
<goal>compile</goal> <!-- <groupId>org.codehaus.gmavenplus</groupId> -->
<goal>testGenerateStubs</goal> <!-- <artifactId>gmavenplus-plugin</artifactId> -->
<goal>testCompile</goal> <!-- <version>1.5</version> -->
<goal>removeStubs</goal> <!-- <executions> -->
<goal>removeTestStubs</goal> <!-- <execution> -->
</goals> <!-- <goals> -->
</execution> <!-- <goal>addSources</goal> -->
</executions> <!-- <goal>addTestSources</goal> -->
</plugin> <!-- <goal>generateStubs</goal> -->
<!-- <goal>compile</goal> -->
<!-- <goal>testGenerateStubs</goal> -->
<!-- <goal>testCompile</goal> -->
<!-- <goal>removeStubs</goal> -->
<!-- <goal>removeTestStubs</goal> -->
<!-- </goals> -->
<!-- </execution> -->
<!-- </executions> -->
<!-- </plugin> -->
</plugins> </plugins>
</pluginManagement> </pluginManagement>
</build> </build>

View File

@@ -1,7 +1,7 @@
package it.fabioformosa; package it.fabioformosa;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer { public class ServletInitializer extends SpringBootServletInitializer {

View File

@@ -3,6 +3,7 @@ package it.fabioformosa.quartzmanager.configuration;
import java.io.IOException; import java.io.IOException;
import java.util.Properties; import java.util.Properties;
import org.quartz.Job;
import org.quartz.JobDetail; import org.quartz.JobDetail;
import org.quartz.SimpleTrigger; import org.quartz.SimpleTrigger;
import org.quartz.Trigger; import org.quartz.Trigger;
@@ -28,66 +29,66 @@ import it.fabioformosa.quartzmanager.scheduler.TriggerMonitorImpl;
@ConditionalOnProperty(name = "quartz.enabled") @ConditionalOnProperty(name = "quartz.enabled")
public class SchedulerConfig { public class SchedulerConfig {
private static JobDetailFactoryBean createJobDetail(Class<?> jobClass) { private static JobDetailFactoryBean createJobDetail(Class<? extends Job> jobClass) {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(jobClass); factoryBean.setJobClass(jobClass);
factoryBean.setDurability(false); factoryBean.setDurability(false);
return factoryBean; return factoryBean;
} }
private static SimpleTriggerFactoryBean createTrigger(JobDetail jobDetail, long pollFrequencyMs, private static SimpleTriggerFactoryBean createTrigger(JobDetail jobDetail, long pollFrequencyMs,
int repeatCount) { int repeatCount) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean(); SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail); factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(0L); factoryBean.setStartDelay(0L);
factoryBean.setRepeatInterval(pollFrequencyMs); factoryBean.setRepeatInterval(pollFrequencyMs);
factoryBean.setRepeatCount(repeatCount); factoryBean.setRepeatCount(repeatCount);
factoryBean factoryBean
.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT);// in case of misfire, ignore all missed triggers and continue .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT);// in case of misfire, ignore all missed triggers and continue
return factoryBean; return factoryBean;
} }
@Bean(name = "triggerMonitor") @Bean(name = "triggerMonitor")
public TriggerMonitor createTriggerMonitor(@Qualifier("jobTrigger") Trigger trigger) { public TriggerMonitor createTriggerMonitor(@Qualifier("jobTrigger") Trigger trigger) {
TriggerMonitor triggerMonitor = new TriggerMonitorImpl(); TriggerMonitor triggerMonitor = new TriggerMonitorImpl();
triggerMonitor.setTrigger(trigger); triggerMonitor.setTrigger(trigger);
return triggerMonitor; return triggerMonitor;
} }
@Bean @Bean
public JobDetailFactoryBean jobDetail() { public JobDetailFactoryBean jobDetail() {
return createJobDetail(SampleJob.class); return createJobDetail(SampleJob.class);
} }
@Bean @Bean
public JobFactory jobFactory(ApplicationContext applicationContext) { public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext); jobFactory.setApplicationContext(applicationContext);
return jobFactory; return jobFactory;
} }
@Bean @Bean
public Properties quartzProperties() throws IOException { public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet(); propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject(); return propertiesFactoryBean.getObject();
} }
@Bean(name = "jobTrigger") @Bean(name = "jobTrigger")
public SimpleTriggerFactoryBean sampleJobTrigger(@Qualifier("jobDetail") JobDetail jobDetail, public SimpleTriggerFactoryBean sampleJobTrigger(@Qualifier("jobDetail") JobDetail jobDetail,
@Value("${job.frequency}") long frequency, @Value("${job.repeatCount}") int repeatCount) { @Value("${job.frequency}") long frequency, @Value("${job.repeatCount}") int repeatCount) {
return createTrigger(jobDetail, frequency, repeatCount); return createTrigger(jobDetail, frequency, repeatCount);
} }
@Bean(name = "scheduler") @Bean(name = "scheduler")
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory,
@Qualifier("jobTrigger") Trigger sampleJobTrigger) throws IOException { @Qualifier("jobTrigger") Trigger sampleJobTrigger) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean(); SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory); factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties()); factory.setQuartzProperties(quartzProperties());
factory.setTriggers(sampleJobTrigger); factory.setTriggers(sampleJobTrigger);
factory.setAutoStartup(false); factory.setAutoStartup(false);
return factory; return factory;
} }
} }

View File

@@ -3,6 +3,7 @@ package it.fabioformosa.quartzmanager.configuration;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -11,6 +12,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import it.fabioformosa.quartzmanager.security.ComboEntryPoint; import it.fabioformosa.quartzmanager.security.ComboEntryPoint;
@@ -67,9 +70,17 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
} }
@Value("${quartz-manager.account.user}")
private String adminUser;
@Value("${quartz-manager.account.pwd}")
private String adminPwd;
@Autowired @Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN"); PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication().withUser(adminUser).password(encoder.encode(adminPwd)).roles("ADMIN");
} }
} }

View File

@@ -17,9 +17,7 @@ public class AuthorityServiceImpl implements AuthorityService {
@Override @Override
public List<Authority> findById(Long id) { public List<Authority> findById(Long id) {
// TODO Auto-generated method stub Authority auth = this.authorityRepository.getOne(id);
Authority auth = this.authorityRepository.findOne(id);
List<Authority> auths = new ArrayList<>(); List<Authority> auths = new ArrayList<>();
auths.add(auth); auths.add(auth);
return auths; return auths;
@@ -27,7 +25,6 @@ public class AuthorityServiceImpl implements AuthorityService {
@Override @Override
public List<Authority> findByname(String name) { public List<Authority> findByname(String name) {
// TODO Auto-generated method stub
Authority auth = this.authorityRepository.findByName(name); Authority auth = this.authorityRepository.findByName(name);
List<Authority> auths = new ArrayList<>(); List<Authority> auths = new ArrayList<>();
auths.add(auth); auths.add(auth);

View File

@@ -37,7 +37,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
public User findById(Long id) throws AccessDeniedException { public User findById(Long id) throws AccessDeniedException {
User u = userRepository.findOne(id); User u = userRepository.getOne(id);
return u; return u;
} }

View File

@@ -1,7 +1,11 @@
server: server:
context-path: /quartz-manager servlet:
context-path: /quartz-manager
session.timeout : 28800
port: 8080 port: 8080
session.timeout : 28800
app:
name: quartz-manager
spring: spring:
thymeleaf: thymeleaf:
@@ -15,16 +19,19 @@ job:
frequency: 4000 frequency: 4000
repeatCount: 19 repeatCount: 19
logging:
level:
org.springframework.web: WARN
it.fabioformosa: INFO
app:
name: quartz-manager
jwt: jwt:
header: Authorization header: Authorization
expires_in: 600 # 10 minutes expires_in: 600 # 10 minutes
secret: queenvictoria secret: queenvictoria
cookie: AUTH-TOKEN cookie: AUTH-TOKEN
logging:
level:
org.springframework.web: WARN
it.fabioformosa: INFO
quartz-manager:
account:
user: admin
pwd: admin

View File

@@ -1,7 +1,7 @@
-- the password hash is generated by BCrypt Calculator Generator(https://www.dailycred.com/article/bcrypt-calculator) -- the password hash is generated by BCrypt Calculator Generator(https://www.dailycred.com/article/bcrypt-calculator)
INSERT INTO user (id, username, password, firstname, lastname) VALUES (1, 'user', '$2a$04$Vbug2lwwJGrvUXTj6z7ff.97IzVBkrJ1XfApfGNl.Z695zqcnPYra', 'John', 'Doe'); INSERT INTO user (id, username, password, firstname, lastname) VALUES (1, 'user', '{bcrypt}$2a$04$Vbug2lwwJGrvUXTj6z7ff.97IzVBkrJ1XfApfGNl.Z695zqcnPYra', 'John', 'Doe');
INSERT INTO user (id, username, password, firstname, lastname) VALUES (2, 'admin', '$2a$04$Vbug2lwwJGrvUXTj6z7ff.97IzVBkrJ1XfApfGNl.Z695zqcnPYra', 'Admin', 'Admin'); INSERT INTO user (id, username, password, firstname, lastname) VALUES (2, 'admin', '{bcrypt}$2a$04$Vbug2lwwJGrvUXTj6z7ff.97IzVBkrJ1XfApfGNl.Z695zqcnPYra', 'Admin', 'Admin');
INSERT INTO authority (id, name) VALUES (1, 'ROLE_USER'); INSERT INTO authority (id, name) VALUES (1, 'ROLE_USER');
INSERT INTO authority (id, name) VALUES (2, 'ROLE_ADMIN'); INSERT INTO authority (id, name) VALUES (2, 'ROLE_ADMIN');

View File

@@ -4,7 +4,7 @@
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout"> <layout class="ch.qos.logback.classic.PatternLayout">
<Pattern> <Pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%.7thread] %-5level [%-40.40logger{49}:%-3L] --- %m%n %d{yyyy-MM-dd HH:mm:ss.SSS} [%.10thread] %-5level [%-40.40logger{49}:%-3L] --- %m%n
</Pattern> </Pattern>
</layout> </layout>
</appender> </appender>

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -1,57 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "angular-spring-starter"
},
"apps": [
{
"root": "src",
"outDir": "../server/src/main/resources/static",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json"
},
{
"project": "src/tsconfig.spec.json"
},
{
"project": "e2e/tsconfig.e2e.json"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}

View File

@@ -0,0 +1,128 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angular-spring-starter": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "../server/src/main/resources/static",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets",
"src/favicon.ico"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "angular-spring-starter:build"
},
"configurations": {
"production": {
"browserTarget": "angular-spring-starter:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "angular-spring-starter:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles": [
"src/styles.css"
],
"assets": [
"src/assets",
"src/favicon.ico"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": []
}
}
}
},
"angular-spring-starter-e2e": {
"root": "e2e",
"sourceRoot": "e2e",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "angular-spring-starter:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"e2e/tsconfig.e2e.json"
],
"exclude": []
}
}
}
}
},
"defaultProject": "angular-spring-starter",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"styleext": "css"
},
"@schematics/angular:directive": {
"prefix": "app"
}
}
}

View File

@@ -4,33 +4,31 @@
module.exports = function (config) { module.exports = function (config) {
config.set({ config.set({
basePath: '', basePath: '',
frameworks: ['jasmine', '@angular/cli'], frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [ plugins: [
require('karma-jasmine'), require('karma-jasmine'),
require('karma-chrome-launcher'), require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'), require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'), require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma') require('@angular-devkit/build-angular/plugins/karma')
], ],
client:{ client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser clearContext: false // leave Jasmine Spec Runner output visible in browser
}, },
files: [ files: [
{ pattern: './src/test.ts', watched: false }
], ],
preprocessors: { preprocessors: {
'./src/test.ts': ['@angular/cli']
}, },
mime: { mime: {
'text/x-typescript': ['ts','tsx'] 'text/x-typescript': ['ts','tsx']
}, },
coverageIstanbulReporter: { coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ], dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true fixWebpackSourcePaths: true
}, },
angularCli: {
environment: 'dev'
},
reporters: config.angularCli && config.angularCli.codeCoverage reporters: config.angularCli && config.angularCli.codeCoverage
? ['progress', 'coverage-istanbul'] ? ['progress', 'coverage-istanbul']
: ['progress', 'kjhtml'], : ['progress', 'kjhtml'],

View File

@@ -12,34 +12,37 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "5.2.8", "@angular/animations": "7.2.13",
"@angular/cdk": "5.2.4", "@angular/cdk": "7.3.7",
"@angular/common": "5.2.8", "@angular/common": "7.2.13",
"@angular/compiler": "5.2.8", "@angular/compiler": "7.2.13",
"@angular/core": "5.2.8", "@angular/core": "7.2.13",
"@angular/flex-layout": "5.0.0-beta.13", "@angular/flex-layout": "7.0.0-beta.24",
"@angular/forms": "5.2.8", "@angular/forms": "7.2.13",
"@angular/http": "5.2.8", "@angular/http": "7.2.13",
"@angular/material": "5.2.4", "@angular/material": "7.3.7",
"@angular/platform-browser": "5.2.8", "@angular/platform-browser": "7.2.13",
"@angular/platform-browser-dynamic": "5.2.8", "@angular/platform-browser-dynamic": "7.2.13",
"@angular/platform-server": "5.2.8", "@angular/platform-server": "7.2.13",
"@angular/router": "5.2.8", "@angular/router": "7.2.13",
"@fortawesome/fontawesome": "^1.1.4", "@fortawesome/fontawesome": "^1.1.4",
"@fortawesome/fontawesome-free-regular": "^5.0.8", "@fortawesome/fontawesome-free-regular": "^5.0.8",
"@fortawesome/fontawesome-free-solid": "^5.0.8", "@fortawesome/fontawesome-free-solid": "^5.0.8",
"@stomp/ng2-stompjs": "^0.6.3", "@stomp/ng2-stompjs": "^0.6.3",
"core-js": "2.5.1", "core-js": "2.5.1",
"hammerjs": "2.0.8", "hammerjs": "2.0.8",
"rxjs": "5.5.2", "net": "^1.0.2",
"rxjs": "6.4.0",
"stompjs": "^2.3.3", "stompjs": "^2.3.3",
"tslib": "^1.9.0",
"zone.js": "0.8.18" "zone.js": "0.8.18"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.13.0",
"@angular-devkit/core": "^0.2.0", "@angular-devkit/core": "^0.2.0",
"@angular/cli": "1.5.3", "@angular/cli": "7.3.7",
"@angular/compiler-cli": "5.2.8", "@angular/compiler-cli": "7.2.13",
"@angular/language-service": "5.2.8", "@angular/language-service": "7.2.13",
"@types/hammerjs": "2.0.34", "@types/hammerjs": "2.0.34",
"@types/jasmine": "2.5.54", "@types/jasmine": "2.5.54",
"@types/jasminewd2": "2.0.3", "@types/jasminewd2": "2.0.3",
@@ -56,6 +59,6 @@
"protractor": "5.1.2", "protractor": "5.1.2",
"ts-node": "3.0.6", "ts-node": "3.0.6",
"tslint": "5.7.0", "tslint": "5.7.0",
"typescript": "2.4.2" "typescript": "3.2.4"
} }
} }

View File

@@ -2,7 +2,6 @@ import { TestBed, async } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { HomeComponent } from './home';
import { ManagerComponent } from './manager'; import { ManagerComponent } from './manager';
import { LoginComponent } from './login'; import { LoginComponent } from './login';
import { MockApiService } from './service/mocks/api.service.mock'; import { MockApiService } from './service/mocks/api.service.mock';
@@ -10,7 +9,6 @@ import { MockApiService } from './service/mocks/api.service.mock';
import { LoginGuard } from './guard'; import { LoginGuard } from './guard';
import { NotFoundComponent } from './not-found'; import { NotFoundComponent } from './not-found';
import { import {
ApiCardComponent,
FooterComponent, FooterComponent,
GithubComponent, GithubComponent,
} from './component'; } from './component';

View File

@@ -4,12 +4,12 @@
<p [class]="notification.msgType" *ngIf="notification">{{notification.msgBody}}</p> <p [class]="notification.msgType" *ngIf="notification">{{notification.msgBody}}</p>
<mat-card-content> <mat-card-content>
<form *ngIf="!submitted" [formGroup]="form" (ngSubmit)="onSubmit()" #changePasswordForm="ngForm"> <form *ngIf="!submitted" [formGroup]="form" (ngSubmit)="onSubmit()" #changePasswordForm="ngForm">
<mat-input-container> <mat-form-field>
<input matInput formControlName="oldPassword" required type="password" placeholder="old password"> <input matInput formControlName="oldPassword" required type="password" placeholder="old password">
</mat-input-container> </mat-form-field>
<mat-input-container> <mat-form-field>
<input matInput formControlName="newPassword" required type="password" placeholder="new password"> <input matInput formControlName="newPassword" required type="password" placeholder="new password">
</mat-input-container> </mat-form-field>
<button type="submit" [disabled]="!changePasswordForm.form.valid" mat-raised-button color="primary">Change Password</button> <button type="submit" [disabled]="!changePasswordForm.form.valid" mat-raised-button color="primary">Change Password</button>
</form> </form>
<mat-spinner *ngIf="submitted" mode="indeterminate"></mat-spinner> <mat-spinner *ngIf="submitted" mode="indeterminate"></mat-spinner>

View File

@@ -11,7 +11,7 @@ mat-card {
-webkit-animation: fadein 1s; /* Safari and Chrome */ -webkit-animation: fadein 1s; /* Safari and Chrome */
} }
mat-input-container { mat-form-field {
width: 100%; width: 100%;
} }

View File

@@ -3,6 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthService } from 'app/service'; import { AuthService } from 'app/service';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DisplayMessage } from '../shared/models/display-message'; import { DisplayMessage } from '../shared/models/display-message';
import { delay, mergeMap } from 'rxjs/operators';
@Component({ @Component({
selector: 'app-change-password', selector: 'app-change-password',
@@ -50,9 +51,7 @@ export class ChangePasswordComponent implements OnInit {
this.submitted = true; this.submitted = true;
this.authService.changePassowrd(this.form.value) this.authService.changePassowrd(this.form.value)
// show me the animation .pipe(delay(1000), mergeMap(() => this.authService.logout()))
.delay(1000)
.mergeMap(() => this.authService.logout())
.subscribe(() => { .subscribe(() => {
this.router.navigate(['/login', { msgType: 'success', msgBody: 'Success! Please sign in with your new password.'}]); this.router.navigate(['/login', { msgType: 'success', msgBody: 'Success! Please sign in with your new password.'}]);
}, error => { }, error => {

View File

@@ -1,7 +1,7 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { LogsWebsocketService, ApiService } from '../../service'; import { LogsWebsocketService, ApiService } from '../../service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'logs-panel', selector: 'logs-panel',

View File

@@ -1,7 +1,7 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core' import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'
import { ProgressWebsocketService } from '../../service'; import { ProgressWebsocketService } from '../../service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
// import {Message} from '@stomp/stompjs'; // import {Message} from '@stomp/stompjs';
// import { Subscription } from 'rxjs/Subscription'; // import { Subscription } from 'rxjs/Subscription';

View File

@@ -16,7 +16,7 @@ describe('AdminGuard', () => {
{ {
provide: Router, provide: Router,
useClass: RouterStub useClass: RouterStub
} },
{ {
provide: UserService, provide: UserService,
useClass: MockUserService useClass: MockUserService

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { UserService } from '../service'; import { UserService } from '../service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class AdminGuard implements CanActivate { export class AdminGuard implements CanActivate {

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router'; import { Router, CanActivate } from '@angular/router';
import { UserService } from '../service'; import { UserService } from '../service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class GuestGuard implements CanActivate { export class GuestGuard implements CanActivate {

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router'; import { Router, CanActivate } from '@angular/router';
import { UserService } from '../service'; import { UserService } from '../service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class LoginGuard implements CanActivate { export class LoginGuard implements CanActivate {

View File

@@ -15,12 +15,12 @@
<p [class]="notification.msgType" *ngIf="notification">{{notification.msgBody}}</p> <p [class]="notification.msgType" *ngIf="notification">{{notification.msgBody}}</p>
<form *ngIf="!submitted" [formGroup]="form" (ngSubmit)="onSubmit()" #loginForm="ngForm"> <form *ngIf="!submitted" [formGroup]="form" (ngSubmit)="onSubmit()" #loginForm="ngForm">
<mat-input-container> <mat-form-field>
<input matInput formControlName="username" required placeholder="user"> <input matInput formControlName="username" required placeholder="user">
</mat-input-container> </mat-form-field>
<mat-input-container> <mat-form-field>
<input matInput formControlName="password" required type="password" placeholder="password"> <input matInput formControlName="password" required type="password" placeholder="password">
</mat-input-container> </mat-form-field>
<button type="submit" [disabled]="!loginForm.form.valid" mat-raised-button color="primary">Login</button> <button type="submit" [disabled]="!loginForm.form.valid" mat-raised-button color="primary">Login</button>
</form> </form>
<!-- <br> <!-- <br>

View File

@@ -12,7 +12,7 @@ mat-card {
} }
mat-input-container { mat-form-field {
display: block; display: block;
} }

View File

@@ -3,14 +3,16 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { DisplayMessage } from '../shared/models/display-message'; import { DisplayMessage } from '../shared/models/display-message';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs';
import { takeUntil, delay } from 'rxjs/operators'
import { import {
UserService, UserService,
AuthService AuthService
} from '../service'; } from '../service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Subject } from 'rxjs/SUbject'; import { Subject } from 'rxjs';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
@@ -22,17 +24,8 @@ export class LoginComponent implements OnInit, OnDestroy {
githubLink = 'https://github.com/fabioformosa/quartz-manager'; githubLink = 'https://github.com/fabioformosa/quartz-manager';
form: FormGroup; form: FormGroup;
/**
* Boolean used in telling the UI
* that the form has been submitted
* and is awaiting a response
*/
submitted = false; submitted = false;
/**
* Notification message from received
* form request or router
*/
notification: DisplayMessage; notification: DisplayMessage;
returnUrl: string; returnUrl: string;
@@ -50,11 +43,10 @@ export class LoginComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.route.params this.route.params
.takeUntil(this.ngUnsubscribe) .pipe(takeUntil(this.ngUnsubscribe))
.subscribe((params: DisplayMessage) => { .subscribe((params: DisplayMessage) => {
this.notification = params; this.notification = params;
}); });
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
this.form = this.formBuilder.group({ this.form = this.formBuilder.group({
username: ['', Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(64)])], username: ['', Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(64)])],
@@ -84,15 +76,11 @@ export class LoginComponent implements OnInit, OnDestroy {
} }
onSubmit() { onSubmit() {
/**
* Innocent until proven guilty
*/
this.notification = undefined; this.notification = undefined;
this.submitted = true; this.submitted = true;
this.authService.login(this.form.value) this.authService.login(this.form.value)
// show me the animation .pipe(delay(1000))
.delay(1000)
.subscribe(data => { .subscribe(data => {
this.userService.getMyInfo().subscribe(); this.userService.getMyInfo().subscribe();
this.router.navigate([this.returnUrl]); this.router.navigate([this.returnUrl]);

View File

@@ -1,8 +1,7 @@
import { HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import 'rxjs/Rx'; import { catchError, map, filter } from 'rxjs/operators'
import 'rxjs/add/observable/throw';
import { serialize } from 'app/shared/utilities/serialize'; import { serialize } from 'app/shared/utilities/serialize';
export enum RequestMethod { export enum RequestMethod {
@@ -36,7 +35,7 @@ export class ApiService {
} }
return this.http.get(path, options) return this.http.get(path, options)
.catch(this.checkError.bind(this)); .pipe(catchError(this.checkError.bind(this)));
} }
post(path: string, body: any, customHeaders?: HttpHeaders): Observable<any> { post(path: string, body: any, customHeaders?: HttpHeaders): Observable<any> {
@@ -58,9 +57,11 @@ export class ApiService {
}); });
return this.http.request(req) return this.http.request(req)
.filter(response => response instanceof HttpResponse) .pipe(
.map((response: HttpResponse<any>) => response.body) filter(response => response instanceof HttpResponse),
.catch(error => this.checkError(error)); map((response: HttpResponse<any>) => response.body),
catchError(error => this.checkError(error))
)
} }
// Display error if logged in, otherwise redirect to IDP // Display error if logged in, otherwise redirect to IDP

View File

@@ -3,7 +3,8 @@ import { HttpHeaders } from '@angular/common/http';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
import { UserService } from './user.service'; import { UserService } from './user.service';
import { ConfigService } from './config.service'; import { ConfigService } from './config.service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable() @Injectable()
export class AuthService { export class AuthService {
@@ -20,10 +21,10 @@ export class AuthService {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
}); });
const body = `username=${user.username}&password=${user.password}`; const body = `username=${user.username}&password=${user.password}`;
return this.apiService.post(this.config.login_url, body, loginHeaders).map(() => { return this.apiService.post(this.config.login_url, body, loginHeaders).pipe(map(() => {
console.log("Login success"); console.log("Login success");
this.userService.getMyInfo().subscribe(); this.userService.getMyInfo().subscribe();
}); }));
} }
signup(user){ signup(user){
@@ -31,16 +32,16 @@ export class AuthService {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}); });
return this.apiService.post(this.config.signup_url, JSON.stringify(user), signupHeaders).map(() =>{ return this.apiService.post(this.config.signup_url, JSON.stringify(user), signupHeaders).pipe(map(() =>{
console.log("Sign up success"); console.log("Sign up success");
}); }));
} }
logout() { logout() {
return this.apiService.post(this.config.logout_url, {}) return this.apiService.post(this.config.logout_url, {})
.map(() => { .pipe(map(() => {
this.userService.currentUser = null; this.userService.currentUser = null;
}); }));
} }
changePassowrd(passwordChanger) { changePassowrd(passwordChanger) {

View File

@@ -3,6 +3,8 @@ import { Headers } from '@angular/http';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
import { ConfigService } from './config.service'; import { ConfigService } from './config.service';
import { map } from 'rxjs/operators'
@Injectable() @Injectable()
export class UserService { export class UserService {
@@ -41,7 +43,7 @@ export class UserService {
} }
getMyInfo() { getMyInfo() {
return this.apiService.get(this.config.whoami_url).map(user => this.currentUser = user); return this.apiService.get(this.config.whoami_url).pipe(map(user => this.currentUser = user));
} }
getAll() { getAll() {

View File

@@ -1,7 +1,7 @@
import { Injectable, OnInit } from '@angular/core'; import { Injectable, OnInit } from '@angular/core';
import { Headers } from '@angular/http'; import { Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { ApiService } from './api.service'; import { ApiService } from './api.service';

View File

@@ -15,22 +15,22 @@
<p [class]="notification.msgType" *ngIf="notification">{{notification.msgBody}}</p> <p [class]="notification.msgType" *ngIf="notification">{{notification.msgBody}}</p>
<form *ngIf="!submitted" [formGroup]="form" (ngSubmit)="onSubmit()" #signupForm="ngForm"> <form *ngIf="!submitted" [formGroup]="form" (ngSubmit)="onSubmit()" #signupForm="ngForm">
<mat-input-container> <mat-form-field>
<label>Username: </label> <label>Username: </label>
<input matInput formControlName="username" required> <input matInput formControlName="username" required>
</mat-input-container> </mat-form-field>
<mat-input-container> <mat-form-field>
<label>Password: </label> <label>Password: </label>
<input matInput formControlName="password" required type="password"> <input matInput formControlName="password" required type="password">
</mat-input-container> </mat-form-field>
<mat-input-container> <mat-form-field>
<label>First Name: </label> <label>First Name: </label>
<input matInput formControlName="firstname"> <input matInput formControlName="firstname">
</mat-input-container> </mat-form-field>
<mat-input-container> <mat-form-field>
<label>Last Name: </label> <label>Last Name: </label>
<input matInput formControlName="lastname"> <input matInput formControlName="lastname">
</mat-input-container> </mat-form-field>
<button type="submit" [disabled]="!signupForm.form.valid" mat-raised-button color="primary">Sign up</button> <button type="submit" [disabled]="!signupForm.form.valid" mat-raised-button color="primary">Sign up</button>
</form> </form>
<br> <br>

View File

@@ -12,7 +12,7 @@
} }
mat-input-container { mat-form-field {
display: block; display: block;
} }

View File

@@ -1,6 +1,21 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
MatCardModule, MatInputModule, MatProgressSpinnerModule, MatProgressBarModule
} from '@angular/material';
import { SignupComponent } from './signup.component'; import { SignupComponent } from './signup.component';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import {
MockUserService,
MockApiService
} from 'app/service/mocks';
import {
UserService,
AuthService,
ApiService,
ConfigService
} from 'app/service';
describe('SignupComponent', () => { describe('SignupComponent', () => {
let component: SignupComponent; let component: SignupComponent;
@@ -8,7 +23,25 @@ describe('SignupComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ SignupComponent ] declarations: [ SignupComponent ],
imports : [RouterTestingModule,
BrowserAnimationsModule,
MatCardModule,
MatInputModule,
MatProgressSpinnerModule,
MatProgressBarModule,
ReactiveFormsModule],
providers: [
{
provide: UserService,
useClass: MockUserService
},
{
provide: ApiService,
useClass: MockApiService
},
AuthService,
ConfigService]
}) })
.compileComponents(); .compileComponents();
})); }));

View File

@@ -3,14 +3,15 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { DisplayMessage } from '../shared/models/display-message'; import { DisplayMessage } from '../shared/models/display-message';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs';
import { takeUntil, delay } from 'rxjs/operators'
import { import {
UserService, UserService,
AuthService AuthService
} from '../service'; } from '../service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Subject } from 'rxjs/SUbject'; import { Subject } from 'rxjs';
@Component({ @Component({
selector: 'app-signup', selector: 'app-signup',
@@ -49,8 +50,9 @@ export class SignupComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.route.params this.route.params.pipe(
.takeUntil(this.ngUnsubscribe) takeUntil(this.ngUnsubscribe)
)
.subscribe((params: DisplayMessage) => { .subscribe((params: DisplayMessage) => {
this.notification = params; this.notification = params;
}); });
@@ -82,7 +84,7 @@ export class SignupComponent implements OnInit, OnDestroy {
this.authService.signup(this.form.value) this.authService.signup(this.form.value)
// show me the animation // show me the animation
.delay(1000) .pipe(delay(1000))
.subscribe(data => { .subscribe(data => {
console.log(data); console.log(data);
this.authService.login(this.form.value).subscribe(data =>{ this.authService.login(this.form.value).subscribe(data =>{

View File

@@ -42,7 +42,6 @@
/** Evergreen browsers require these. **/ /** Evergreen browsers require these. **/
import 'core-js/es6/reflect'; import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
@@ -56,6 +55,8 @@ import 'core-js/es7/reflect';
*/ */
import 'zone.js/dist/zone'; // Included with Angular CLI. import 'zone.js/dist/zone'; // Included with Angular CLI.
(window as any).global = window
/*************************************************************************************************** /***************************************************************************************************

View File

@@ -11,7 +11,8 @@
] ]
}, },
"files": [ "files": [
"test.ts" "test.ts",
"polyfills.ts"
], ],
"include": [ "include": [
"**/*.spec.ts", "**/*.spec.ts",

View File

@@ -1,6 +1,7 @@
{ {
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"importHelpers": true,
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"baseUrl": "src", "baseUrl": "src",
"sourceMap": true, "sourceMap": true,
@@ -15,6 +16,7 @@
"lib": [ "lib": [
"es2016", "es2016",
"dom" "dom"
] ],
"module": "es2015"
} }
} }

View File

@@ -12,7 +12,7 @@
"curly": true, "curly": true,
"eofline": true, "eofline": true,
"forin": true, "forin": true,
"import-blacklist": [true, "rxjs"], "import-blacklist": [true],
"import-spacing": true, "import-spacing": true,
"indent": [ "indent": [
true, true,