-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
i871: F5 in WTI causes logout #1021
Merged
Merged
Changes from 81 commits
Commits
Show all changes
86 commits
Select commit
Hold shift + click to select a range
5338cf4
i186: removed "defaultProject" from angular.json;
a65cd49
i186: removed unsupported option "es5BrowserSupport" from angular.json:
de5c111
i186: updated WTI-UI package.json to Angular 16 and its dependencies.
3e23f02
i186: additional package.json version updates: angular/material & /cdk.
7630ef3
i186: updated app.module for new angular/material subclass imports.
fa796fc
i186: removed "entryComponents" declarations; these were...
74e3b98
i833: update .github/workflows/ci.yml to require node v16.20.x
e742b8d
i833: update github "checkout" action from V2 to V4.
8cc4f89
i833: update package.json to specify core-js v3.33.1 over deprecated 2.6
ff860b7
Merge pull request #834 from clevengr/i_833_Upgrade_WTI_Angular_Compo…
lane55 b8102cc
i871: added global constants file to WTI-UI project.
c8b409b
i871: update app.component in prep for supporting F5 redirection:
60b60df
i871: attempt to enable browser console route tracing (doesn't work?)
860433f
i871: save "runs page" to sessionStorage on view startup.
18e2af7
i871: added 'title' elements to Angular routing paths.
clevengr c4bc6c3
i871: subscribe to router events in AppComponent constructor for debug.
179d976
i871: update AuthService.isLoggedIn() to also check for "current page".
bb45d5b
i871: update WTI-UI constants.ts with keys for every UI page.
clevengr 6e1723d
i871: make clars and scorebd pages update localStorage "curPage".
clevengr 51a0bb3
i871: make options page update localStorage curPage
clevengr 8dfd00b
i871: refactor loadEnvironment(); call on Refresh as well as initial.
clevengr cde0e9c
i871: add console debugging output.
clevengr fb0eab2
i871: remove RouterModule tracing (previously added for debugging).
clevengr c9a3689
i871: add debugging console output.
clevengr cdf459b
i871: add code to save/restore Auth token/username in browser.
clevengr 52db6e2
i871: add ContestService as core module provider.
clevengr ebb101a
I871: major updates to multiple modules, as follows:
a46b66e
I871: fixed incorrect parameter names in AuthService.
clevengr 9ef0128
i871: set DEBUG_MODE to true (should be reset to null before release)
clevengr 1d45993
i871: added debug code and documentation comments.
clevengr 71628f6
i871: added debug print statements
clevengr e570d65
i871: add "build>options" props to force generation of JS->TS sourcemaps
b577f3d
i871: added debug output to multiple files.
f848105
i871: added param to @injectable forcing singleton; added debug output
edd8e77
i871: added no-arg constructor which prints debug output & calls super()
4e146a6
i871: added debug output
clevengr fa8d6a8
i871: made abstract IContestService Injectable,providedIn:root. Also,
clevengr e584e0e
i871: made services Injectable and providedIn:root.
clevengr b1ec913
i871: removed no-longer-needed import of Injector.
clevengr 455cc8d
i871: injected services via ctor params instead of using an Injector.
clevengr 4cfd9a4
i871: updated debugging output messages to properly reflect code flow.
867f558
i871: change ctor param from ContestService to IContestService.
ce6fe5b
i186: updated WTI-UI package.json to Angular 16 and its dependencies.
7a270b3
i186: additional package.json version updates: angular/material & /cdk.
5daee21
i871: added global constants file to WTI-UI project.
4278c04
i871: update app.component in prep for supporting F5 redirection:
12e95ad
i871: attempt to enable browser console route tracing (doesn't work?)
9ec3cd7
i871: save "runs page" to sessionStorage on view startup.
d4fe6ca
i871: added 'title' elements to Angular routing paths.
clevengr 89ff46e
i871: subscribe to router events in AppComponent constructor for debug.
ed3de67
i871: update AuthService.isLoggedIn() to also check for "current page".
e96eed2
i871: update WTI-UI constants.ts with keys for every UI page.
clevengr 768d627
i871: make clars and scorebd pages update localStorage "curPage".
clevengr 0decd75
i871: make options page update localStorage curPage
clevengr 9808508
i871: refactor loadEnvironment(); call on Refresh as well as initial.
clevengr 34f87de
i871: add console debugging output.
clevengr 16745c9
i871: remove RouterModule tracing (previously added for debugging).
clevengr 6337158
i871: add debugging console output.
clevengr b551ca0
i871: add code to save/restore Auth token/username in browser.
clevengr fea07e7
i871: add ContestService as core module provider.
clevengr fcafdc4
I871: major updates to multiple modules, as follows:
468a1e3
I871: fixed incorrect parameter names in AuthService.
clevengr 9c51ec9
i871: set DEBUG_MODE to true (should be reset to null before release)
clevengr 3f9c4d9
i871: added debug code and documentation comments.
clevengr 28459c5
i871: added debug print statements
clevengr fd2b1b6
i871: add "build>options" props to force generation of JS->TS sourcemaps
5335d17
i871: added debug output to multiple files.
81255ec
i871: added param to @injectable forcing singleton; added debug output
8c91b01
i871: added no-arg constructor which prints debug output & calls super()
0d3b626
i871: added debug output
clevengr dbfba5e
i871: made abstract IContestService Injectable,providedIn:root. Also,
clevengr 95dac1c
i871: made services Injectable and providedIn:root.
clevengr b1519f9
i871: removed no-longer-needed import of Injector.
clevengr aa235e7
i871: injected services via ctor params instead of using an Injector.
clevengr cf408cc
i871: updated debugging output messages to properly reflect code flow.
85bb3f6
i871: change ctor param from ContestService to IContestService.
8af8601
Merge branch 'i871_F5_in_WTI' of https://github.com/clevengr/pc2v9.gi…
clevengr 572e23a
i871: delete conflict markers overlooked during merge.
clevengr a8e4d94
i871: use "IWebsocketService" instead of "WebsocketService". Result:
clevengr 4605979
i871: fix typo in console output.
clevengr ce3a96f
i871: added class-level documentation on code flow.
clevengr 718fd4f
i871: restore OptionsPage values from sessionStorage on F5 restart.
1641b6a
i871: add constants for Option Details; rename constants for clarity.
a6a2589
i871: add OptionsPage radiobtn handlers to save state in sessionStorage
fc15f20
i871: add debug output to UiHelperService constructor.
9bbe022
i871: save opts in sessionStorage in ngOnInit and in btn handlers; also,
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,241 @@ | ||
import { Component, OnInit } from '@angular/core'; | ||
import { HttpClient } from '@angular/common/http'; | ||
import { environment } from 'src/environments/environment'; | ||
import * as Constants from 'src/constants'; | ||
import { Router } from '@angular/router'; | ||
import { AuthService } from 'src/app/modules/core/auth/auth.service' ; | ||
import { IContestService } from 'src/app/modules/core/abstract-services/i-contest.service' ; | ||
import { IWebsocketService } from 'src/app/modules/core/abstract-services/i-websocket.service' ; | ||
import { DEBUG_MODE } from 'src/constants'; | ||
|
||
/* | ||
This AppComponent class is the main starting point for the WTI-UI Angular Single-Page-Application (SPA). | ||
(The overall SPA starts in main.ts, which invokes app.module.ts, which in turn bootstraps this AppComponent class.) | ||
|
||
When app.module.ts invokes this class's constructor, the constructor parameters cause TypeScript to automatically construct | ||
local property variables (objects) of type HttpClient, Router, AuthService, IContestService, and IWebsocketService. | ||
Construcing the AuthService object in turn causes creation of an ITeamsService object. | ||
|
||
The AuthService, IContestService, ITeamsService, and IWebsocketService classes are all listed in the "providers" array | ||
of class CoreModule (core.module.ts), which means that CoreModule is responsible for providing those service classes. | ||
All four service classes are marked as "injectable", which means they can be injected into other classes. All four classes | ||
are marked as "providedIn: 'root'" in their "@Injectable" decorator, which means they are all defined as singletons provided | ||
by (injected by) the "root injector" | ||
|
||
The latter three classes (IContestService, ITeamsService, and IWebsocketService) are listed in CoreModule with "provide" properties | ||
indicating that they are to be "provided" by corresponding "factory methods" (also in CoreModule). These factory methods choose | ||
between "real" and "mock" service providers depending on the value of an "environment flag" named "useMock" (see the files | ||
under "environments"). | ||
|
||
Once construction is complete, this AppComponent then starts running at its "ngOnInit()" method. | ||
ngOnInit() checks the browser's "sessionStorage" to see if there is a "current page" recorded there. | ||
If not, AppComponent invokes "loadEnvironment()" to load the SPA configuration from file "assets/appconfig.json". | ||
|
||
If an existing "current page" IS found in "sessionStorage", AppComponent interprets it as indicating that a "browser refresh" | ||
(F5) has occurred. In this case, AppComponent performs the same "loadEnvironment() operations, then loads additional state | ||
information (userName and token value) from "sessionStorage" and restores it into the AuthService class. | ||
It then startsa new WebSocket for communication with the WTI Server, checks the ContestService "isContestRunning" value | ||
(using it to trigger updates to things like display of problem names), and finally uses the Router to navigate to the | ||
previous SPA page. | ||
|
||
Meanwhile, as part of the AppModule bootstrap process, module "AppRoutingModule" sets up a list of "available routes" | ||
(that is, pages which the SPA knows how to transfer to), with the first (default) route being the LoginPageComponent. | ||
This causes the SPA to display the LoginPageComponent, which builds a "submission form" for the user to enter | ||
team name and password. When this form's "Submit" button is clicked, | ||
the LoginPageComponent.onSubmit() method invokes the AuthService's "login" method, which connects to | ||
the WTI server and logs the user into the PC2 server, then (on successful login) uses the Router's | ||
path list to transfer to the "/runs" page. | ||
|
||
*/ | ||
|
||
@Component({ | ||
selector: 'app-root', | ||
templateUrl: './app.component.html', | ||
styleUrls: ['./app.component.scss'] | ||
}) | ||
|
||
export class AppComponent implements OnInit { | ||
configLoaded = false; | ||
|
||
constructor(private _httpClient: HttpClient) { } | ||
constructor(private _httpClient: HttpClient, | ||
public router: Router, | ||
private _authService: AuthService, | ||
private _contestService: IContestService, | ||
private _websocketService: IWebsocketService) { | ||
if (DEBUG_MODE) { | ||
console.log("Executing AppComponent constructor..."); | ||
} | ||
//this.router.events.subscribe(console.log); //shows router tracing on console | ||
} | ||
|
||
ngOnInit(): void { | ||
// Load appconfig.json from assets directory, overwrite environment.ts | ||
// with these values | ||
this._httpClient.get('assets/appconfig.json') | ||
.subscribe((data: any) => { | ||
this.configLoaded = true; | ||
if (!data) { return; } | ||
Object.keys(data).forEach((key: string) => environment[key] = data[key]); | ||
}, (error: any) => { | ||
console.log('could not find appconfig.json in assets directory. using default values!'); | ||
this.configLoaded = true; | ||
}); | ||
} | ||
} | ||
|
||
if (DEBUG_MODE) { | ||
console.log ("Entering AppComponent.ngOnInit()..."); | ||
console.log ("...sessionStorage.length = " + sessionStorage.length); | ||
if (sessionStorage.length > 0) { | ||
console.log ("...sessionStorage values:"); | ||
for (let i = 0; i < sessionStorage.length; i++) { | ||
let key = sessionStorage.key(i); | ||
let value = sessionStorage.getItem(key); | ||
console.log(" Key: ", key, " Value: ", value); | ||
} | ||
} | ||
} | ||
|
||
//check if we're loading for the first time | ||
if (!getCurrentPage()) { | ||
if (DEBUG_MODE) { | ||
console.log ('Starting Single-Page-Application from scratch...'); | ||
} | ||
//we have no current page so we must be starting from scratch; | ||
// the following matches the sum total of what ngOnInit() used to do before "F5 handling" was added -- jlc | ||
this.loadEnvironment(); | ||
|
||
} else { | ||
//there is a current page stored; we must be reloading from (e.g.) an F5 refresh | ||
if (DEBUG_MODE) { | ||
console.log ('Restarting Single-Page-Application after refresh navigation...'); | ||
} | ||
|
||
//restore former environment | ||
if (DEBUG_MODE) { | ||
console.log ("...Loading environment...") ; | ||
} | ||
this.loadEnvironment(); | ||
|
||
//The following was initially done by the login-page component's onSubmit() method during login; | ||
// this F5 "restore state" code needs to accomplish the equivalent: | ||
//this._authService.login(loginCreds) | ||
// .pipe(takeUntil(this._unsubscribe)) | ||
// .subscribe((result: TeamsLoginResponse) => { | ||
// this._authService.completeLogin(result.teamId, result.teamName); //save 'teamId' returned from HTTP login "token" | ||
// this._websocketService.startWebsocket(); //create a websocket connection to the WTI Server | ||
// this._contestService.getIsContestRunning() //get contest isRunning state and save in ContestService | ||
// .subscribe((val: boolean) => { | ||
// this._contestService.isContestRunning = val; | ||
// this._contestService.contestClock.next(); | ||
// }); | ||
// } | ||
|
||
//restore the connection token and username into the AuthService from browser sessionStorage | ||
let token = getCurrentToken(); | ||
let username = getCurrentUserName(); | ||
if (DEBUG_MODE) { | ||
if (!!token && !!username) { | ||
console.log("...restoring token '" + token + "' and username '" + username + "' into AuthService..." ); | ||
} | ||
} | ||
|
||
//check whether we found non-null token and username from sessionStorage | ||
if (! (!!token && !!username) ) { | ||
console.warn ("Attempting to reload after F5 restart but found invalid state: token = ", token, " and username = ", username, " !!") ; | ||
//TODO: what should happen if the above occurs?? | ||
} else { | ||
//put the token and username back into AuthService (accomplishing what was originally done | ||
// by AuthService.completeLogin(teamId, teamName); teamId is the "token") | ||
this._authService.token = token; | ||
this._authService.username = username; | ||
} | ||
|
||
//re-create websocket connection to the WTI Server | ||
if (DEBUG_MODE) { | ||
console.log ("...calling WebsocketService.startWebsocket()..."); | ||
} | ||
this._websocketService.startWebsocket(); | ||
|
||
//update the "is contest running" value in the IContestService object using the value returned from | ||
// an HTTP call within ContestService.getIsContestRunning() | ||
this._contestService.getIsContestRunning() | ||
.subscribe((val: boolean) => { | ||
if (DEBUG_MODE) { | ||
console.log("AppComponent F5 'restore-state' code: "); | ||
console.log (" Subscription callback from ContestService.getIsContestRunning() returned: ", val); | ||
console.log (" ContestService object has uniqueId", this._contestService.uniqueId); | ||
//JSON.stringify() can't handle recursive objects like Angular Subjects | ||
//console.log (JSON.stringify(this._contestService,null,2)); //obj, replacerFunction, indent | ||
console.log (" and contents:"); | ||
console.log (this._contestService); | ||
} | ||
this._contestService.isContestRunning = val; | ||
this._contestService.contestClock.next(); | ||
}); | ||
|
||
// transfer to the (former) "current page". | ||
let page = getCurrentPage(); | ||
if (DEBUG_MODE) { | ||
console.log ('...navigating to previous page: ' + page); | ||
} | ||
|
||
//navigate to the most recently saved page | ||
// TODO: consider whether using history.pushState()/popState() is a better solution for this... | ||
this.router.navigate([page]) | ||
.then(nav => { | ||
if (DEBUG_MODE) { | ||
console.log ("...navigation complete."); | ||
} | ||
}, err => { | ||
console.log("Call to router.navigate([" + page + "]) failed (returned '" + err + "')"); | ||
}); | ||
|
||
} | ||
|
||
} | ||
|
||
// Load appconfig.json from assets directory, overwrite environment.ts with these values | ||
loadEnvironment(): void { | ||
if (DEBUG_MODE) { | ||
console.log("...loading environment from 'assets/appconfig.json'..."); | ||
} | ||
this._httpClient.get('assets/appconfig.json') | ||
.subscribe((data: any) => { | ||
this.configLoaded = true; | ||
if (!data) { return; } | ||
Object.keys(data).forEach((key: string) => environment[key] = data[key]); | ||
}, (error: any) => { | ||
console.log('Could not find appconfig.json in assets directory. using default values!'); | ||
this.configLoaded = true; | ||
}); | ||
}//end function loadEnvironment() | ||
|
||
}//end class AppComponent | ||
|
||
//save the current page in sessionStorage so a subsequent F5 (refresh) can return to that page | ||
export function saveCurrentPage(page:string) { | ||
sessionStorage.setItem(Constants.CURRENT_PAGE_KEY, page) ; | ||
} | ||
|
||
//return the most recently saved page | ||
export function getCurrentPage() { | ||
return (sessionStorage.getItem(Constants.CURRENT_PAGE_KEY)); | ||
} | ||
|
||
//clear any record of a "current page" | ||
export function clearCurrentPage() { | ||
sessionStorage.removeItem(Constants.CURRENT_PAGE_KEY); | ||
} | ||
|
||
//save the current token in sessionStorage so a subsequent F5 (refresh) can restore it | ||
export function saveCurrentToken(token:string) { | ||
sessionStorage.setItem(Constants.CONNECTION_TOKEN_KEY, token) ; | ||
} | ||
|
||
//return the most recently saved token | ||
export function getCurrentToken() { | ||
return (sessionStorage.getItem(Constants.CONNECTION_TOKEN_KEY)); | ||
} | ||
|
||
//save the current username in sessionStorage so a subsequent F5 (refresh) can restore it | ||
export function saveCurrentUserName(username:string) { | ||
sessionStorage.setItem(Constants.CONNECTION_USERNAME_KEY, username) ; | ||
} | ||
|
||
//return the most recently saved username | ||
export function getCurrentUserName() { | ||
return (sessionStorage.getItem(Constants.CONNECTION_USERNAME_KEY)); | ||
} | ||
|
||
//clear all data in sessionStorage | ||
export function clearSessionStorage() { | ||
sessionStorage.clear(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
projects/WTI-UI/src/app/modules/core/abstract-services/i-contest.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
projects/WTI-UI/src/app/modules/core/abstract-services/i-teams.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a lot of cases where DEBUG_MODE is checked for logging purposes. Could we maybe be create a logging class that when initialized checks the DEBUG_MODE. Having if cases all around makes it harder to read. Logs should be for example used as log.debug("blah blah"). Alternatively we can use winston.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading some forums it is probably a bad idea to use winston on client side.