From 5081ea80ae765fa82245d455791fbfa9f384d77d Mon Sep 17 00:00:00 2001 From: Randy Woods Date: Fri, 17 Jan 2025 14:07:01 -0700 Subject: [PATCH] Fixed token preparation for assessment removal Refactored deprecated .toPromise() usage in most places. --- CSETWebNg/src/app/app.module.ts | 29 +-- .../question-extras.component.ts | 2 +- .../user-language/user-language.component.ts | 6 +- .../my-assessments.component.ts | 86 +++++---- .../src/app/services/aggregation.service.ts | 9 +- .../assess-compare-analytics.service.ts | 10 +- .../src/app/services/assessment.service.ts | 172 +++++++++--------- .../app/services/authentication.service.ts | 34 ++-- CSETWebNg/src/app/services/config.service.ts | 20 +- 9 files changed, 196 insertions(+), 172 deletions(-) diff --git a/CSETWebNg/src/app/app.module.ts b/CSETWebNg/src/app/app.module.ts index acadfa303f..2788c23159 100644 --- a/CSETWebNg/src/app/app.module.ts +++ b/CSETWebNg/src/app/app.module.ts @@ -76,9 +76,9 @@ import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { - MAT_DATE_LOCALE, - MatNativeDateModule, - MatRippleModule + MAT_DATE_LOCALE, + MatNativeDateModule, + MatRippleModule } from '@angular/material/core'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressBarModule } from '@angular/material/progress-bar'; @@ -688,9 +688,11 @@ import { AllReviewedComponent } from './reports/all-reviewed/all-reviewed.compon import { QuestionsReviewedComponent } from './reports/questions-reviewed/questions-reviewed.component'; import { RolesChangedComponent } from './dialogs/roles-changed/roles-changed.component'; import { AnalyticsResultsComponent } from './assessment/results/analytics-results/analytics-results.component'; +import { firstValueFrom } from 'rxjs'; -@NgModule({ declarations: [ +@NgModule({ + declarations: [ AppComponent, InitialComponent, LoginComponent, @@ -1225,8 +1227,8 @@ import { AnalyticsResultsComponent } from './assessment/results/analytics-result AllAnsweredquestionsComponent, AllCommentsmarkedComponent, AllReviewedComponent, - QuestionsReviewedComponent, - RolesChangedComponent, + QuestionsReviewedComponent, + RolesChangedComponent, AnalyticsResultsComponent ], bootstrap: [AppComponent], imports: [BrowserModule, @@ -1324,8 +1326,8 @@ import { AnalyticsResultsComponent } from './assessment/results/analytics-result CodeEditorModule.forRoot({ typingsWorkerUrl: 'assets/workers/typings-worker.js', baseUrl: 'assets/monaco' - })], - providers: [ + })], + providers: [ TranslocoService, provideTranslocoScope('tutorial', 'reports'), ConfigService, @@ -1336,10 +1338,10 @@ import { AnalyticsResultsComponent } from './assessment/results/analytics-result return () => { return configSvc.loadConfig().then(() => { // Load and set the language based on config - return tSvc - .load(configSvc.config.defaultLang) - .toPromise() - .then(() => { + + const obs = tSvc.load(configSvc.config.defaultLang); + const prom = firstValueFrom(obs); + return prom.then(() => { tSvc.setActiveLang(configSvc.config.defaultLang); return authSvc.checkLocal(); }); @@ -1406,5 +1408,6 @@ import { AnalyticsResultsComponent } from './assessment/results/analytics-result FooterService, AnalyticsService, provideHttpClient(withInterceptorsFromDi()) - ] }) + ] +}) export class AppModule { } diff --git a/CSETWebNg/src/app/assessment/questions/question-extras/question-extras.component.ts b/CSETWebNg/src/app/assessment/questions/question-extras/question-extras.component.ts index 3e7c5596b9..20f18d3abd 100644 --- a/CSETWebNg/src/app/assessment/questions/question-extras/question-extras.component.ts +++ b/CSETWebNg/src/app/assessment/questions/question-extras/question-extras.component.ts @@ -166,7 +166,7 @@ export class QuestionExtrasComponent implements OnInit { async fetchDetails(): Promise { const details = this.questionsSvc.getDetails(this.myQuestion.questionId, this.myQuestion.questionType).toPromise(); - return details + return details; } /** diff --git a/CSETWebNg/src/app/dialogs/user-language/user-language.component.ts b/CSETWebNg/src/app/dialogs/user-language/user-language.component.ts index b85cbc18a2..207b1d2d82 100644 --- a/CSETWebNg/src/app/dialogs/user-language/user-language.component.ts +++ b/CSETWebNg/src/app/dialogs/user-language/user-language.component.ts @@ -5,6 +5,7 @@ import { EditUserComponent } from '../edit-user/edit-user.component'; import { TranslocoService } from '@jsverse/transloco'; import { ConfigService } from '../../services/config.service'; import { DateAdapter } from '@angular/material/core'; +import { firstValueFrom } from 'rxjs'; @Component({ selector: 'app-user-language', @@ -50,7 +51,10 @@ export class UserLanguageComponent implements OnInit { * */ save() { - this.tSvc.load(this.langSelection).toPromise().then(() => { + const obs = this.tSvc.load(this.langSelection); + const prom = firstValueFrom(obs); + + prom.then(() => { this.tSvc.setActiveLang(this.langSelection); this.authSvc.setUserLang(this.langSelection).subscribe(() => { this.dateAdapter.setLocale(this.langSelection); diff --git a/CSETWebNg/src/app/initial/my-assessments/my-assessments.component.ts b/CSETWebNg/src/app/initial/my-assessments/my-assessments.component.ts index f585b30ce3..853a243674 100644 --- a/CSETWebNg/src/app/initial/my-assessments/my-assessments.component.ts +++ b/CSETWebNg/src/app/initial/my-assessments/my-assessments.component.ts @@ -37,7 +37,8 @@ import { Title } from "@angular/platform-browser"; import { NavigationService } from "../../services/navigation/navigation.service"; import { QuestionFilterService } from '../../services/filtering/question-filter.service'; import { ReportService } from '../../services/report.service'; -import { concatMap, map } from "rxjs/operators"; +import { of } from "rxjs"; +import { concatMap, map, tap, catchError } from "rxjs/operators"; import { NCUAService } from "../../services/ncua.service"; import { NavTreeService } from "../../services/navigation/nav-tree.service"; import { LayoutService } from "../../services/layout.service"; @@ -335,48 +336,57 @@ export class MyAssessmentsComponent implements OnInit { } } + /** + * "Deletes" an assessment by removing the current user from it. The assessment + * is not deleted, but will no longer appear in the current user's list. + */ removeAssessment(assessment: UserAssessment, assessmentIndex: number) { - // first, call the API to see if this is a legal move - this.assessSvc - .isDeletePermitted() - .subscribe(canDelete => { - if (!canDelete) { - this.dialog.open(AlertComponent, { - data: { - messageText: - "You cannot remove an assessment that has other users." + // first, get a token branded for the target assessment + this.assessSvc.getAssessmentToken(assessment.assessmentId).then(() => { + + // next, call the API to see if this is a legal move + this.assessSvc + .isDeletePermitted() + .subscribe(canDelete => { + if (!canDelete) { + this.dialog.open(AlertComponent, { + data: { + messageText: + "You cannot remove an assessment that has other users." + } + }); + return; + } + + // if it's legal, see if they really want to + const dialogRef = this.dialog.open(ConfirmComponent); + dialogRef.componentInstance.confirmMessage = + this.tSvc.translate('dialogs.remove assessment', { assessmentName: assessment.assessmentName }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.assessSvc.removeMyContact(assessment.assessmentId).pipe( + tap(() => { + this.sortedAssessments.splice(assessmentIndex, 1); + }), + catchError(error => { + this.dialog.open(AlertComponent, { + data: { messageText: error.statusText } + }); + return of(null); + }) + ).subscribe(); } }); - return; - } - - // if it's legal, see if they really want to - const dialogRef = this.dialog.open(ConfirmComponent); - dialogRef.componentInstance.confirmMessage = - // "Are you sure you want to remove '" + - // assessment.assessmentName + - // "'?"; - this.tSvc.translate('dialogs.remove assessment', { assessmentName: assessment.assessmentName }); - dialogRef.afterClosed().subscribe(result => { - if (result) { - this.assessSvc.removeMyContact(assessment.assessmentId).subscribe( - x => { - this.sortedAssessments.splice(assessmentIndex, 1); - }, - x => { - this.dialog.open(AlertComponent, { - data: { messageText: x.statusText } - }); - }); - } }); - }); + }); } + /** + * + */ sortData(sort: Sort) { - if (!sort.active || sort.direction === "") { - // this.sortedAssessments = data; return; } @@ -401,10 +411,16 @@ export class MyAssessmentsComponent implements OnInit { }); } + /** + * + */ logout() { this.authSvc.logout(); } + /** + * + */ clickDownloadLink(ment_id: number, jsonOnly: boolean = false) { let encryption = this.preventEncrypt; // Only allow encryption on .csetw files and only allow PCII scrubbing on JSON files diff --git a/CSETWebNg/src/app/services/aggregation.service.ts b/CSETWebNg/src/app/services/aggregation.service.ts index d445f7d721..ada4def878 100644 --- a/CSETWebNg/src/app/services/aggregation.service.ts +++ b/CSETWebNg/src/app/services/aggregation.service.ts @@ -26,6 +26,7 @@ import { HttpClient } from '@angular/common/http'; import { ConfigService } from './config.service'; import { Router } from '@angular/router'; import { Aggregation } from '../models/aggregation.model'; +import { firstValueFrom } from 'rxjs'; @Injectable() export class AggregationService { @@ -110,10 +111,10 @@ export class AggregationService { * @param aggId */ getAggregationToken(aggId: number) { - return this.http - .get(this.configSvc.apiUrl + 'auth/token?aggregationId=' + aggId) - .toPromise() - .then((response: { token: string }) => { + const obs = this.http.get(this.configSvc.apiUrl + 'auth/token?aggregationId=' + aggId); + const prom = firstValueFrom(obs); + + return prom.then((response: { token: string }) => { localStorage.removeItem('userToken'); localStorage.setItem('userToken', response.token); if (aggId) { diff --git a/CSETWebNg/src/app/services/assess-compare-analytics.service.ts b/CSETWebNg/src/app/services/assess-compare-analytics.service.ts index d37f71583d..d67e103e94 100644 --- a/CSETWebNg/src/app/services/assess-compare-analytics.service.ts +++ b/CSETWebNg/src/app/services/assess-compare-analytics.service.ts @@ -28,7 +28,7 @@ import { AssessmentService } from './assessment.service'; import { AssessmentDetail } from '../models/assessment-info.model'; -import { Observable } from 'rxjs'; +import { firstValueFrom, Observable } from 'rxjs'; import { Router } from '@angular/router'; const headers = { headers: new HttpHeaders().set("Content-Type", "application/json"), @@ -69,10 +69,10 @@ export class AssessCompareAnalyticsService { } getAssessmentToken(assessId: number) { - return this.http - .get(this.configSvc.apiUrl + 'auth/token?assessmentId=' + assessId) - .toPromise() - .then((response: { token: string }) => { + const obs = this.http.get(this.configSvc.apiUrl + 'auth/token?assessmentId=' + assessId); + const prom = firstValueFrom(obs); + + return prom.then((response: { token: string }) => { localStorage.removeItem('userToken'); localStorage.setItem('userToken', response.token); if (assessId) { diff --git a/CSETWebNg/src/app/services/assessment.service.ts b/CSETWebNg/src/app/services/assessment.service.ts index 0cc91e7a6b..884b58b8b8 100644 --- a/CSETWebNg/src/app/services/assessment.service.ts +++ b/CSETWebNg/src/app/services/assessment.service.ts @@ -24,9 +24,9 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { - AssessmentContactsResponse, - AssessmentDetail, - MaturityModel + AssessmentContactsResponse, + AssessmentDetail, + MaturityModel } from '../models/assessment-info.model'; import { User } from '../models/user.model'; import { ConfigService } from './config.service'; @@ -34,7 +34,7 @@ import { Router } from '@angular/router'; import { DemographicExtendedService } from './demographic-extended.service'; import { CyberFloridaService } from './cyberflorida.service'; import { Answer } from '../models/questions.model'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, first, firstValueFrom, Observable } from 'rxjs'; import { TranslocoService } from '@jsverse/transloco'; import { ConversionService } from './conversion.service'; @@ -80,7 +80,7 @@ export class AssessmentService { */ public isBrandNew = false; - public assessmentCreator: any; + public assessmentCreator: any; /** * @@ -164,20 +164,20 @@ export class AssessmentService { * */ getAssessmentToken(assessId: number) { - return this.http - .get(this.apiUrl + 'auth/token?assessmentId=' + assessId) - .toPromise() - .then((response: { token: string }) => { - localStorage.removeItem('userToken'); - localStorage.setItem('userToken', response.token); - if (assessId) { - localStorage.removeItem('assessmentId'); - localStorage.setItem( - 'assessmentId', - assessId ? assessId.toString() : '' - ); - } - }); + const obs: Observable = this.http.get(this.apiUrl + 'auth/token?assessmentId=' + assessId); + const prom: Promise = firstValueFrom(obs); + + return prom.then((response: { token: string }) => { + localStorage.removeItem('userToken'); + localStorage.setItem('userToken', response.token); + if (assessId) { + localStorage.removeItem('assessmentId'); + localStorage.setItem( + 'assessmentId', + assessId ? assessId.toString() : '' + ); + } + }); } /** @@ -230,23 +230,23 @@ export class AssessmentService { * */ getAssessmentContacts() { - return this.http - .get(this.apiUrl + 'contacts') - .toPromise() - .then((response: AssessmentContactsResponse) => { - this.userRoleId = response.currentUserRole; - return response; - }); + const obs = this.http.get(this.apiUrl + 'contacts'); + const prom = firstValueFrom(obs); + + return prom.then((response: AssessmentContactsResponse) => { + this.userRoleId = response.currentUserRole; + return response; + }); } - getCreator(){ - return this.http - .get(this.apiUrl + 'assessmentcreator') - .toPromise() - .then((response: any) => { - this.assessmentCreator = response; - return response; - }); + getCreator() { + const obs = this.http.get(this.apiUrl + 'assessmentcreator'); + const prom = firstValueFrom(obs); + + return prom.then((response: any) => { + this.assessmentCreator = response; + return response; + }); } /** @@ -265,7 +265,7 @@ export class AssessmentService { var id10 = (ids[9] != undefined ? ids[9] : 0); headers.params = headers.params.set('id1', id1).set('id2', id2).set('id3', id3).set('id4', id4) - .set('id5', id5).set('id6', id6).set('id7', id7).set('id8', id8).set('id9', id9).set('id10', id10); + .set('id5', id5).set('id6', id6).set('id7', id7).set('id8', id8).set('id9', id9).set('id10', id10); return this.http.get(this.apiUrl + 'contactsById', headers); } @@ -308,22 +308,22 @@ export class AssessmentService { createContact(contact: User) { const body = this.configSvc.config.defaultInviteTemplate; return this.http.post(this.apiUrl + 'contacts/addnew', { - firstName: contact.firstName, - lastName: contact.lastName, - primaryEmail: contact.primaryEmail, - title: contact.title, - phone: contact.phone, - cellPhone: contact.cellPhone, - reportsTo: contact.reportsTo, - organizationName: contact.organizationName, - siteName: contact.siteName, - emergencyCommunicationsProtocol: contact.emergencyCommunicationsProtocol, - isSiteParticipant: contact.isSiteParticipant, - isPrimaryPoc: contact.isPrimaryPoc, - assessmentRoleId: contact.assessmentRoleId, - subject: this.configSvc.config.defaultInviteSubject, - body: body - }, + firstName: contact.firstName, + lastName: contact.lastName, + primaryEmail: contact.primaryEmail, + title: contact.title, + phone: contact.phone, + cellPhone: contact.cellPhone, + reportsTo: contact.reportsTo, + organizationName: contact.organizationName, + siteName: contact.siteName, + emergencyCommunicationsProtocol: contact.emergencyCommunicationsProtocol, + isSiteParticipant: contact.isSiteParticipant, + isPrimaryPoc: contact.isPrimaryPoc, + assessmentRoleId: contact.assessmentRoleId, + subject: this.configSvc.config.defaultInviteSubject, + body: body + }, headers ); } @@ -331,22 +331,22 @@ export class AssessmentService { createMergeContact(contact: User) { const body = this.configSvc.config.defaultInviteTemplate; return this.http.post(this.apiUrl + 'contacts/addnewmergecontact', { - firstName: contact.firstName, - lastName: contact.lastName, - primaryEmail: contact.primaryEmail, - title: contact.title, - phone: contact.phone, - cellPhone: contact.cellPhone, - reportsTo: contact.reportsTo, - organizationName: contact.organizationName, - siteName: contact.siteName, - emergencyCommunicationsProtocol: contact.emergencyCommunicationsProtocol, - isSiteParticipant: contact.isSiteParticipant, - isPrimaryPoc: contact.isPrimaryPoc, - assessmentRoleId: contact.assessmentRoleId, - subject: this.configSvc.config.defaultInviteSubject, - body: body - }, + firstName: contact.firstName, + lastName: contact.lastName, + primaryEmail: contact.primaryEmail, + title: contact.title, + phone: contact.phone, + cellPhone: contact.cellPhone, + reportsTo: contact.reportsTo, + organizationName: contact.organizationName, + siteName: contact.siteName, + emergencyCommunicationsProtocol: contact.emergencyCommunicationsProtocol, + isSiteParticipant: contact.isSiteParticipant, + isPrimaryPoc: contact.isPrimaryPoc, + assessmentRoleId: contact.assessmentRoleId, + subject: this.configSvc.config.defaultInviteSubject, + body: body + }, headers ); } @@ -390,7 +390,7 @@ export class AssessmentService { /** * Requests removing a user from an assessment. */ - removeContact(assessmentContactId: number) { + removeContact(assessmentContactId: number): Observable { return this.http.post( this.apiUrl + 'contacts/remove', { assessmentContactId: assessmentContactId }, @@ -441,21 +441,21 @@ export class AssessmentService { } return new Promise((resolve, reject) => { - this.createNewAssessmentFromGallery(workflow, galleryItem) - .toPromise() - .then( - (response: any) => { - // set the brand new flag - this.isBrandNew = true; - this.loadAssessment(response.id).then(() => { - resolve('assessment loaded'); - }); - }, - error => - console.log( - 'Unable to create new assessment: ' + (error).message - ) - ); + const obs = this.createNewAssessmentFromGallery(workflow, galleryItem); + const prom = firstValueFrom(obs); + prom.then( + (response: any) => { + // set the brand new flag + this.isBrandNew = true; + this.loadAssessment(response.id).then(() => { + resolve('assessment loaded'); + }); + }, + error => + console.log( + 'Unable to create new assessment: ' + (error).message + ) + ); }); } @@ -469,7 +469,7 @@ export class AssessmentService { }); } - refreshAssessment(){ + refreshAssessment() { this.getAssessmentDetail().subscribe((detail: AssessmentDetail) => { this.assessment = detail }) @@ -616,7 +616,7 @@ export class AssessmentService { if (modelName == '*' && !!this.assessment.maturityModel.modelName) { return true; } - + return this.assessment.maturityModel.modelName.toLowerCase() === modelName.toLowerCase(); } @@ -726,5 +726,5 @@ export class AssessmentService { } - + } diff --git a/CSETWebNg/src/app/services/authentication.service.ts b/CSETWebNg/src/app/services/authentication.service.ts index 22106b710a..6306f8d9e1 100644 --- a/CSETWebNg/src/app/services/authentication.service.ts +++ b/CSETWebNg/src/app/services/authentication.service.ts @@ -22,7 +22,7 @@ // //////////////////////////////// import { map } from 'rxjs/operators'; -import { timer, Observable } from 'rxjs'; +import { timer, Observable, firstValueFrom } from 'rxjs'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; @@ -96,18 +96,18 @@ export class AuthenticationService { * */ checkLocal() { - return this.http - .post( - this.configSvc.apiUrl + 'auth/login/standalone', - JSON.stringify({ - TzOffset: new Date().getTimezoneOffset().toString(), - // If InstallationMode isn't empty, use it. Otherwise, default to CSET. - Scope: this.configSvc.installationMode - }), - headers - ) - .toPromise() - .then( + const obs = this.http.post( + this.configSvc.apiUrl + 'auth/login/standalone', + JSON.stringify({ + TzOffset: new Date().getTimezoneOffset().toString(), + // If InstallationMode isn't empty, use it. Otherwise, default to CSET. + Scope: this.configSvc.installationMode + }), + headers + ); + const prom = firstValueFrom(obs); + + return prom.then( (response: LoginResponse) => { if (!response?.email) { @@ -392,10 +392,10 @@ export class AuthenticationService { * Checks and sets the current user's cisa assessor workflow option. */ configureCisaAssessorWorkflow(user) { - return this.configSvc - .getCisaAssessorWorkflow() - .toPromise() - .then((cisaAssessorWorkflowEnabled) => { + const obs = this.configSvc.getCisaAssessorWorkflow(); + const prom = firstValueFrom(obs); + + return prom.then((cisaAssessorWorkflowEnabled) => { if (cisaAssessorWorkflowEnabled) { return this.configSvc.enableCisaAssessorWorkflow().then(() => { return user; diff --git a/CSETWebNg/src/app/services/config.service.ts b/CSETWebNg/src/app/services/config.service.ts index 4044d5164c..c3a02045f0 100644 --- a/CSETWebNg/src/app/services/config.service.ts +++ b/CSETWebNg/src/app/services/config.service.ts @@ -24,8 +24,8 @@ import { HttpClient } from '@angular/common/http'; import { Injectable, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; -import { concat } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { concat, firstValueFrom } from 'rxjs'; +import { first, tap } from 'rxjs/operators'; import { merge } from 'lodash'; import { ModuleBehavior } from '../models/module-config.model'; @@ -114,10 +114,10 @@ export class ConfigService { if (!this.initialized) { this.isRunningInElectron = localStorage.getItem('isRunningInElectron') == 'true'; - return this.http - .get('assets/settings/config.json') - .toPromise() - .then((config) => { + const obs = this.http.get('assets/settings/config.json'); + const prom = firstValueFrom(obs); + + return prom.then((config) => { this.config = config; }) .then(() => { @@ -145,10 +145,10 @@ export class ConfigService { } enableCisaAssessorWorkflow() { - return this.http - .get('assets/settings/config.IOD.json') - .toPromise() - .then((iodConfig) => { + const obs = this.http.get('assets/settings/config.IOD.json') + const prom = firstValueFrom(obs); + + return prom.then((iodConfig) => { merge(this.config, iodConfig); this.setConfigPropertiesForLocalService(); });