WTI UI Angular Project Structure - pc2ccs/pc2v9 GitHub Wiki

Overview

As described at Web Access and Web Team Interface Implementation, PC² supports browser access to a PC² contest through a component called the Web Team Interface (WTI). The WTI consists of two main elements: the WTI Server and the WTI Client. The WTI Server is implemented by a PC² project called WTI-API (see WTI API Project Implementation Details), while the WTI Client is implemented by the WTI-UI project.

As described at WTI UI Project Implementation Details, the WTI-UI project uses Angular to implement the browser-side "front end" of the WTI project. Angular code is based on the so-called MVVM architecture pattern. (MVVM is a variant of the classical MVC architecture pattern; the primary difference is that in MVVM -- unlike MVC -- the Model and the View communicate directly with each other using a component called, rather uncreatively, the "ModelView".)

An Angular application is comprised of several parts:

  • A set Angular modules, each of which is identified by the Angular @NgModule annotation.
  • A set of Angular components, each of which is identified by the Angular @Component annotation.
  • A set of HTML templates, each of which defines the overall HTML structure for one portion of the HTML pages generated by the application.
  • Optional additional information such as style sheets which can be used to control the appearance of HTML pages.

The Angular application uses these elements to define the structure and operations comprising the MVVM implementation.

Angular modules can reference other modules, and can also contain Angular components. Angular components contain references to HTML templates. The HTML templates can contain Angular directives which allow the component code to modify the HTML (for example, by inserting data into selected positions within the HTML code); in this way the Angular code can manage and modify the HTML which displayed by the browser.

The Angular modules implementing WTI-UI are contained in the folder src/app at the root of the PC² WTI-UI project. The topmost level of the WTI-UI Angular application is an Angular module named AppModule, which is written in TypeScript and contained in file src/app/app.module.ts.

AppModule imports various submodules comprising the WTI-UI interface from folder src/app/modules. Sub-folders under src/app/modules contain Angular modules defining the various pages comprising the WTI-UI single-page application: Login/Logout, Run Submission, Clarifications, Scoreboard, and Options. Each of these modules is also written in TypeScript, and is stored in a corresponding sub-folder beneath src/app/modules.

Each module folder (and subfolder) also contains a .html file which acts as the HTML template for the elements defined by that module, as well as a .scss (Sass Cascading Style Sheet) file which provides style sheet formatting for the corresponding HTML page.

The rest of this article covers details on the organization of the WTI-UI Angular project modules.

Core

The core module contains support for a variety of core elements used throughout the WTI-UI project. The top-level management of core modules is contained in Angular class CoreModule, defined in TypeScript file src/app/modules/core/core.module.ts . The primary function of CoreModule is to define factory methods for obtaining service providers. CoreModule defines three such factory methods:

  • TeamsServiceFactory, which returns a new TeamsService that supports making requests such as "login", "submit run", "submit clarification", etc.
  • ContestServiceFactory, which returns a new ContestService that supports making requests such as "get contest languages", "get contest problems", "get contest scoreboard", etc.
  • WebsocketServiceFactory, which returns a new WebsocketService that supports receiving websocket messages from a remote HTTP client (typically, from the WTI-API server to which the WTI-UI browser code is connected).

Each of the CoreModule factory methods is capable of returning either a "real" service or a "mock" version of that service; which is returned depends on the value of environment variable useMock: if useMock is true then a mock implementation is returned; otherwise, a real service implementation is returned.

Class CoreModule defines Angular providers which use the Angular dependency injection framework to make core services available to WTI-UI components. For example, CoreModule declares a providers list which includes

{ provide: ITeamsService, useFactory: TeamsServiceFactory, deps: [HttpClient] }

which says that the ITeamsService is provided (made available to other Angular modules) via the TeamsServiceFactory, which in turn "depends on" Angular module "HttpClient". CoreModule makes all of the WTI-UI services available to WTI-UI Angular modules in this same manner.

The core elements managed by CoreModule are all defined in subfolders beneath src/app/modules/core, and are organized as follows.

Core: Abstract Services

Folder src/app/modules/core/abstract-services contains TypeScript files defining each of the abstract services available to WTI-UI modules: ContestService, TeamsService, and WebsocketService. Each TypeScript module in abstract-services exports a corresponding abstract class (similar to an interface definition in Java; TypeScript does not directly support "interface definitions") which defines the methods which must be implemented by the corresponding service implementation. The exported classes are all named starting with an "I" (following the Java convention for naming interface definitions).

For example, the code defining the abstract "contest service", found in file src/app/module/core/abstract-services/i-contest.service.ts, exports abstract class IContestService which in turn defines the following abstract methods:

  • abstract getLanguages(): Observable<ContestLanguage[]>;
  • abstract getProblems(): Observable<ContestProblem[]>;
  • abstract getJudgements(): Observable<string[]>;
  • abstract getClarifications(): Observable<Clarification[]>;
  • abstract getIsContestRunning(): Observable;
  • abstract getStandings(): Observable;

Similarly, file /src/app/modules/core/abstract-services/i-teams-service.ts exports abstract class ITeamsService which defines the following abstract methods:

  • abstract login(loginCredentials: LoginCredentials): Observable;
  • abstract logout(): Observable;
  • abstract submitRun(submission: Submission): Observable;
  • abstract getRuns(): Observable<Run[]>;
  • abstract postClarification(clarification: NewClarification): Observable;

Abstract class IWebsocketService (found in file src/app/modules/core/abstract-services/i-websocket.service.ts) defines two abstract methods, startWebsocket() and stopWebsocket. In addition, it defines a concrete method incomingMessage(message: WebsocketMessage) which is used by various WTI-UI components to listen for and dispatch websocket messages to appropriate handlers.

Core: Services

Folder src/app/modules/core/services contains TypeScript files defining concrete implementations for each of the abstract services (as defined above): contest.service.ts, teams.service.ts, and websocket.service.ts. The services folder also contains matching "mock" implementation services (for example, contest.mock.service.ts), each of which provides a "mock implementation" of the corresponding abstract service. (A mock implementation in WTI-UI terms is one which returns "mock data" without actually connecting to a PC² server.)

The services folder also contains one additional "service implementation": ui-helper.service.ts, which provides UI support services such as displaying incoming clarification, incoming run judgement, and similar pop-up UI notifications.

Each of the concrete services defined in src/app/modules/core/services is implemented as a TypeScript class which extends the correspondingly-defined abstract service. Each concrete service class is annotated with the Angular @Injectable annotation. This annotation indicates to Angular that the corresponding class may be "injected" as a service into other Angular classes; this is how the various WTI-UI components access the available services.

For example, the constructor for the ClarificationsPageComponent lists IContestService as one of its parameters, and there are two @Injectable classes implementing IContestService: ContestService and ContestMockService. The WTI-UI Angular module CoreModule defines method ContestServiceFactory which returns either a ContestService implementation or a ContestMockService implementation (depending on the value of environment variable useMock), which causes the appropriate IContestService to be injected into the ClarificationsPageComponent constructor.

Note also that there exists a special service named AuthService, which is defined in a separate folder (see below).

Core: Models

Folder src/app/modules/core/models contains TypeScript files defining classes which represent "models" (data descriptions) for each of the data types which the WTI-UI maintains. In general these comprise WTI-UI versions of PC² models, and include the following Typescript classes (defined in correspondingly-named .ts files):

  • Submission, an encapsulation of all the data comprising a submission from a team to the PC² system.
  • Run, a description of the WTI-UI view of the results of a submission.
  • Clarification, an encapsulation of all information related to a clarification request submitted to the PC² system.
  • ContestLanguage, containing the name of a contest language along with its corresponding id in PC².
  • ContestProblem, containing the name of a contest problem along with a shortname description (typically used for display purposes).
  • FileSubmission, containing the name of a file associated with a submission, along with the byteData for the file.
  • LoginCredentials, containing a team name (PC² account name) and password.
  • NewClarification, containing a problem name and a clarification message being sent from a team to the PC² server.
  • TeamLoginResponse, containing a team name (PC² account name) and the corresponding unique teamId associated by the WTI-API server with that team's connection to the PC² server.
  • WebsocketMessage, containing a string identifying the message type along with an associated id for the message.

The WTI-UI project uses these classes to represent model data throughout the WTI-UI implementation.

Core: Auth Service

Folder src/app/modules/core/auth contains TypeScript files associated with the WTI-UI authorization service. This service comprises three classes, each defined in a correspondingly-name Typescript file:

  • AuthService
    This class interacts with the LoginPageComponent class to verify login credentials and complete the login process. AuthService is injected into LoginPageComponent as a service. When a team enters login credentials (account and password) and presses the Login button, the LoginPageComponent's onSubmit() method passes the login credentials to the injected AuthService's login() method.
    AuthService is itself injected with an ITeamsService; the AuthService login() method in turn invokes the ITeamsService login() method, which submits the login credentials via an HTTP request to the WTI-API server (which in turn submits them to the PC² server).
    The login response returned by the WTI-API server is passed back to the LoginPageComponent, which again uses AuthService to save the login response (by invoking AuthService.completeLogin()). LoginPageComponent then finalizes the login process by invoking its injected IWebsocketService's startWebsocketService() method to open a websocket back to the WTI-API server and add to the websocket a listener for websocket messages.

  • AuthGuard

    This injectable class is used by the WTI-UI to control what components in the WTI-UI interface can be invoked at a given time. The WTI-UI AppRoutingModule class works with the Angular RouterModule to establish "valid routes" to which a user can navigate, and uses its injected AuthGuard to establish control over those components which should not be accessible unless a team is logged in. Any attempt to access such a page causes the RouterModule to transfer first to AuthGuard, whose canActivate() method checks to see if the team is logged in. If so, navigation to the requested page is allowed to continue; otherwise, navigation is redirected to the Login page.

  • AuthInterceptor

    This injectable class is installed by the CoreModule class as an Angular HTTP Interceptor, meaning that all outgoing HTTP requests (such as Login, Submit Run, Submit Clarification, etc.) will first be passed to AuthInterceptor's intercept() method. AuthInterceptor is injected with an AuthService; intercept() fetches the current teamId token from its AuthService. (If this team has already successfully logged in, AuthService will contain the unique token associated by the WTI-API server with that team.) intercept() then adds the team's token (if it exists) to the HTTP request headers and then forwards the request to the WTI-API server. In this way every HTTP request after a successful login will contain the team's id token in the request headers, which is how the WTI-API server recognizes the team from which a request originated.

AppModule

In addition to importing the various Angular modules mentioned above, AppModule imports several general-purpose WTI-UI Angular modules, including:

  • AppRoutingModule, which handles connections between each of the AppModule main page components and the Angular modules which implement them.
  • SharedModule, which collects together a variety of components (such as web page headers, footers, and pop-up dialogs) which are shared among multiple pages in the browser interface.

See Also

⚠️ **GitHub.com Fallback** ⚠️