Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
anis
anis-client
Commits
621c2ceb
Commit
621c2ceb
authored
Nov 10, 2020
by
Tifenn Guillas
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into 148-add-comments
parents
c89f7a64
64ff8597
Changes
62
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
62 changed files
with
3127 additions
and
3379 deletions
+3127
-3379
.browserslistrc
.browserslistrc
+0
-0
VERSION
VERSION
+1
-1
conf-dev/Dockerfile
conf-dev/Dockerfile
+1
-1
e2e/tsconfig.json
e2e/tsconfig.json
+1
-1
package.json
package.json
+40
-38
src/app/app.module.ts
src/app/app.module.ts
+3
-3
src/app/auth/auth.module.ts
src/app/auth/auth.module.ts
+21
-0
src/app/auth/init.keycloak.ts
src/app/auth/init.keycloak.ts
+40
-0
src/app/auth/store/auth.action.ts
src/app/auth/store/auth.action.ts
+46
-0
src/app/auth/store/auth.effects.ts
src/app/auth/store/auth.effects.ts
+45
-0
src/app/auth/store/auth.reducer.ts
src/app/auth/store/auth.reducer.ts
+35
-0
src/app/auth/store/auth.selector.ts
src/app/auth/store/auth.selector.ts
+15
-0
src/app/auth/store/user-profile.model.ts
src/app/auth/store/user-profile.model.ts
+11
-0
src/app/core/components/nav.component.html
src/app/core/components/nav.component.html
+7
-7
src/app/core/components/nav.component.ts
src/app/core/components/nav.component.ts
+12
-2
src/app/core/containers/app.component.html
src/app/core/containers/app.component.html
+4
-2
src/app/core/containers/app.component.spec.ts
src/app/core/containers/app.component.spec.ts
+7
-10
src/app/core/containers/app.component.ts
src/app/core/containers/app.component.ts
+17
-10
src/app/login/components/account-benefits.component.html
src/app/login/components/account-benefits.component.html
+0
-10
src/app/login/components/account-benefits.component.spec.ts
src/app/login/components/account-benefits.component.spec.ts
+0
-20
src/app/login/components/account-benefits.component.ts
src/app/login/components/account-benefits.component.ts
+0
-7
src/app/login/components/form-forgot-password.component.html
src/app/login/components/form-forgot-password.component.html
+0
-12
src/app/login/components/form-forgot-password.component.spec.ts
...p/login/components/form-forgot-password.component.spec.ts
+0
-28
src/app/login/components/form-forgot-password.component.ts
src/app/login/components/form-forgot-password.component.ts
+0
-14
src/app/login/components/form-login.component.html
src/app/login/components/form-login.component.html
+0
-23
src/app/login/components/form-login.component.spec.ts
src/app/login/components/form-login.component.spec.ts
+0
-29
src/app/login/components/form-login.component.ts
src/app/login/components/form-login.component.ts
+0
-17
src/app/login/components/form-register.component.html
src/app/login/components/form-register.component.html
+0
-29
src/app/login/components/form-register.component.spec.ts
src/app/login/components/form-register.component.spec.ts
+0
-29
src/app/login/components/form-register.component.ts
src/app/login/components/form-register.component.ts
+0
-17
src/app/login/components/index.ts
src/app/login/components/index.ts
+0
-11
src/app/login/containers/change-password.component.html
src/app/login/containers/change-password.component.html
+0
-33
src/app/login/containers/change-password.component.spec.ts
src/app/login/containers/change-password.component.spec.ts
+0
-46
src/app/login/containers/change-password.component.ts
src/app/login/containers/change-password.component.ts
+0
-26
src/app/login/containers/forgot-password.component.css
src/app/login/containers/forgot-password.component.css
+0
-6
src/app/login/containers/forgot-password.component.html
src/app/login/containers/forgot-password.component.html
+0
-20
src/app/login/containers/forgot-password.component.spec.ts
src/app/login/containers/forgot-password.component.spec.ts
+0
-52
src/app/login/containers/forgot-password.component.ts
src/app/login/containers/forgot-password.component.ts
+0
-19
src/app/login/containers/login.component.css
src/app/login/containers/login.component.css
+0
-6
src/app/login/containers/login.component.html
src/app/login/containers/login.component.html
+0
-25
src/app/login/containers/login.component.spec.ts
src/app/login/containers/login.component.spec.ts
+0
-67
src/app/login/containers/login.component.ts
src/app/login/containers/login.component.ts
+0
-24
src/app/login/login-routing.module.ts
src/app/login/login-routing.module.ts
+0
-24
src/app/login/login.module.ts
src/app/login/login.module.ts
+0
-28
src/app/login/store/login.action.spec.ts
src/app/login/store/login.action.spec.ts
+0
-104
src/app/login/store/login.action.ts
src/app/login/store/login.action.ts
+0
-135
src/app/login/store/login.effects.ts
src/app/login/store/login.effects.ts
+0
-189
src/app/login/store/login.reducer.spec.ts
src/app/login/store/login.reducer.spec.ts
+0
-64
src/app/login/store/login.selector.spec.ts
src/app/login/store/login.selector.spec.ts
+0
-14
src/app/login/store/login.service.ts
src/app/login/store/login.service.ts
+0
-54
src/app/login/store/model/change-password.model.ts
src/app/login/store/model/change-password.model.ts
+0
-4
src/app/login/store/model/index.ts
src/app/login/store/model/index.ts
+0
-3
src/app/login/store/model/login-token.model.ts
src/app/login/store/model/login-token.model.ts
+0
-4
src/app/login/store/model/login.model.ts
src/app/login/store/model/login.model.ts
+0
-4
src/app/search/containers/dataset.component.ts
src/app/search/containers/dataset.component.ts
+4
-4
src/app/static/static.module.ts
src/app/static/static.module.ts
+5
-1
src/assets/silent-check-sso.html
src/assets/silent-check-sso.html
+9
-0
src/environments/environment.prod.ts
src/environments/environment.prod.ts
+4
-1
src/environments/environment.ts
src/environments/environment.ts
+4
-1
tsconfig.json
tsconfig.json
+1
-1
tslint.json
tslint.json
+65
-2
yarn.lock
yarn.lock
+2729
-2097
No files found.
browserslist
→
.
browserslist
rc
View file @
621c2ceb
File moved
VERSION
View file @
621c2ceb
3.4.0
\ No newline at end of file
3.5.0
\ No newline at end of file
conf-dev/Dockerfile
View file @
621c2ceb
FROM
node:1
3
-slim
FROM
node:1
4
-slim
ENV
DEBIAN_FRONTEND=noninteractive
...
...
e2e/tsconfig.json
View file @
621c2ceb
...
...
@@ -3,7 +3,7 @@
"compilerOptions"
:
{
"outDir"
:
"../out-tsc/e2e"
,
"module"
:
"commonjs"
,
"target"
:
"es
5
"
,
"target"
:
"es
2018
"
,
"types"
:
[
"jasmine"
,
"jasminewd2"
,
...
...
package.json
View file @
621c2ceb
...
...
@@ -11,52 +11,54 @@
},
"private"
:
true
,
"dependencies"
:
{
"
@angular/animations
"
:
"
~
9.1.1
"
,
"
@angular/common
"
:
"
~
9.1.1
"
,
"
@angular/compiler
"
:
"
~
9.1.1
"
,
"
@angular/core
"
:
"
~
9.1.1
"
,
"
@angular/forms
"
:
"
~
9.1.1
"
,
"
@angular/platform-browser
"
:
"
~
9.1.1
"
,
"
@angular/platform-browser-dynamic
"
:
"
~
9.1.1
"
,
"
@angular/router
"
:
"
~
9.1.1
"
,
"
@fortawesome/fontawesome-free
"
:
"
^5.1
3.0
"
,
"
@ng-select/ng-select
"
:
"
^
4
.0.
0
"
,
"
@ngrx/effects
"
:
"
^
9.1.0
"
,
"
@ngrx/entity
"
:
"
^
9.1.0
"
,
"
@ngrx/router-store
"
:
"
^
9.1.0
"
,
"
@ngrx/store
"
:
"
^
9.1.0
"
,
"
@ngrx/store-devtools
"
:
"
^
9.1.0
"
,
"
bootstrap
"
:
"
^4.
4.1
"
,
"
@angular/animations
"
:
"
~
10.2.2
"
,
"
@angular/common
"
:
"
~
10.2.2
"
,
"
@angular/compiler
"
:
"
~
10.2.2
"
,
"
@angular/core
"
:
"
~
10.2.2
"
,
"
@angular/forms
"
:
"
~
10.2.2
"
,
"
@angular/platform-browser
"
:
"
~
10.2.2
"
,
"
@angular/platform-browser-dynamic
"
:
"
~
10.2.2
"
,
"
@angular/router
"
:
"
~
10.2.2
"
,
"
@fortawesome/fontawesome-free
"
:
"
^5.1
5.1
"
,
"
@ng-select/ng-select
"
:
"
^
5
.0.
8
"
,
"
@ngrx/effects
"
:
"
^
10.0.1
"
,
"
@ngrx/entity
"
:
"
^
10.0.1
"
,
"
@ngrx/router-store
"
:
"
^
10.0.1
"
,
"
@ngrx/store
"
:
"
^
10.0.1
"
,
"
@ngrx/store-devtools
"
:
"
^
10.0.1
"
,
"
bootstrap
"
:
"
^4.
5.3
"
,
"
d3
"
:
"
^5.15.1
"
,
"
ng-inline-svg
"
:
"
^10.1.0
"
,
"
ngx-bootstrap
"
:
"
^5.6.1
"
,
"
keycloak-angular
"
:
"
^8.0.1
"
,
"
keycloak-js
"
:
"
^11.0.3
"
,
"
ng-inline-svg
"
:
"
^11.0.1
"
,
"
ngx-bootstrap
"
:
"
^6.1.0
"
,
"
ngx-json-viewer
"
:
"
^2.4.0
"
,
"
ngx-toastr
"
:
"
^1
2.0.1
"
,
"
rxjs
"
:
"
~6.
5.4
"
,
"
tslib
"
:
"
^
1.9
.0
"
,
"
ngx-toastr
"
:
"
^1
3.1.0
"
,
"
rxjs
"
:
"
~6.
6.3
"
,
"
tslib
"
:
"
^
2.0
.0
"
,
"
zone.js
"
:
"
~0.10.3
"
},
"devDependencies"
:
{
"
@angular-devkit/build-angular
"
:
"
~0.
901.1
"
,
"
@angular/cli
"
:
"
~
9.1.1
"
,
"
@angular/compiler-cli
"
:
"
~
9.1.1
"
,
"
@angular/language-service
"
:
"
~
9.1.1
"
,
"
@angular-devkit/build-angular
"
:
"
~0.
1002.0
"
,
"
@angular/cli
"
:
"
~
10.2.0
"
,
"
@angular/compiler-cli
"
:
"
~
10.2.2
"
,
"
@angular/language-service
"
:
"
~
10.2.2
"
,
"
@types/d3
"
:
"
^5.7.2
"
,
"
@types/jasmine
"
:
"
~3.
3.8
"
,
"
@types/jasmine
"
:
"
~3.
6.1
"
,
"
@types/jasminewd2
"
:
"
~2.0.3
"
,
"
@types/node
"
:
"
^12.11.1
"
,
"
codelyzer
"
:
"
^
5
.0.
0
"
,
"
codelyzer
"
:
"
^
6
.0.
1
"
,
"
intl
"
:
"
^1.2.5
"
,
"
jasmine-core
"
:
"
~3.
4
.0
"
,
"
jasmine-spec-reporter
"
:
"
~
4.2.1
"
,
"
karma
"
:
"
~
4.1
.0
"
,
"
karma-chrome-launcher
"
:
"
~
2.2
.0
"
,
"
karma-coverage-istanbul-reporter
"
:
"
~
2
.0.
1
"
,
"
karma-jasmine
"
:
"
~
2
.0.
1
"
,
"
karma-jasmine-html-reporter
"
:
"
^1.
4
.0
"
,
"
protractor
"
:
"
~
5.4
.0
"
,
"
ts-node
"
:
"
~
7
.0.0
"
,
"
tslint
"
:
"
~
5
.1
5
.0
"
,
"
typescript
"
:
"
~
3.7
.5
"
"
jasmine-core
"
:
"
~3.
5
.0
"
,
"
jasmine-spec-reporter
"
:
"
~
5.0.0
"
,
"
karma
"
:
"
~
5.0
.0
"
,
"
karma-chrome-launcher
"
:
"
~
3.1
.0
"
,
"
karma-coverage-istanbul-reporter
"
:
"
~
3
.0.
2
"
,
"
karma-jasmine
"
:
"
~
4
.0.
0
"
,
"
karma-jasmine-html-reporter
"
:
"
^1.
5
.0
"
,
"
protractor
"
:
"
~
7.0
.0
"
,
"
ts-node
"
:
"
~
9
.0.0
"
,
"
tslint
"
:
"
~
6
.1.0
"
,
"
typescript
"
:
"
~
4.0
.5
"
}
}
src/app/app.module.ts
View file @
621c2ceb
...
...
@@ -11,8 +11,8 @@ import { EffectsModule } from '@ngrx/effects';
import
{
CustomRouterStateSerializer
}
from
'
./shared/utils
'
;
import
{
reducers
,
metaReducers
}
from
'
./app.reducer
'
;
import
{
CoreModule
}
from
'
./core/core.module
'
;
import
{
AuthModule
}
from
'
./auth/auth.module
'
;
import
{
StaticModule
}
from
'
./static/static.module
'
;
import
{
LoginModule
}
from
'
./login/login.module
'
;
import
{
MetamodelModule
}
from
'
./metamodel/metamodel.module
'
;
import
{
AppRoutingModule
}
from
'
./app-routing.module
'
;
import
{
AppComponent
}
from
'
./core/containers/app.component
'
;
...
...
@@ -24,8 +24,8 @@ import { environment } from '../environments/environment';
BrowserAnimationsModule
,
HttpClientModule
,
CoreModule
,
AuthModule
,
StaticModule
,
LoginModule
,
MetamodelModule
,
StoreModule
.
forRoot
(
reducers
,
{
metaReducers
,
...
...
@@ -45,6 +45,6 @@ import { environment } from '../environments/environment';
!
environment
.
production
?
StoreDevtoolsModule
.
instrument
()
:
[],
EffectsModule
.
forRoot
([])
],
bootstrap
:
[
AppComponent
]
bootstrap
:
[
AppComponent
]
})
export
class
AppModule
{
}
src/app/auth/auth.module.ts
0 → 100644
View file @
621c2ceb
import
{
NgModule
}
from
'
@angular/core
'
;
import
{
KeycloakAngularModule
}
from
'
keycloak-angular
'
;
import
{
StoreModule
}
from
'
@ngrx/store
'
;
import
{
EffectsModule
}
from
'
@ngrx/effects
'
;
import
{
initializeKeycloakAnis
}
from
'
./init.keycloak
'
;
import
{
reducer
}
from
'
./store/auth.reducer
'
;
import
{
AuthEffects
}
from
'
./store/auth.effects
'
;
@
NgModule
({
imports
:
[
KeycloakAngularModule
,
StoreModule
.
forFeature
(
'
auth
'
,
reducer
),
EffectsModule
.
forFeature
([
AuthEffects
])
],
providers
:
[
initializeKeycloakAnis
]
})
export
class
AuthModule
{
}
src/app/auth/init.keycloak.ts
0 → 100644
View file @
621c2ceb
import
{
APP_INITIALIZER
}
from
'
@angular/core
'
;
import
{
from
}
from
'
rxjs
'
;
import
{
KeycloakService
,
KeycloakEventType
}
from
'
keycloak-angular
'
;
import
{
Store
}
from
'
@ngrx/store
'
;
import
*
as
keycloakActions
from
'
./store/auth.action
'
;
import
*
as
fromKeycloak
from
'
./store/auth.reducer
'
;
import
{
environment
}
from
'
../../environments/environment
'
;
function
initializeKeycloak
(
keycloak
:
KeycloakService
,
store
:
Store
<
{
keycloak
:
fromKeycloak
.
State
}
>
)
{
return
async
()
=>
{
from
(
keycloak
.
keycloakEvents$
).
subscribe
(
event
=>
{
if
(
event
.
type
===
KeycloakEventType
.
OnAuthSuccess
)
{
store
.
dispatch
(
new
keycloakActions
.
AuthSuccessAction
());
}
})
return
keycloak
.
init
({
config
:
{
url
:
environment
.
ssoAuthUrl
,
realm
:
environment
.
ssoRealm
,
clientId
:
environment
.
ssoClientId
,
},
initOptions
:
{
onLoad
:
'
check-sso
'
,
silentCheckSsoRedirectUri
:
window
.
location
.
origin
+
'
/assets/silent-check-sso.html
'
},
loadUserProfileAtStartUp
:
true
});
}
}
export
const
initializeKeycloakAnis
=
{
provide
:
APP_INITIALIZER
,
useFactory
:
initializeKeycloak
,
multi
:
true
,
deps
:
[
KeycloakService
,
Store
],
};
\ No newline at end of file
src/app/auth/store/auth.action.ts
0 → 100644
View file @
621c2ceb
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
'
;
export
class
LoginAction
implements
Action
{
readonly
type
=
LOGIN
;
constructor
(
public
payload
:
{}
=
null
)
{
}
}
export
class
LogoutAction
implements
Action
{
readonly
type
=
LOGOUT
;
constructor
(
public
payload
:
{}
=
null
)
{
}
}
export
class
AuthSuccessAction
implements
Action
{
readonly
type
=
AUTH_SUCCESS
;
constructor
(
public
payload
:
{}
=
null
)
{
}
}
export
class
LoadUserProfileSuccessAction
implements
Action
{
readonly
type
=
LOAD_USER_PROFILE_SUCCESS
;
constructor
(
public
payload
:
UserProfile
)
{
}
}
export
class
OpenEditProfileAction
implements
Action
{
readonly
type
=
OPEN_EDIT_PROFILE
;
constructor
(
public
payload
:
{}
=
null
)
{
}
}
export
type
Actions
=
LoginAction
|
LogoutAction
|
AuthSuccessAction
|
LoadUserProfileSuccessAction
|
OpenEditProfileAction
;
src/app/auth/store/auth.effects.ts
0 → 100644
View file @
621c2ceb
import
{
Injectable
}
from
'
@angular/core
'
;
import
{
from
}
from
'
rxjs
'
;
import
{
switchMap
,
map
,
tap
}
from
'
rxjs/operators
'
;
import
{
Effect
,
Actions
,
ofType
}
from
'
@ngrx/effects
'
;
import
{
KeycloakService
}
from
'
keycloak-angular
'
;
import
*
as
authActions
from
'
./auth.action
'
;
import
{
environment
}
from
'
../../../environments/environment
'
;
@
Injectable
()
export
class
AuthEffects
{
constructor
(
private
actions$
:
Actions
,
private
keycloak
:
KeycloakService
)
{
}
@
Effect
({
dispatch
:
false
})
loginAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
LOGIN
),
tap
(
_
=>
this
.
keycloak
.
login
())
);
@
Effect
({
dispatch
:
false
})
logoutAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
LOGOUT
),
tap
(
_
=>
this
.
keycloak
.
logout
())
);
@
Effect
()
authSuccessAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
AUTH_SUCCESS
),
switchMap
(
_
=>
from
(
this
.
keycloak
.
loadUserProfile
()).
pipe
(
map
(
userProfile
=>
new
authActions
.
LoadUserProfileSuccessAction
(
userProfile
))
)
)
);
@
Effect
({
dispatch
:
false
})
OpenEditProfileAction$
=
this
.
actions$
.
pipe
(
ofType
(
authActions
.
OPEN_EDIT_PROFILE
),
tap
(
_
=>
window
.
open
(
environment
.
ssoAuthUrl
+
'
/realms/
'
+
environment
.
ssoRealm
+
'
/account
'
,
'
_blank
'
))
);
}
src/app/
login
/store/
login
.reducer.ts
→
src/app/
auth
/store/
auth
.reducer.ts
View file @
621c2ceb
import
*
as
actions
from
'
./login.action
'
;
import
{
LoginToken
}
from
'
./model
'
;
import
*
as
actions
from
'
./auth.action
'
;
export
interface
State
{
isAuthenticated
:
boolean
;
loginToken
:
LoginToken
;
userProfile
:
any
;
}
export
const
initialState
:
State
=
{
isAuthenticated
:
false
,
loginToken
:
null
userProfile
:
null
};
export
function
reducer
(
state
:
State
=
initialState
,
action
:
actions
.
Actions
):
State
{
switch
(
action
.
type
)
{
case
actions
.
LOGIN_LOCAL_STORAGE_SUCCESS
:
case
actions
.
LOGIN_SUCCESS
:
const
loginToken
:
LoginToken
=
action
.
payload
;
case
actions
.
AUTH_SUCCESS
:
return
{
...
state
,
isAuthenticated
:
true
,
loginToken
isAuthenticated
:
true
};
case
actions
.
LOGOUT
:
case
actions
.
LOAD_USER_PROFILE_SUCCESS
:
const
userProfile
=
action
.
payload
;
return
{
isAuthenticated
:
fals
e
,
loginToken
:
null
}
;
...
stat
e
,
userProfile
}
default
:
return
state
;
...
...
@@ -36,4 +32,4 @@ export function reducer(state: State = initialState, action: actions.Actions): S
}
export
const
isAuthenticated
=
(
state
:
State
)
=>
state
.
isAuthenticated
;
export
const
get
LoginToken
=
(
state
:
State
)
=>
state
.
loginToken
;
export
const
get
UserProfile
=
(
state
:
State
)
=>
state
.
userProfile
;
src/app/
login
/store/
login
.selector.ts
→
src/app/
auth
/store/
auth
.selector.ts
View file @
621c2ceb
import
{
createSelector
,
createFeatureSelector
}
from
'
@ngrx/store
'
;
import
*
as
login
from
'
./
login
.reducer
'
;
import
*
as
auth
from
'
./
auth
.reducer
'
;
export
const
get
LoginS
tate
=
createFeatureSelector
<
login
.
State
>
(
'
login
'
);
export
const
get
Auth
tate
=
createFeatureSelector
<
auth
.
State
>
(
'
auth
'
);
export
const
isAuthenticated
=
createSelector
(
get
LoginS
tate
,
login
.
isAuthenticated
get
Auth
tate
,
auth
.
isAuthenticated
);
export
const
get
LoginToken
=
createSelector
(
get
LoginS
tate
,
login
.
getLoginToken
export
const
get
UserProfile
=
createSelector
(
get
Auth
tate
,
auth
.
getUserProfile
);
src/app/auth/store/user-profile.model.ts
0 → 100644
View file @
621c2ceb
export
interface
UserProfile
{
id
?:
string
;
username
?:
string
;
email
?:
string
;
firstName
?:
string
;
lastName
?:
string
;
enabled
?:
boolean
;
emailVerified
?:
boolean
;
totp
?:
boolean
;
createdTimestamp
?:
number
;
}
\ No newline at end of file
src/app/core/components/nav.component.html
View file @
621c2ceb
...
...
@@ -30,7 +30,7 @@
</ul>
<button
*ngIf=
"!isAuthenticated"
class=
"btn btn-outline-success my-2 my-sm-0"
id=
"button-sign-in"
routerLink=
"/l
ogin"
>
(click)=
"emitL
ogin
()
"
>
Sign In / Register
</button>
<span
*ngIf=
"isAuthenticated"
id=
"dropdown-menu"
dropdown
>
...
...
@@ -45,12 +45,12 @@
<ul
id=
"basic-link-dropdown"
*dropdownMenu
class=
"dropdown-menu dropdown-menu-right dropdown-up"
role=
"menu"
aria-labelledby=
"basic-link"
>
<li
id=
"li-email"
role=
"menuitem"
>
<span
class=
"dropdown-item font-italic"
>
{{
loginToken
.email }}
</span>
<span
class=
"dropdown-item font-italic"
>
{{
userProfile
.email }}
</span>
</li>
<li
class=
"divider dropdown-divider"
></li>
<li
role=
"menuitem"
>
<a
class=
"dropdown-item
"
routerLink=
"/change-password
"
>
<span
class=
"fas fa-
key fa-fw
"
></span>
Change password
<a
class=
"dropdown-item
pointer"
(click)=
"emitOpenEditProfile()
"
>
<span
class=
"fas fa-
id-card
"
></span>
Edit profile
</a>
</li>
<li
class=
"divider dropdown-divider"
></li>
...
...
@@ -71,7 +71,7 @@
<ul
id=
"basic-link-dropdown"
*dropdownMenu
class=
"dropdown-menu dropdown-menu-right dropdown-up"
role=
"menu"
aria-labelledby=
"basic-link"
>
<li
*ngIf=
"isAuthenticated"
role=
"menuitem"
>
<span
class=
"dropdown-item font-italic"
>
{{
loginToken
.email }}
</span>
<span
class=
"dropdown-item font-italic"
>
{{
userProfile
.email }}
</span>
</li>
<li
*ngIf=
"isAuthenticated"
class=
"divider dropdown-divider"
></li>
<li
role=
"menuitem"
>
...
...
@@ -96,8 +96,8 @@
</li>
<li
*ngIf=
"isAuthenticated"
class=
"divider dropdown-divider"
></li>
<li
*ngIf=
"isAuthenticated"
role=
"menuitem"
>
<a
class=
"dropdown-item
"
routerLink=
"/change-password
"
>
<span
class=
"fas fa-
key fa-fw
"
></span>
Change password
<a
class=
"dropdown-item
pointer"
(click)=
"emitOpenEditProfile()
"
>
<span
class=
"fas fa-
id-card
"
></span>
Edit profile
</a>
</li>
<li
class=
"divider dropdown-divider"
></li>
...
...
src/app/core/components/nav.component.ts
View file @
621c2ceb
import
{
Component
,
Input
,
Output
,
EventEmitter
,
ChangeDetectionStrategy
}
from
'
@angular/core
'
;
import
{
LoginToken
}
from
'
../../login/store/model
'
;
import
{
Instance
}
from
'
../../metamodel/model
'
;
import
{
UserProfile
}
from
'
../../auth/store/user-profile.model
'
;
import
{
environment
}
from
'
../../../environments/environment
'
@
Component
({
...
...
@@ -12,9 +12,11 @@ import { environment } from '../../../environments/environment'
})
export
class
NavComponent
{
@
Input
()
isAuthenticated
:
boolean
;
@
Input
()
loginToken
:
LoginToken
;
@
Input
()
userProfile
:
UserProfile
;
@
Input
()
instance
:
Instance
;
@
Output
()
login
:
EventEmitter
<
any
>
=
new
EventEmitter
();
@
Output
()
logout
:
EventEmitter
<
any
>
=
new
EventEmitter
();
@
Output
()
openEditProfile
:
EventEmitter
<
any
>
=
new
EventEmitter
();
baseHref
:
string
=
environment
.
baseHref
;
isSearchAllowed
():
boolean
{
...
...
@@ -38,7 +40,15 @@ export class NavComponent {
return
false
;
}
emitLogin
()
{
this
.
login
.
emit
();
}
emitLogout
()
{
this
.
logout
.
emit
();
}
emitOpenEditProfile
()
{
this
.
openEditProfile
.
emit
();
}
}
src/app/core/containers/app.component.html
View file @
621c2ceb
<header>
<app-nav
[isAuthenticated]=
"isAuthenticated | async"
[
loginToken]=
"loginToken
| async"
[
userProfile]=
"userProfile
| async"
[instance]=
"instance | async"
(logout)=
"logout()"
>
(login)=
"login()"
(logout)=
"logout()"
(openEditProfile)=
"openEditProfile()"
>
</app-nav>
</header>
<main
role=
"main"
class=
"container-fluid pb-4"
>
...
...
src/app/core/containers/app.component.spec.ts
View file @
621c2ceb
...
...
@@ -3,11 +3,10 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import
{
RouterTestingModule
}
from
'
@angular/router/testing
'
;
import
{
provideMockStore
,
MockStore
}
from
'
@ngrx/store/testing
'
;
import
*
as
fromAuth
from
'
../../auth/store/auth.reducer
'
;
import
*
as
authActions
from
'
../../auth/store/auth.action
'
;
import
{
UserProfile
}
from
'
../../auth/store/user-profile.model
'
;
import
{
AppComponent
}
from
'
./app.component
'
;
import
*
as
fromLogin
from
'
../../login/store/login.reducer
'
;
import
*
as
loginActions
from
'
../../login/store/login.action
'
;
import
{
LoginToken
}
from
'
../../login/store/model
'
;
import
*
as
fromMetamodel
from
'
../../metamodel/reducers
'
;
import
*
as
instanceActions
from
'
../../metamodel/action/instance.action
'
;
...
...
@@ -15,14 +14,14 @@ describe('[Core] Container: AppComponent', () => {
@
Component
({
selector
:
'
app-nav
'
,
template
:
''
})
class
NavStubComponent
{
@
Input
()
isAuthenticated
:
boolean
;
@
Input
()
loginToken
:
LoginToken
;
@
Input
()
userProfile
:
UserProfile
;
}
let
component
:
AppComponent
;
let
fixture
:
ComponentFixture
<
AppComponent
>
;
let
store
:
MockStore
;
const
initialState
=
{
login
:
{
...
from
Login
.
initialState
},
auth
:
{
...
from
Auth
.
initialState
},
metamodel
:
{
...
fromMetamodel
}
};
...
...
@@ -47,17 +46,15 @@ describe('[Core] Container: AppComponent', () => {
});
it
(
'
should execute ngOnInit lifecycle
'
,
()
=>
{
const
loginLocalStorageAction
=
new
loginActions
.
LoginLocalStorageAction
();
const
loadInstanceMetaAction
=
new
instanceActions
.
LoadInstanceMetaAction
();
const
spy
=
spyOn
(
store
,
'
dispatch
'
);
component
.
ngOnInit
();
expect
(
spy
).
toHaveBeenCalledTimes
(
2
);
expect
(
spy
).
toHaveBeenCalledWith
(
loginLocalStorageAction
);
expect
(
spy
).
toHaveBeenCalledTimes
(
1
);
expect
(
spy
).
toHaveBeenCalledWith
(
loadInstanceMetaAction
);
});
it
(
'
#logout() should dispatch LogoutAction
'
,
()
=>
{
const
logoutAction
=
new
login
Actions
.
LogoutAction
();
const
logoutAction
=
new
auth
Actions
.
LogoutAction
();
const
spy
=
spyOn
(
store
,
'
dispatch
'
);
component
.
logout
();
expect
(
spy
).
toHaveBeenCalledTimes
(
1
);
...
...
src/app/core/containers/app.component.ts
View file @
621c2ceb
...
...
@@ -3,18 +3,18 @@ import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import
{
Store
}
from
'
@ngrx/store
'
;
import
{
Observable
}
from
'
rxjs
'
;
import
*
as
fromLogin
from
'
../../login/store/login.reducer
'
;
import
*
as
loginActions
from
'
../../login/store/login.action
'
;
import
*
as
loginSelector
from
'
../../login/store/login.selector
'
;
import
{
LoginToken
}
from
'
../../login/store/model
'
;
import
*
as
fromMetamodel
from
'
../../metamodel/reducers
'
;
import
*
as
instanceActions
from
'
../../metamodel/action/instance.action
'
;
import
*
as
metamodelSelector
from
'
../../metamodel/selectors
'
;
import
*
as
fromAuth
from
'
../../auth/store/auth.reducer
'
;
import
*
as
authActions
from
'
../../auth/store/auth.action
'
;
import
*
as
authSelector
from
'
../../auth/store/auth.selector
'
;
import
{
UserProfile
}
from
'
../../auth/store/user-profile.model
'
;
import
{
Instance
}
from
'
../../metamodel/model
'
;
import
{
VERSIONS
}
from
'
../../../settings/settings
'
;
interface
StoreState
{
login
:
from
Login
.
State
;
auth
:
from
Auth
.
State
;
metamodel
:
fromMetamodel
.
State
;
}
...
...
@@ -28,21 +28,28 @@ export class AppComponent implements OnInit {
public
anisClientVersion
:
string
=
VERSIONS
.
anisClient
;
public
year
=
(
new
Date
()).
getFullYear
();
public
isAuthenticated
:
Observable
<
boolean
>
;
public
loginToken
:
Observable
<
LoginToken
>
;
public
userProfile
:
Observable
<
UserProfile
>
public
instance
:
Observable
<
Instance
>
;