Commit 485c3b72 authored by Tifenn Guillas's avatar Tifenn Guillas

Merge branch '148-add-comments' into 'develop'

Resolve "Add comments"

Closes #148

See merge request !159
parents 64ff8597 db5e1aa2
Pipeline #3407 passed with stages
in 9 minutes and 36 seconds
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { ActionReducerMap, ActionReducer, MetaReducer } from '@ngrx/store';
import * as fromRouter from '@ngrx/router-store';
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { NgModule } from '@angular/core';
import { KeycloakAngularModule } from 'keycloak-angular';
......@@ -18,4 +27,8 @@ import { AuthEffects } from './store/auth.effects';
initializeKeycloakAnis
]
})
/**
* @class
* @classdesc Authentication module.
*/
export class AuthModule { }
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { APP_INITIALIZER } from '@angular/core';
import { from } from 'rxjs';
import { KeycloakService, KeycloakEventType } from 'keycloak-angular';
import { Store } from '@ngrx/store';
import { from } from 'rxjs';
import { KeycloakService, KeycloakEventType } from 'keycloak-angular';
import * as keycloakActions from './store/auth.action';
import * as fromKeycloak from './store/auth.reducer';
......
import * as authActions from './auth.action';
import { UserProfile } from './user-profile.model';
describe('[Auth] Action', () => {
it('should create LoginAction', () => {
const action = new authActions.LoginAction();
expect(action.type).toEqual(authActions.LOGIN);
});
it('should create LogoutAction', () => {
const action = new authActions.LogoutAction();
expect(action.type).toEqual(authActions.LOGOUT);
});
it('should create AuthSuccessAction', () => {
const action = new authActions.AuthSuccessAction();
expect(action.type).toEqual(authActions.AUTH_SUCCESS);
});
it('should create LoadUserProfileSuccessAction', () => {
const userProfile: UserProfile = { id: 'toto@mail.com', username: 'toto' };
const action = new authActions.LoadUserProfileSuccessAction(userProfile);
expect(action.type).toEqual(authActions.LOAD_USER_PROFILE_SUCCESS);
expect(action.payload).toEqual(userProfile);
});
it('should create OpenEditProfileAction', () => {
const action = new authActions.OpenEditProfileAction();
expect(action.type).toEqual(authActions.OPEN_EDIT_PROFILE);
});
});
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Action } from '@ngrx/store';
import { UserProfile } from './user-profile.model';
export const LOGIN = '[Auth] Login';
export const LOGOUT = '[Auth] Logout';
export const AUTH_SUCCESS = '[Auth] Auth Success';
export const LOAD_USER_PROFILE_SUCCESS = '[Auth] Load User Profile Success';
export const OPEN_EDIT_PROFILE = '[Auth] Edit Profile';
/**
* @class
* @classdesc LoginAction action.
* @readonly
*/
export class LoginAction implements Action {
readonly type = LOGIN;
constructor(public payload: {} = null) { }
}
/**
* @class
* @classdesc LogoutAction action.
* @readonly
*/
export class LogoutAction implements Action {
readonly type = LOGOUT;
constructor(public payload: {} = null) { }
}
/**
* @class
* @classdesc AuthSuccessAction action.
* @readonly
*/
export class AuthSuccessAction implements Action {
readonly type = AUTH_SUCCESS;
constructor(public payload: {} = null) { }
}
/**
* @class
* @classdesc LoadUserProfileSuccessAction action.
* @readonly
*/
export class LoadUserProfileSuccessAction implements Action {
readonly type = LOAD_USER_PROFILE_SUCCESS;
constructor(public payload: UserProfile) { }
}
/**
* @class
* @classdesc OpenEditProfileAction action.
* @readonly
*/
export class OpenEditProfileAction implements Action {
readonly type = OPEN_EDIT_PROFILE;
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Injectable } from '@angular/core';
import { from } from 'rxjs';
import { switchMap, map, tap } from 'rxjs/operators';
......@@ -9,24 +18,37 @@ import * as authActions from './auth.action';
import { environment } from '../../../environments/environment';
@Injectable()
/**
* @class
* @classdesc Authentication effects.
*/
export class AuthEffects {
constructor(
private actions$: Actions,
private keycloak: KeycloakService
) { }
/**
* Logs in.
*/
@Effect({ dispatch: false })
loginAction$ = this.actions$.pipe(
ofType(authActions.LOGIN),
tap(_ => this.keycloak.login())
);
/**
* Logs out.
*/
@Effect({ dispatch: false })
logoutAction$ = this.actions$.pipe(
ofType(authActions.LOGOUT),
tap(_ => this.keycloak.logout())
);
/**
* Loads user profile when log in success.
*/
@Effect()
authSuccessAction$ = this.actions$.pipe(
ofType(authActions.AUTH_SUCCESS),
......@@ -37,6 +59,9 @@ export class AuthEffects {
)
);
/**
* Opens edit profile page.
*/
@Effect({ dispatch: false })
OpenEditProfileAction$ = this.actions$.pipe(
ofType(authActions.OPEN_EDIT_PROFILE),
......
import * as fromAuth from './auth.reducer';
import * as authActions from './auth.action';
import { UserProfile } from './user-profile.model';
describe('[Auth] Reducer', () => {
it('should return init state', () => {
const { initialState } = fromAuth;
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(state).toBe(initialState);
});
it('should set isAuthenticated to true', () => {
const { initialState } = fromAuth;
const action = new authActions.AuthSuccessAction();
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeTruthy();
expect(state.userProfile).toBeNull();
expect(state).not.toEqual(initialState);
});
it('should set userProfile', () => {
const userProfile: UserProfile = { id: 'toto@mail.com', username: 'toto' };
const { initialState } = fromAuth;
const action = new authActions.LoadUserProfileSuccessAction(userProfile);
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeFalsy();
expect(state.userProfile).toEqual(userProfile);
expect(state).not.toEqual(initialState);
});
it('should get isAuthenticated', () => {
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(fromAuth.isAuthenticated(state)).toBeFalsy();
});
it('should get userProfile', () => {
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(fromAuth.getUserProfile(state)).toBeNull();
});
});
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import * as actions from './auth.action';
import { UserProfile } from './user-profile.model';
/**
* Interface for authentication state.
*
* @interface State
*/
export interface State {
isAuthenticated: boolean;
userProfile: any;
userProfile: UserProfile;
}
export const initialState: State = {
......@@ -10,6 +25,14 @@ export const initialState: State = {
userProfile: null
};
/**
* Reduces state.
*
* @param {State} state - The state.
* @param {actions} action - The action.
*
* @return State
*/
export function reducer(state: State = initialState, action: actions.Actions): State {
switch (action.type) {
case actions.AUTH_SUCCESS:
......@@ -19,11 +42,9 @@ export function reducer(state: State = initialState, action: actions.Actions): S
};
case actions.LOAD_USER_PROFILE_SUCCESS:
const userProfile = action.payload;
return {
...state,
userProfile
userProfile: action.payload
}
default:
......
import * as authSelector from './auth.selector';
import * as fromAuth from './auth.reducer';
describe('[Auth] Selector', () => {
it('should get isAuthenticated', () => {
const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.isAuthenticated(state)).toBeFalsy();
});
it('should get userProfile', () => {
const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.getUserProfile(state)).toBeNull();
});
});
\ No newline at end of file
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { createSelector, createFeatureSelector } from '@ngrx/store';
import * as auth from './auth.reducer';
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Interface for user profile.
*
* @interface UserProfile
*/
export interface UserProfile {
id?: string;
username?: string;
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
.dropdown-up {
top: 80% !important;
right: 5px !important;
......
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
import { Instance } from '../../metamodel/model';
......@@ -10,6 +19,10 @@ import { environment } from '../../../environments/environment'
styleUrls: [ 'nav.component.css' ],
changeDetection: ChangeDetectionStrategy.OnPush
})
/**
* @class
* @classdesc Core nav component.
*/
export class NavComponent {
@Input() isAuthenticated: boolean;
@Input() userProfile: UserProfile;
......@@ -17,8 +30,14 @@ export class NavComponent {
@Output() login: EventEmitter<any> = new EventEmitter();
@Output() logout: EventEmitter<any> = new EventEmitter();
@Output() openEditProfile: EventEmitter<any> = new EventEmitter();
baseHref: string = environment.baseHref;
/**
* Checks if search link is allowed.
*
* @return boolean
*/
isSearchAllowed(): boolean {
if (this.instance && this.instance.config.search) {
return this.instance.config.search;
......@@ -26,6 +45,11 @@ export class NavComponent {
return false;
}
/**
* Checks if search multiple link is allowed.
*
* @return boolean
*/
isSearchMultipleAllowed(): boolean {
if (this.instance && this.instance.config.search_multiple) {
return this.instance.config.search_multiple.allowed;
......@@ -33,6 +57,11 @@ export class NavComponent {
return false;
}
/**
* Checks if documentation link is allowed.
*
* @return boolean
*/
isDocumentationAllowed(): boolean {
if (this.instance && this.instance.config.documentation) {
return this.instance.config.documentation;
......@@ -40,15 +69,30 @@ export class NavComponent {
return false;
}
emitLogin() {
/**
* Emits event to log in.
*
* @fires EventEmitter<any>
*/
emitLogin(): void {
this.login.emit();
}
emitLogout() {
/**
* Emits event to log out.
*
* @return EventEmitter<any>
*/
emitLogout(): void {
this.logout.emit();
}
emitOpenEditProfile() {
/**
* Emits event to go to edit profile page.
*
* @return EventEmitter<any>
*/
emitOpenEditProfile(): void {
this.openEditProfile.emit();
}
}
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
main {
margin-top: 100px;
}
\ No newline at end of file
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
......@@ -13,6 +22,11 @@ import { UserProfile } from '../../auth/store/user-profile.model';
import { Instance } from '../../metamodel/model';
import { VERSIONS } from '../../../settings/settings';
/**
* Interface for store state.
*
* @interface StoreState
*/
interface StoreState {
auth: fromAuth.State;
metamodel: fromMetamodel.State;
......@@ -24,6 +38,12 @@ interface StoreState {
styleUrls: [ './app.component.css' ],
encapsulation: ViewEncapsulation.None
})
/**
* @class
* @classdesc App container.
*
* @implements OnInit
*/
export class AppComponent implements OnInit {
public anisClientVersion: string = VERSIONS.anisClient;
public year = (new Date()).getFullYear();
......@@ -41,14 +61,23 @@ export class AppComponent implements OnInit {
this.store.dispatch(new instanceActions.LoadInstanceMetaAction());
}
/**
* Dispatches action to log in.
*/
login(): void {
this.store.dispatch(new authActions.LoginAction());
}
/**
* Dispatches action to log out.
*/
logout(): void {
this.store.dispatch(new authActions.LogoutAction());
}
/**
* Dispatches action to open edit profile page.
*/
openEditProfile(): void {
this.store.dispatch(new authActions.OpenEditProfileAction());
}
......