From ba158bb5597790f46259ef2e36f3d2ce75ba12b8 Mon Sep 17 00:00:00 2001 From: Bolo Date: Mon, 16 May 2022 10:34:59 +0200 Subject: [PATCH] Angular oauth2 / use ProjectFile --- .../domain/AngularOauth2DomainService.java | 15 ++++- ...mustache => http-auth.interceptor.spec.ts} | 12 ++-- ...r.ts.mustache => http-auth.interceptor.ts} | 10 +-- ...s.mustache => oauth2-auth.service.spec.ts} | 27 ++++---- .../webapp/app/auth/oauth2-auth.service.ts | 65 +++++++++++++++++++ .../app/auth/oauth2-auth.service.ts.mustache | 62 ------------------ ...ent.html.mustache => login.component.html} | 0 ...ec.ts.mustache => login.component.spec.ts} | 11 ++-- ...mponent.ts.mustache => login.component.ts} | 5 +- .../AngularOauth2DomainServiceTest.java | 46 ++++++------- 10 files changed, 127 insertions(+), 126 deletions(-) rename src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/{http-auth.interceptor.spec.ts.mustache => http-auth.interceptor.spec.ts} (89%) rename src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/{http-auth.interceptor.ts.mustache => http-auth.interceptor.ts} (77%) rename src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/{oauth2-auth.service.spec.ts.mustache => oauth2-auth.service.spec.ts} (88%) create mode 100644 src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts delete mode 100644 src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts.mustache rename src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/{login.component.html.mustache => login.component.html} (100%) rename src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/{login.component.spec.ts.mustache => login.component.spec.ts} (83%) rename src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/{login.component.ts.mustache => login.component.ts} (65%) diff --git a/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainService.java b/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainService.java index bbd1b037011..dc70c6902e9 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainService.java +++ b/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainService.java @@ -9,10 +9,12 @@ import static tech.jhipster.lite.generator.project.domain.Constants.MAIN_WEBAPP; import static tech.jhipster.lite.generator.project.domain.DefaultConfig.BASE_NAME; +import java.util.List; import tech.jhipster.lite.error.domain.GeneratorException; import tech.jhipster.lite.generator.client.angular.common.domain.AngularCommonService; import tech.jhipster.lite.generator.packagemanager.npm.domain.NpmService; import tech.jhipster.lite.generator.project.domain.Project; +import tech.jhipster.lite.generator.project.domain.ProjectFile; import tech.jhipster.lite.generator.project.domain.ProjectRepository; public class AngularOauth2DomainService implements AngularOauth2Service { @@ -54,9 +56,18 @@ private void addDependencies(Project project) { private void addFiles(Project project) { project.addDefaultConfig(BASE_NAME); - AngularOauth2 + List projectFiles = AngularOauth2 .getFilesToAdd() - .forEach((fileName, path) -> projectRepository.template(project, getPath(SOURCE_WEBAPP, path), fileName, getPath(MAIN_WEBAPP, path))); + .entrySet() + .stream() + .map(e -> + ProjectFile + .forProject(project) + .withSource(getPath(SOURCE_WEBAPP, e.getValue()), e.getKey()) + .withDestinationFolder(getPath(MAIN_WEBAPP, e.getValue())) + ) + .toList(); + projectRepository.add(projectFiles); } private void updateExistingFiles(Project project) { diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts similarity index 89% rename from src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts.mustache rename to src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts index 57e228af2c4..ad304c0cd0d 100644 --- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts.mustache +++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts @@ -1,9 +1,9 @@ -import {TestBed} from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; -import {HttpAuthInterceptor} from './http-auth.interceptor'; -import {Oauth2AuthService} from './oauth2-auth.service'; -import {HttpHandler, HttpRequest} from '@angular/common/http'; -import {lastValueFrom, of} from 'rxjs'; +import { HttpAuthInterceptor } from './http-auth.interceptor'; +import { Oauth2AuthService } from './oauth2-auth.service'; +import { HttpHandler, HttpRequest } from '@angular/common/http'; +import { lastValueFrom, of } from 'rxjs'; import Mock = jest.Mock; const URL = 'http://localhost:8080/api/dummy'; @@ -15,7 +15,7 @@ const buildHttpRequest = () => { return originalRequest.clone({ headers: originalRequest.headers.append('ContentType', 'application/json'), }); -} +}; describe('HttpAuthInterceptor', () => { let interceptor: HttpAuthInterceptor; diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts similarity index 77% rename from src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts.mustache rename to src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts index 503a8f18e58..be028f07753 100644 --- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts.mustache +++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts @@ -1,16 +1,10 @@ import { Injectable } from '@angular/core'; -import { - HttpRequest, - HttpHandler, - HttpEvent, - HttpInterceptor -} from '@angular/common/http'; +import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { Observable } from 'rxjs'; -import {Oauth2AuthService} from './oauth2-auth.service'; +import { Oauth2AuthService } from './oauth2-auth.service'; @Injectable() export class HttpAuthInterceptor implements HttpInterceptor { - constructor(private oauth2AuthService: Oauth2AuthService) {} intercept(request: HttpRequest, next: HttpHandler): Observable> { diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.spec.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.spec.ts similarity index 88% rename from src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.spec.ts.mustache rename to src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.spec.ts index 2376a9b075a..a92879a8fa0 100644 --- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.spec.ts.mustache +++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.spec.ts @@ -1,7 +1,7 @@ -import {Oauth2AuthService} from './oauth2-auth.service'; -import {TestBed} from '@angular/core/testing'; -import Keycloak, {KeycloakError, KeycloakInitOptions, KeycloakInstance, KeycloakPromise} from 'keycloak-js'; -import {lastValueFrom} from 'rxjs'; +import { Oauth2AuthService } from './oauth2-auth.service'; +import { TestBed } from '@angular/core/testing'; +import Keycloak, { KeycloakError, KeycloakInitOptions, KeycloakInstance, KeycloakPromise } from 'keycloak-js'; +import { lastValueFrom } from 'rxjs'; import SpyInstance = jest.SpyInstance; jest.mock('keycloak-js', () => ({ @@ -25,8 +25,8 @@ jest.mock('../../environments/environment', () => ({ keycloak: { url: 'http://localhost:1234', realm: 'jhipster', - client_id: 'web_app' - } + client_id: 'web_app', + }, }, })); @@ -71,7 +71,8 @@ describe('Oauth2 Auth Service', () => { it('should init refresh token and return true when the user is authenticated', async () => { // Given - jest.spyOn(keycloakInstance, 'init') + jest + .spyOn(keycloakInstance, 'init') .mockReturnValue(Promise.resolve(true).then() as unknown as KeycloakPromise); // When @@ -92,7 +93,8 @@ describe('Oauth2 Auth Service', () => { it('should reload window and return false when the user is not authenticated', async () => { // Given - jest.spyOn(keycloakInstance, 'init') + jest + .spyOn(keycloakInstance, 'init') .mockReturnValue(Promise.resolve(false).then() as unknown as KeycloakPromise); // When @@ -126,8 +128,7 @@ describe('Oauth2 Auth Service', () => { jest.spyOn(Date, 'now').mockReturnValue(1651318847714); // 2022-04-30 13:40:47 let updateTokenPromise = Promise.resolve(false); - jest.spyOn(keycloakInstance, 'updateToken') - .mockReturnValue(updateTokenPromise as unknown as KeycloakPromise); + jest.spyOn(keycloakInstance, 'updateToken').mockReturnValue(updateTokenPromise as unknown as KeycloakPromise); // When await lastValueFrom(service.initAuthentication()); @@ -143,8 +144,7 @@ describe('Oauth2 Auth Service', () => { it('should call update token and log debug "refreshed" message when token is refreshed', async () => { // Given let updateTokenPromise = Promise.resolve(true); - jest.spyOn(keycloakInstance, 'updateToken') - .mockReturnValue(updateTokenPromise as unknown as KeycloakPromise); + jest.spyOn(keycloakInstance, 'updateToken').mockReturnValue(updateTokenPromise as unknown as KeycloakPromise); // When await lastValueFrom(service.initAuthentication()); @@ -160,8 +160,7 @@ describe('Oauth2 Auth Service', () => { it('should call update token and log error message on error', async () => { // Given let updateTokenPromise = Promise.reject(new Error('unknown error')); - jest.spyOn(keycloakInstance, 'updateToken') - .mockReturnValue(updateTokenPromise as unknown as KeycloakPromise); + jest.spyOn(keycloakInstance, 'updateToken').mockReturnValue(updateTokenPromise as unknown as KeycloakPromise); // When await lastValueFrom(service.initAuthentication()); diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts new file mode 100644 index 00000000000..8b9a1187299 --- /dev/null +++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts @@ -0,0 +1,65 @@ +import { Injectable } from '@angular/core'; +import Keycloak, { KeycloakConfig, KeycloakInstance } from 'keycloak-js'; +import { from, Observable, tap } from 'rxjs'; +import { environment } from '../../environments/environment'; + +const MIN_TOKEN_VALIDITY_SECONDS = 70; +const REFRESH_TOKEN_TIMEOUT_MS = 6000; + +@Injectable({ providedIn: 'root' }) +export class Oauth2AuthService { + private keycloak!: KeycloakInstance; + + get token(): string | undefined { + return this.keycloak.token; + } + + get isAuthenticated(): boolean | undefined { + return this.keycloak.authenticated; + } + + initAuthentication(): Observable { + const config: KeycloakConfig = { + url: environment.keycloak.url, + realm: environment.keycloak.realm, + clientId: environment.keycloak.client_id, + }; + this.keycloak = Keycloak(config); + + return from(this.keycloak.init({ onLoad: 'login-required', checkLoginIframe: false })).pipe( + tap(authenticated => { + if (!authenticated) { + window.location.reload(); + } else { + console.debug('Authenticated'); + } + }), + tap(() => { + this.initUpdateTokenRefresh(); + }) + ); + } + + logout(): void { + this.keycloak.logout(); + } + + private initUpdateTokenRefresh(): void { + setInterval( + () => + this.keycloak + .updateToken(MIN_TOKEN_VALIDITY_SECONDS) + .then(refreshed => { + if (refreshed) { + console.debug('Token refreshed'); + } else { + const exp = this.keycloak.tokenParsed!.exp!; + const timeSkew = this.keycloak.timeSkew!; + console.debug('Token not refreshed, valid for ' + Math.round(exp + timeSkew - Date.now() / 1000) + ' seconds'); + } + }) + .catch(e => console.error('Failed to refresh token: ' + e)), + REFRESH_TOKEN_TIMEOUT_MS + ); + } +} diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts.mustache deleted file mode 100644 index 4f4a3e5a6a6..00000000000 --- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/oauth2-auth.service.ts.mustache +++ /dev/null @@ -1,62 +0,0 @@ -import {Injectable} from '@angular/core'; -import Keycloak, {KeycloakConfig, KeycloakInstance} from 'keycloak-js'; -import {from, Observable, tap} from 'rxjs'; -import {environment} from '../../environments/environment'; - -const MIN_TOKEN_VALIDITY_SECONDS = 70; -const REFRESH_TOKEN_TIMEOUT_MS = 6000; - -@Injectable({ providedIn: 'root' }) -export class Oauth2AuthService { - private keycloak!: KeycloakInstance; - - get token(): string | undefined { - return this.keycloak.token; - } - - get isAuthenticated(): boolean | undefined { - return this.keycloak.authenticated; - } - - initAuthentication(): Observable { - const config: KeycloakConfig = { - url: environment.keycloak.url, - realm: environment.keycloak.realm, - clientId: environment.keycloak.client_id, - }; - this.keycloak = Keycloak(config); - - return from(this.keycloak.init({ onLoad: 'login-required', checkLoginIframe: false })) - .pipe( - tap(authenticated => { - if (!authenticated) { - window.location.reload(); - } else { - console.debug('Authenticated'); - } - }), - tap(() => { - this.initUpdateTokenRefresh(); - }), - ); - } - - logout(): void { - this.keycloak.logout(); - } - - private initUpdateTokenRefresh(): void { - setInterval(() => - this.keycloak.updateToken(MIN_TOKEN_VALIDITY_SECONDS).then(refreshed => { - if (refreshed) { - console.debug('Token refreshed'); - } else { - const exp = this.keycloak.tokenParsed!.exp!; - const timeSkew = this.keycloak.timeSkew!; - console.debug('Token not refreshed, valid for ' - + Math.round(exp + timeSkew - Date.now() / 1000) + ' seconds'); - } - }).catch(e => console.error('Failed to refresh token: ' + e)) - , REFRESH_TOKEN_TIMEOUT_MS); - } -} diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.html.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.html similarity index 100% rename from src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.html.mustache rename to src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.html diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.spec.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.spec.ts similarity index 83% rename from src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.spec.ts.mustache rename to src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.spec.ts index 4e7fc44a137..9c048af3d93 100644 --- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.spec.ts.mustache +++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.spec.ts @@ -1,8 +1,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import {By} from '@angular/platform-browser'; +import { By } from '@angular/platform-browser'; import { LoginComponent } from './login.component'; -import {Oauth2AuthService} from '../auth/oauth2-auth.service'; +import { Oauth2AuthService } from '../auth/oauth2-auth.service'; describe('LoginComponent', () => { let component: LoginComponent; @@ -12,9 +12,8 @@ describe('LoginComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ LoginComponent ] - }) - .compileComponents(); + declarations: [LoginComponent], + }).compileComponents(); }); beforeEach(() => { @@ -36,5 +35,5 @@ describe('LoginComponent', () => { fixture.debugElement.query(By.css('#btn-logout')).nativeElement.click(); expect(oauth2AuthService.logout).toHaveBeenCalledWith(); - }) + }); }); diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.ts similarity index 65% rename from src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.ts.mustache rename to src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.ts index b0cef4d7132..7ba17745e03 100644 --- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.ts.mustache +++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/login/login.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import {Oauth2AuthService} from '../auth/oauth2-auth.service'; +import { Oauth2AuthService } from '../auth/oauth2-auth.service'; @Component({ selector: 'jh-lite-login', @@ -7,8 +7,7 @@ import {Oauth2AuthService} from '../auth/oauth2-auth.service'; styleUrls: [], }) export class LoginComponent { - - constructor(private oauth2AuthService: Oauth2AuthService) { } + constructor(private oauth2AuthService: Oauth2AuthService) {} logout(): void { this.oauth2AuthService.logout(); diff --git a/src/test/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainServiceTest.java b/src/test/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainServiceTest.java index 47b2fed6de2..29afd8a9f3f 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainServiceTest.java +++ b/src/test/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2DomainServiceTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.groups.Tuple.tuple; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.eq; @@ -14,10 +15,12 @@ import static tech.jhipster.lite.generator.client.angular.security.oauth2.domain.AngularOauth2.ENVIRONMENT_PROD_TS_FILE_PATH; import static tech.jhipster.lite.generator.client.angular.security.oauth2.domain.AngularOauth2.ENVIRONMENT_TS_FILE_PATH; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -26,6 +29,7 @@ import tech.jhipster.lite.generator.client.angular.common.domain.AngularCommonService; import tech.jhipster.lite.generator.packagemanager.npm.domain.NpmService; import tech.jhipster.lite.generator.project.domain.Project; +import tech.jhipster.lite.generator.project.domain.ProjectFile; import tech.jhipster.lite.generator.project.domain.ProjectRepository; @UnitTest @@ -44,6 +48,9 @@ class AngularOauth2DomainServiceTest { @InjectMocks AngularOauth2DomainService angularOauth2DomainService; + @Captor + ArgumentCaptor> projectFilesArgCaptor; + @Test void shouldAddOauth2() { // Given @@ -60,33 +67,22 @@ void shouldAddOauth2() { } private void verifyAddedFiles(Project project) { - verify(projectRepository, times(7)).template(any(Project.class), anyString(), anyString(), anyString()); - ArgumentCaptor fileNameArgCaptor = ArgumentCaptor.forClass(String.class); - verify(projectRepository, times(4)) - .template( - eq(project), - eq("client/angular/security/oauth2/src/main/webapp/app/auth"), - fileNameArgCaptor.capture(), - eq("src/main/webapp/app/auth") - ); - assertThat(fileNameArgCaptor.getAllValues()) + verify(projectRepository).add(projectFilesArgCaptor.capture()); + String authSourcePath = "client/angular/security/oauth2/src/main/webapp/app/auth"; + String authDestinationPath = "src/main/webapp/app/auth"; + String loginSourcePath = "client/angular/security/oauth2/src/main/webapp/app/login"; + String loginDestinationPath = "src/main/webapp/app/login"; + assertThat(projectFilesArgCaptor.getValue()) + .extracting(ProjectFile::project, p -> p.source().folder(), p -> p.source().file(), p -> p.destination().folder()) .containsExactlyInAnyOrder( - "oauth2-auth.service.ts", - "oauth2-auth.service.spec.ts", - "http-auth.interceptor.ts", - "http-auth.interceptor.spec.ts" - ); - - fileNameArgCaptor = ArgumentCaptor.forClass(String.class); - verify(projectRepository, times(3)) - .template( - eq(project), - eq("client/angular/security/oauth2/src/main/webapp/app/login"), - fileNameArgCaptor.capture(), - eq("src/main/webapp/app/login") + tuple(project, authSourcePath, "oauth2-auth.service.ts", authDestinationPath), + tuple(project, authSourcePath, "oauth2-auth.service.spec.ts", authDestinationPath), + tuple(project, authSourcePath, "http-auth.interceptor.ts", authDestinationPath), + tuple(project, authSourcePath, "http-auth.interceptor.spec.ts", authDestinationPath), + tuple(project, loginSourcePath, "login.component.html", loginDestinationPath), + tuple(project, loginSourcePath, "login.component.ts", loginDestinationPath), + tuple(project, loginSourcePath, "login.component.spec.ts", loginDestinationPath) ); - assertThat(fileNameArgCaptor.getAllValues()) - .containsExactlyInAnyOrder("login.component.html", "login.component.ts", "login.component.spec.ts"); } @Test