Skip to content

Commit

Permalink
NXP-32714: add signout butn & update utest
Browse files Browse the repository at this point in the history
  • Loading branch information
rakeshkumar1019 committed Jul 4, 2024
1 parent 9e04b91 commit 36368e4
Show file tree
Hide file tree
Showing 19 changed files with 255 additions and 114 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/utest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ jobs:
- name: Unit tests
working-directory: nuxeo-admin-console-web/angular-app
run: |
xvfb-run --auto-servernum --server-args="-screen 0 1024x768x24" npm test
xvfb-run --auto-servernum --server-args="-screen 0 1024x768x24" npm test
12 changes: 0 additions & 12 deletions nuxeo-admin-console-package/package.json

This file was deleted.

4 changes: 3 additions & 1 deletion nuxeo-admin-console-web/angular-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"watch": "ng build --watch --configuration development",
"test": "ng test --code-coverage --watch=false",
"lint": "ng lint",
"clean":"npm cache clean --force",
"clean": "npm cache clean --force",
"prepare": "husky"
},
"dependencies": {
Expand All @@ -28,6 +28,8 @@
"@ngrx/router-store": "^16.0.0",
"@ngrx/store": "^16.0.0",
"@ngrx/store-devtools": "^16.0.0",
"angular": "^1.8.3",
"angular-material": "^1.2.5",
"angular-oauth2-oidc": "^15.0.1",
"hammerjs": "^2.0.8",
"rxjs": "~7.8.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { PersistenceService } from "./shared/services/persistence.service";
import { WarningComponent } from "./features/warning/warning.component";
import { CommonService } from "./shared/services/common.service";
import { EventEmitter } from "@angular/core";
import { provideMockStore } from '@ngrx/store/testing';
import { StoreModule } from '@ngrx/store';

describe("AppComponent", () => {
let component: AppComponent;
Expand All @@ -32,7 +34,7 @@ describe("AppComponent", () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AppComponent, BaseLayoutComponent],
imports: [CommonModule, MatDialogModule],
imports: [CommonModule, MatDialogModule, StoreModule.forRoot(provideMockStore)],
providers: [
{ provide: ComponentFixtureAutoDetect, useValue: true },
{ provide: PersistenceService, useClass: persistenceServiceStub },
Expand All @@ -53,6 +55,7 @@ describe("AppComponent", () => {
spyOn(component.dialogService, "open");
});
it("should open the warning dialog if warning preference is not set", () => {
if (component?.currentUser) {
spyOn(component.persistenceService, "get").and.returnValue(null);
const loadAppSubscriptionSpy = spyOn(
component.commonService.loadApp,
Expand All @@ -67,29 +70,36 @@ describe("AppComponent", () => {
}
);
expect(loadAppSubscriptionSpy).toHaveBeenCalled();
}
});

it("should not open the warning dialog if warning preference is set", () => {
if (component?.currentUser) {
spyOn(component.persistenceService, "get").and.returnValue("true");
component.ngOnInit();
expect(component.persistenceService.get).toHaveBeenCalled();
expect(component.dialogService.open).not.toHaveBeenCalled();
expect(component.loadApp).toEqual(true);
}
});
});

it("should set loadApp to true or false based on the value received from the service subscription", () => {
if (component?.currentUser) {
component.ngOnInit();
component.commonService.loadApp.emit(true);
expect(component.loadApp).toBe(true);
component.ngOnInit();
component.commonService.loadApp.emit(false);
expect(component.loadApp).toBe(false);
}
});

it("should remove theloadAppSubscription when component is destroyed", () => {
if (component?.currentUser) {
spyOn(component.loadAppSubscription, "unsubscribe");
component.ngOnDestroy();
expect(component.loadAppSubscription.unsubscribe).toHaveBeenCalled();
}
});
});
49 changes: 33 additions & 16 deletions nuxeo-admin-console-web/angular-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import { NuxeoJSClientService } from './shared/services/nuxeo-js-client.service'
import { Component, OnDestroy, OnInit } from "@angular/core";
import { MatDialog } from "@angular/material/dialog";
import { PersistenceService } from "./shared/services/persistence.service";
import { Subscription } from "rxjs";
import { Subscription, Observable } from "rxjs";
import { CommonService } from "./shared/services/common.service";
import { WarningComponent } from "./features/warning/warning.component";
import { Store, select } from "@ngrx/store";
import { authActions } from "./auth/store/actions";
import { AuthStateInterface } from "./auth/types/authState.interface";
import { UserInterface } from './shared/types/user.interface';

@Component({
selector: "app",
Expand All @@ -14,30 +18,43 @@ import { WarningComponent } from "./features/warning/warning.component";
export class AppComponent implements OnInit, OnDestroy {
loadApp = false;
loadAppSubscription = new Subscription();
currentUser$: Observable<UserInterface | null | undefined>;
currentUserSubscription: Subscription = new Subscription();
currentUser: UserInterface | null | undefined = undefined;
constructor(
public dialogService: MatDialog,
public persistenceService: PersistenceService,
public commonService: CommonService,
private nuxeoJsClientService: NuxeoJSClientService
) {}
private nuxeoJsClientService: NuxeoJSClientService,
private store: Store<{ auth: AuthStateInterface }>
) {
this.currentUser$ = this.store.pipe(select((state: { auth: AuthStateInterface }) => state.auth.currentUser));
}

ngOnInit(): void {
this.nuxeoJsClientService.initiateJSClient();
const doNotWarn = !!this.persistenceService.get("doNotWarn");
if (!doNotWarn) {
this.dialogService.open(WarningComponent, {
disableClose: true,
});
this.loadAppSubscription = this.commonService.loadApp.subscribe(
(load) => {
this.loadApp = load;
this.currentUserSubscription = this.currentUser$.subscribe(user => {
this.currentUser = user;
if (this.currentUser) {
const preferenceKey = `doNotWarn-${this.currentUser.id}`;
const doNotWarn = !!this.persistenceService.get(preferenceKey);
if (!doNotWarn) {
this.dialogService.open(WarningComponent, {
disableClose: true,
});
this.loadAppSubscription = this.commonService.loadApp.subscribe(load => {
this.loadApp = load;
});
} else {
this.loadApp = true;
}
);
} else {
this.loadApp = true;
}
}
});

this.store.dispatch(authActions.getCurrentUser());
}
ngOnDestroy(): void {
this.loadAppSubscription.unsubscribe();
this.currentUserSubscription.unsubscribe();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,48 @@ import { HttpClient } from "@angular/common/http";
import { map, Observable } from "rxjs";
import { environment } from "../../../environments/environment";
import { UserInterface } from "../../shared/types/user.interface";
import { AuthResponseInterface } from "../types/authResponse.interface";
import { AuthUserResponseInterface } from "../types/authResponse.interface";
import { HylandSSORequestInterface } from "../types/hylandSSORequest.interface";
import { NuxeoJSClientService } from "../../shared/services/nuxeo-js-client.service";

@Injectable({
providedIn: "root",
})
export class AuthService {
constructor(private http: HttpClient) {}
constructor(private http: HttpClient, private nuxeoJsClientService: NuxeoJSClientService) { }

getUser(response: AuthResponseInterface): UserInterface {
return response.user;
}

getCurrentUser(): Observable<UserInterface> {
const url = environment.apiUrl + "/user.json";
return this.http.get<AuthResponseInterface>(url).pipe(map(this.getUser));
const url = `${this.nuxeoJsClientService.getApiUrl()}me`;
return this.http.get<AuthUserResponseInterface>(url).pipe(
map(response => this.getUser(response))
);
}

getUser(response: AuthUserResponseInterface): UserInterface {
return {
id: response.id,
properties: {
firstName: response.properties.firstName,
lastName: response.properties.lastName,
email: response.properties.email,
username: response.properties.username
}
};
}

sso(data: HylandSSORequestInterface): Observable<UserInterface> {
const url = environment.apiUrl + "/users/sso";
return this.http
.post<AuthResponseInterface>(url, data)
.post<AuthUserResponseInterface>(url, data)
.pipe(map(this.getUser));
}
signOut(): Observable<void> {
const url = `${this.nuxeoJsClientService.getBaseUrl()}logout`;
return this.http.get<void>(url, {});
}

}



Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ export const authActions = createActionGroup({
"Get current user": emptyProps(),
"Get current user success": props<{ currentUser: UserInterface }>(),
"Get current user failure": emptyProps(),
"Sign out": emptyProps(),
"Sign out success": emptyProps(),
"Sign out failure": props<{ errors: BackendErrorsInterface }>(),
},
});
36 changes: 9 additions & 27 deletions nuxeo-admin-console-web/angular-app/src/app/auth/store/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ export const getCurrentUserEffect = createEffect(
return actions$.pipe(
ofType(authActions.getCurrentUser),
switchMap(() => {
const token = persistenceService.get("accessToken");

if (!token) {
return of(authActions.getCurrentUserFailure());
}
return authService.getCurrentUser().pipe(
map((currentUser: UserInterface) => {
return authActions.getCurrentUserSuccess({ currentUser });
Expand All @@ -36,23 +31,22 @@ export const getCurrentUserEffect = createEffect(
{ functional: true }
);

export const ssoEffect = createEffect(
export const signOutEffect = createEffect(
(
actions$ = inject(Actions),
authService = inject(AuthService),
persistenceService = inject(PersistenceService)
) => {

return actions$.pipe(
ofType(authActions.sso),
switchMap(({ request }) => {
return authService.sso(request).pipe(
map((currentUser: UserInterface) => {
persistenceService.set("accessToken", currentUser.token);
return authActions.ssoSuccess({ currentUser });
ofType(authActions.signOut),
switchMap(() => {
return authService.signOut().pipe(
map(() => {
return authActions.signOutSuccess();
}),
catchError((errorResponse: HttpErrorResponse) => {
return of(
authActions.ssoFailure({
authActions.signOutFailure({
errors: errorResponse.error.errors,
})
);
Expand All @@ -61,17 +55,5 @@ export const ssoEffect = createEffect(
})
);
},
{ functional: true }
);

export const redirectAfterSSOEffect = createEffect(
(actions$ = inject(Actions), router = inject(Router)) => {
return actions$.pipe(
ofType(authActions.ssoSuccess),
tap(() => {
router.navigateByUrl("/");
})
);
},
{ functional: true, dispatch: false }
);
);
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const authFeature = createFeature({
on(authActions.getCurrentUser, (state) => ({
...state,
isLoading: true,
})),
}
)),
on(authActions.getCurrentUserSuccess, (state, action) => ({
...state,
isLoading: false,
Expand All @@ -46,6 +47,14 @@ const authFeature = createFeature({
on(routerNavigationAction, (state) => ({
...state,
validationErrors: null,
})),
on(authActions.signOutSuccess, (state) => ({
...state,
currentUser: null,
})),
on(authActions.signOutFailure, (state, { errors }) => ({
...state,
errors,
}))
),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@ import { UserInterface } from "../../shared/types/user.interface";
export interface AuthResponseInterface {
user: UserInterface;
}

export interface AuthUserResponseInterface{
id:string,
properties:{
firstName:string,
lastName:string,
email: string;
username: string;
}
}
Loading

0 comments on commit 36368e4

Please sign in to comment.