From 1a341e63b9840e909e5f6dc010b312513f07d279 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Mon, 16 Dec 2024 17:17:33 +0530 Subject: [PATCH 01/12] feat: create provider filter dropdown --- .../portal/src/app/graphql/graphql.queries.ts | 23 +++++ .../notifications.component.html | 13 ++- .../notifications/notifications.component.ts | 96 +++++++++++++++++++ .../src/app/views/providers/provider.model.ts | 18 ++++ .../app/views/providers/providers.service.ts | 42 ++++++++ 5 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 apps/portal/src/app/views/providers/provider.model.ts create mode 100644 apps/portal/src/app/views/providers/providers.service.ts diff --git a/apps/portal/src/app/graphql/graphql.queries.ts b/apps/portal/src/app/graphql/graphql.queries.ts index 53f7935d..83545c7c 100644 --- a/apps/portal/src/app/graphql/graphql.queries.ts +++ b/apps/portal/src/app/graphql/graphql.queries.ts @@ -86,6 +86,29 @@ export const GetApplications = gql` } `; +export const GetProviders = gql` + query GetProviders($limit: Int!, $offset: Int!, $filters: [UniversalFilter!]) { + providers( + options: { + limit: $limit + offset: $offset + sortBy: "createdOn" + sortOrder: ASC + filters: $filters + } + ) { + providers { + channelType + createdOn + name + providerId + status + updatedOn + } + } + } +`; + export const LoginUser = gql` mutation LoginUser($username: String!, $password: String!) { login(loginUserInput: { username: $username, password: $password }) { diff --git a/apps/portal/src/app/views/notifications/notifications.component.html b/apps/portal/src/app/views/notifications/notifications.component.html index d62fbd79..96e916a9 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.html +++ b/apps/portal/src/app/views/notifications/notifications.component.html @@ -13,6 +13,14 @@

Notifications

[showClear]="true" (onChange)="loadNotificationsLazy({ first: 0, rows: this.pageSize })" > + Notifications placeholder="Select an Application" class="grid-dropdown" [showClear]="false" - (onChange)="loadNotificationsLazy({ first: 0, rows: this.pageSize })" + (onChange)=" + loadNotificationsLazy({ first: 0, rows: this.pageSize }); + getProvidersForSelectedApplication() + " *ngIf="applications.length !== 0" > diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index d3859929..70c4eace 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -7,6 +7,8 @@ import { NotificationsService } from './notifications.service'; import { Notification, NotificationResponse } from './notification.model'; import { ApplicationsService } from '../applications/applications.service'; import { ApplicationResponse } from '../applications/application.model'; +import { ProvidersService } from '../providers/providers.service'; +import { ProviderResponse } from '../providers/provider.model'; @Component({ selector: 'app-notifications', @@ -48,12 +50,16 @@ export class NotificationsComponent implements OnInit { applications = []; + providers = []; + selectedChannelType = null; selectedDeliveryStatus = null; selectedApplication = null; + selectedProvider = null; + selectedFromDate = null; selectedToDate = null; @@ -85,6 +91,7 @@ export class NotificationsComponent implements OnInit { constructor( private notificationService: NotificationsService, private applicationService: ApplicationsService, + private providerService: ProvidersService, private messageService: MessageService, private authService: AuthService, ) {} @@ -177,6 +184,9 @@ export class NotificationsComponent implements OnInit { // Now that applications are loaded, load notifications this.loadNotificationsLazy({ first: 0, rows: this.pageSize }); + + // Load providers for selected application + this.getProvidersForSelectedApplication(); }); } @@ -225,6 +235,84 @@ export class NotificationsComponent implements OnInit { } } + getProvidersForSelectedApplication() { + // Set the query variables + const variables = { + filters: [], + offset: 0, + limit: 15, + }; + + // Set query filters + variables.filters.push({ + field: 'applicationId', + operator: 'eq', + value: this.selectedApplication.toString(), + }); + + // Fetch the login token + const loginToken = this.getJWTLoginToken(); + + if (!loginToken) { + // Handle missing token + return; + } + + // Fetch providers and handle errors + this.providerService + .getProviders(variables, loginToken) + .pipe( + // catchError operator to handle errors + catchError((error) => { + this.messageService.add({ + key: 'tst', + severity: 'error', + summary: 'Error', + detail: `There was an error while loading providers. Reason: ${error.message}`, + }); + return of(null); // Return null to indicate error + }), + ) + .subscribe((providerResponse: ProviderResponse | null) => { + if (providerResponse?.errors?.length) { + const unauthorizedError = providerResponse.errors.find( + (error) => error.message === 'Unauthorized', + ); + + this.providers = []; + + if (unauthorizedError) { + this.messageService.add({ + key: 'tst', + severity: 'error', + summary: 'Error', + detail: 'Unauthorized access. Please log in again.', + }); + this.authService.logoutUser(); + } else { + providerResponse.errors.forEach((error) => { + this.messageService.add({ + key: 'tst', + severity: 'error', + summary: 'Error', + detail: `GraphQL Error - Get Providers: ${error.message}`, + }); + }); + } + } else if (providerResponse?.providers?.length) { + // Fetch list of providers for dropdown + this.providers = providerResponse.providers.map((obj) => ({ + // Name to display and ID to return upon selection + label: `${obj.name} - ${this.channelTypeMap[obj.channelType].altText}`, + value: obj.providerId, + })); + } else { + this.providers = []; + this.selectedProvider = null; + } + }); + } + loadNotificationsLazy(event: LazyLoadEvent) { this.loading = true; @@ -288,6 +376,14 @@ export class NotificationsComponent implements OnInit { }); } + if (this.selectedProvider) { + variables.filters.push({ + field: 'providerId', + operator: 'eq', + value: this.selectedProvider.toString(), + }); + } + if (this.selectedFromDate) { variables.filters.push({ field: 'createdOn', diff --git a/apps/portal/src/app/views/providers/provider.model.ts b/apps/portal/src/app/views/providers/provider.model.ts new file mode 100644 index 00000000..41f860cd --- /dev/null +++ b/apps/portal/src/app/views/providers/provider.model.ts @@ -0,0 +1,18 @@ +import { GraphQLFormattedError } from 'graphql/error/GraphQLError'; + +export interface Provider { + providerId: number; + name: string; + channelType: number; + createdOn: Date; + updatedOn: Date; + status: number; +} + +export interface ProviderResponse { + providers: Provider[]; + total: number; + offset: number; + limit: number; + errors?: ReadonlyArray; +} diff --git a/apps/portal/src/app/views/providers/providers.service.ts b/apps/portal/src/app/views/providers/providers.service.ts new file mode 100644 index 00000000..e0b3d227 --- /dev/null +++ b/apps/portal/src/app/views/providers/providers.service.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@angular/core'; +import { Observable, catchError, map } from 'rxjs'; +import { GraphqlService } from 'src/app/graphql/graphql.service'; +import { GetProviders } from 'src/app/graphql/graphql.queries'; +import { ApolloQueryResult } from '@apollo/client/core'; +import { Provider, ProviderResponse } from './provider.model'; + +interface GetProvidersResponse { + providers: { + providers?: Provider[]; + total?: number; + offset?: number; + limit?: number; + }; +} +@Injectable({ + providedIn: 'root', +}) +export class ProvidersService { + constructor(private graphqlService: GraphqlService) {} + + getProviders(variables, inputToken): Observable { + return this.graphqlService.query(GetProviders, variables, inputToken).pipe( + map((response: ApolloQueryResult) => { + const providerArray = response.data?.providers.providers; + + const providerResponseObject: ProviderResponse = { + providers: providerArray, + total: response.data?.providers.total, + offset: response.data?.providers.offset, + limit: response.data?.providers.limit, + errors: response.errors || null, + }; + return providerResponseObject; + }), + catchError((error) => { + const errorMessage: string = error.message; + throw new Error(errorMessage); + }), + ); + } +} From a1dfd5c58bfbc1921ee35a00fc0003a7392d466e Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Mon, 16 Dec 2024 17:28:44 +0530 Subject: [PATCH 02/12] feat: remove channeltype filter --- .../notifications.component.html | 8 ----- .../notifications/notifications.component.ts | 30 ------------------- 2 files changed, 38 deletions(-) diff --git a/apps/portal/src/app/views/notifications/notifications.component.html b/apps/portal/src/app/views/notifications/notifications.component.html index 96e916a9..0a404aca 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.html +++ b/apps/portal/src/app/views/notifications/notifications.component.html @@ -5,14 +5,6 @@

Notifications

- ({ - label: `${this.channelTypeMap[value].altText} - ${this.channelTypeMap[value].providerName}`, - value, - })); - deliveryStatuses = Object.entries(DeliveryStatus).map(([, value]) => ({ label: this.deliveryStatusMap[value].value, value, @@ -52,8 +47,6 @@ export class NotificationsComponent implements OnInit { providers = []; - selectedChannelType = null; - selectedDeliveryStatus = null; selectedApplication = null; @@ -345,29 +338,6 @@ export class NotificationsComponent implements OnInit { value: this.selectedApplication.toString(), }); - if (this.selectedChannelType) { - if (this.selectedChannelType === this.allPortalChannelTypes.UNKNOWN) { - // Condition to filter all notifications with unknown channel type - const existingChannelTypes = Object.keys(this.channelTypeMap).filter( - (value) => value !== this.allPortalChannelTypes.UNKNOWN.toString(), - ); - existingChannelTypes.forEach((key) => { - variables.filters.push({ - field: 'channelType', - operator: 'ne', - value: key.toString(), - }); - }); - } else { - // Default behavior when we are sorting on known channelType - variables.filters.push({ - field: 'channelType', - operator: 'eq', - value: this.selectedChannelType.toString(), - }); - } - } - if (this.selectedDeliveryStatus) { variables.filters.push({ field: 'deliveryStatus', From bfb40f6e0f2cb02757d9e1e44171eb7d57d8fe9b Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Mon, 16 Dec 2024 17:39:08 +0530 Subject: [PATCH 03/12] fix: add pagination values to query --- apps/portal/src/app/graphql/graphql.queries.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/portal/src/app/graphql/graphql.queries.ts b/apps/portal/src/app/graphql/graphql.queries.ts index 83545c7c..d25338a1 100644 --- a/apps/portal/src/app/graphql/graphql.queries.ts +++ b/apps/portal/src/app/graphql/graphql.queries.ts @@ -105,6 +105,9 @@ export const GetProviders = gql` status updatedOn } + total + offset + limit } } `; From e4e999310f461bc291f7e10de4efc0ecaaec62a8 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Tue, 17 Dec 2024 20:54:34 +0530 Subject: [PATCH 04/12] fix: add applicationId in graphql query --- apps/portal/src/app/graphql/graphql.queries.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/portal/src/app/graphql/graphql.queries.ts b/apps/portal/src/app/graphql/graphql.queries.ts index d25338a1..2f77d06c 100644 --- a/apps/portal/src/app/graphql/graphql.queries.ts +++ b/apps/portal/src/app/graphql/graphql.queries.ts @@ -98,6 +98,7 @@ export const GetProviders = gql` } ) { providers { + applicationId channelType createdOn name From 220abdb054619fb384e2dbaea3443218f47a79a3 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Wed, 18 Dec 2024 11:32:39 +0530 Subject: [PATCH 05/12] fix: add null check for selectedApplication --- .../src/app/views/notifications/notifications.component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index 752f7fb9..017aafce 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -236,6 +236,11 @@ export class NotificationsComponent implements OnInit { limit: 15, }; + if (!this.selectedApplication) { + // Handle missing selected application + return; + } + // Set query filters variables.filters.push({ field: 'applicationId', From ff6011dbff2aaa3380beebe29ac7cb25d16933a8 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Wed, 18 Dec 2024 21:23:39 +0530 Subject: [PATCH 06/12] feat: get provider, notifications together on application change --- .../portal/src/app/graphql/graphql.queries.ts | 109 ++++++++++- .../notifications.component.html | 3 +- .../notifications/notifications.component.ts | 169 +++++++++++------- .../src/app/views/providers/provider.model.ts | 13 +- .../app/views/providers/providers.service.ts | 100 +++++++++-- 5 files changed, 301 insertions(+), 93 deletions(-) diff --git a/apps/portal/src/app/graphql/graphql.queries.ts b/apps/portal/src/app/graphql/graphql.queries.ts index 2f77d06c..60b1f468 100644 --- a/apps/portal/src/app/graphql/graphql.queries.ts +++ b/apps/portal/src/app/graphql/graphql.queries.ts @@ -86,15 +86,30 @@ export const GetApplications = gql` } `; -export const GetProviders = gql` - query GetProviders($limit: Int!, $offset: Int!, $filters: [UniversalFilter!]) { +export const LoginUser = gql` + mutation LoginUser($username: String!, $password: String!) { + login(loginUserInput: { username: $username, password: $password }) { + token + } + } +`; + +export const GetProvidersAndNotifications = gql` + query GetProvidersAndNotifications( + $providerLimit: Int! + $providerOffset: Int! + $providerFilters: [UniversalFilter!] + $notificationLimit: Int! + $notificationOffset: Int! + $notificationFilters: [UniversalFilter!] + ) { providers( options: { - limit: $limit - offset: $offset + limit: $providerLimit + offset: $providerOffset sortBy: "createdOn" sortOrder: ASC - filters: $filters + filters: $providerFilters } ) { providers { @@ -110,13 +125,89 @@ export const GetProviders = gql` offset limit } + notifications( + options: { + limit: $notificationLimit + offset: $notificationOffset + sortBy: "createdOn" + sortOrder: DESC + filters: $notificationFilters + } + ) { + notifications { + channelType + createdBy + createdOn + data + deliveryStatus + id + result + status + updatedBy + updatedOn + } + total + offset + limit + } } `; -export const LoginUser = gql` - mutation LoginUser($username: String!, $password: String!) { - login(loginUserInput: { username: $username, password: $password }) { - token +export const GetProvidersAndArchivedNotifications = gql` + query GetProvidersAndArchivedNotifications( + $providerLimit: Int! + $providerOffset: Int! + $providerFilters: [UniversalFilter!] + $notificationLimit: Int! + $notificationOffset: Int! + $notificationFilters: [UniversalFilter!] + ) { + providers( + options: { + limit: $providerLimit + offset: $providerOffset + sortBy: "createdOn" + sortOrder: ASC + filters: $providerFilters + } + ) { + providers { + applicationId + channelType + createdOn + name + providerId + status + updatedOn + } + total + offset + limit + } + archivedNotifications( + options: { + limit: $notificationLimit + offset: $notificationOffset + sortBy: "createdOn" + sortOrder: DESC + filters: $notificationFilters + } + ) { + archivedNotifications { + channelType + createdBy + createdOn + data + deliveryStatus + notificationId + result + status + updatedBy + updatedOn + } + total + offset + limit } } `; diff --git a/apps/portal/src/app/views/notifications/notifications.component.html b/apps/portal/src/app/views/notifications/notifications.component.html index 0a404aca..5d21056a 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.html +++ b/apps/portal/src/app/views/notifications/notifications.component.html @@ -53,8 +53,7 @@

Notifications

class="grid-dropdown" [showClear]="false" (onChange)=" - loadNotificationsLazy({ first: 0, rows: this.pageSize }); - getProvidersForSelectedApplication() + loadProvidersAndNotificationsForSelectedApplication({ first: 0, rows: this.pageSize }) " *ngIf="applications.length !== 0" >
diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index 167df94d..fe85c925 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -8,7 +8,7 @@ import { Notification, NotificationResponse } from './notification.model'; import { ApplicationsService } from '../applications/applications.service'; import { ApplicationResponse } from '../applications/application.model'; import { ProvidersService } from '../providers/providers.service'; -import { ProviderResponse } from '../providers/provider.model'; +import { ProviderAndNotificationResponse } from '../providers/provider.model'; @Component({ selector: 'app-notifications', @@ -90,7 +90,7 @@ export class NotificationsComponent implements OnInit { ) {} ngOnInit(): void { - this.getApplications(); + this.getApplicationsAndInitializeData(); } toggleArchive() { @@ -99,7 +99,7 @@ export class NotificationsComponent implements OnInit { this.loadNotificationsLazy({ first: 0, rows: this.pageSize }); } - getApplications() { + getApplicationsAndInitializeData() { // Set the query variables const variables = { filters: [], @@ -175,11 +175,8 @@ export class NotificationsComponent implements OnInit { this.selectedApplication = null; } - // Now that applications are loaded, load notifications - this.loadNotificationsLazy({ first: 0, rows: this.pageSize }); - - // Load providers for selected application - this.getProvidersForSelectedApplication(); + // Now that applications are loaded, load providers and notifications + this.loadProvidersAndNotificationsForSelectedApplication({ first: 0, rows: this.pageSize }); }); } @@ -228,12 +225,18 @@ export class NotificationsComponent implements OnInit { } } - getProvidersForSelectedApplication() { + loadProvidersAndNotificationsForSelectedApplication(event: LazyLoadEvent) { + this.loading = true; + this.selectedProvider = null; + // Set the query variables const variables = { - filters: [], - offset: 0, - limit: 15, + providerFilters: [], + providerOffset: 0, + providerLimit: 20, + notificationFilters: [], + notificationOffset: event.first, + notificationLimit: event.rows, }; if (!this.selectedApplication) { @@ -242,11 +245,18 @@ export class NotificationsComponent implements OnInit { } // Set query filters - variables.filters.push({ + const applicationIdFilterObject = { field: 'applicationId', operator: 'eq', value: this.selectedApplication.toString(), - }); + }; + + variables.providerFilters.push(applicationIdFilterObject); + variables.notificationFilters.push(applicationIdFilterObject); + + // Add rest of the filters + const combinedNotificationFilters = this.getCombinedNotificationFilters(); + variables.notificationFilters.push(...combinedNotificationFilters); // Fetch the login token const loginToken = this.getJWTLoginToken(); @@ -256,9 +266,15 @@ export class NotificationsComponent implements OnInit { return; } - // Fetch providers and handle errors + // Set page size + this.pageSize = event.rows; + + // Set current page + this.currentPage = Math.floor(event.first / event.rows) + 1; + + // Fetch providers & notifications together. Handle errors this.providerService - .getProviders(variables, loginToken) + .getProvidersAndNotifications(variables, loginToken, this.archivedNotificationToggle) .pipe( // catchError operator to handle errors catchError((error) => { @@ -266,14 +282,14 @@ export class NotificationsComponent implements OnInit { key: 'tst', severity: 'error', summary: 'Error', - detail: `There was an error while loading providers. Reason: ${error.message}`, + detail: `Error while loading providers and notifications: ${error.message}`, }); return of(null); // Return null to indicate error }), ) - .subscribe((providerResponse: ProviderResponse | null) => { - if (providerResponse?.errors?.length) { - const unauthorizedError = providerResponse.errors.find( + .subscribe((providerAndNotificationResponse: ProviderAndNotificationResponse | null) => { + if (providerAndNotificationResponse?.errors?.length) { + const unauthorizedError = providerAndNotificationResponse.errors.find( (error) => error.message === 'Unauthorized', ); @@ -288,26 +304,35 @@ export class NotificationsComponent implements OnInit { }); this.authService.logoutUser(); } else { - providerResponse.errors.forEach((error) => { + providerAndNotificationResponse.errors.forEach((error) => { this.messageService.add({ key: 'tst', severity: 'error', summary: 'Error', - detail: `GraphQL Error - Get Providers: ${error.message}`, + detail: `GraphQL Error - Get Providers and Notifications: ${error.message}`, }); }); } - } else if (providerResponse?.providers?.length) { + } else if ( + providerAndNotificationResponse?.providers?.length && + providerAndNotificationResponse?.notifications?.length + ) { // Fetch list of providers for dropdown - this.providers = providerResponse.providers.map((obj) => ({ + this.providers = providerAndNotificationResponse.providers.map((obj) => ({ // Name to display and ID to return upon selection label: `${obj.name} - ${this.channelTypeMap[obj.channelType].altText}`, value: obj.providerId, })); + this.notifications = providerAndNotificationResponse.notifications; + this.totalRecords = providerAndNotificationResponse.notificationTotal; } else { this.providers = []; this.selectedProvider = null; + this.notifications = []; + this.totalRecords = 0; } + + this.loading = false; }); } @@ -343,49 +368,9 @@ export class NotificationsComponent implements OnInit { value: this.selectedApplication.toString(), }); - if (this.selectedDeliveryStatus) { - variables.filters.push({ - field: 'deliveryStatus', - operator: 'eq', - value: this.selectedDeliveryStatus.toString(), - }); - } - - if (this.selectedProvider) { - variables.filters.push({ - field: 'providerId', - operator: 'eq', - value: this.selectedProvider.toString(), - }); - } - - if (this.selectedFromDate) { - variables.filters.push({ - field: 'createdOn', - operator: 'gt', - value: new Date( - new Date(this.selectedFromDate).setDate(this.selectedFromDate.getDate()), - ).toISOString(), - }); - } - - if (this.selectedToDate) { - variables.filters.push({ - field: 'createdOn', - operator: 'lt', - value: new Date( - new Date(this.selectedToDate).setDate(this.selectedToDate.getDate() + 1), - ).toISOString(), - }); - } - - if (this.searchValue) { - variables.filters.push({ - field: 'data', - operator: 'contains', - value: this.searchValue, - }); - } + // Add rest of the filters + const combinedNotificationFilters = this.getCombinedNotificationFilters(); + variables.filters.push(...combinedNotificationFilters); // Set page size this.pageSize = event.rows; @@ -455,6 +440,56 @@ export class NotificationsComponent implements OnInit { } } + getCombinedNotificationFilters() { + const combinedNotificationFilters = []; + + if (this.selectedDeliveryStatus) { + combinedNotificationFilters.push({ + field: 'deliveryStatus', + operator: 'eq', + value: this.selectedDeliveryStatus.toString(), + }); + } + + if (this.selectedProvider) { + combinedNotificationFilters.push({ + field: 'providerId', + operator: 'eq', + value: this.selectedProvider.toString(), + }); + } + + if (this.selectedFromDate) { + combinedNotificationFilters.push({ + field: 'createdOn', + operator: 'gt', + value: new Date( + new Date(this.selectedFromDate).setDate(this.selectedFromDate.getDate()), + ).toISOString(), + }); + } + + if (this.selectedToDate) { + combinedNotificationFilters.push({ + field: 'createdOn', + operator: 'lt', + value: new Date( + new Date(this.selectedToDate).setDate(this.selectedToDate.getDate() + 1), + ).toISOString(), + }); + } + + if (this.searchValue) { + combinedNotificationFilters.push({ + field: 'data', + operator: 'contains', + value: this.searchValue, + }); + } + + return combinedNotificationFilters; + } + showJsonObject(json: Record): void { this.jsonDialogData = json; this.jsonDialogVisible = true; diff --git a/apps/portal/src/app/views/providers/provider.model.ts b/apps/portal/src/app/views/providers/provider.model.ts index 41f860cd..b3b0a121 100644 --- a/apps/portal/src/app/views/providers/provider.model.ts +++ b/apps/portal/src/app/views/providers/provider.model.ts @@ -1,4 +1,5 @@ import { GraphQLFormattedError } from 'graphql/error/GraphQLError'; +import { Notification } from '../notifications/notification.model'; export interface Provider { providerId: number; @@ -9,10 +10,14 @@ export interface Provider { status: number; } -export interface ProviderResponse { +export interface ProviderAndNotificationResponse { providers: Provider[]; - total: number; - offset: number; - limit: number; + providerTotal: number; + providerOffset: number; + providerLimit: number; + notifications: Notification[]; + notificationTotal: number; + notificationOffset: number; + notificationLimit: number; errors?: ReadonlyArray; } diff --git a/apps/portal/src/app/views/providers/providers.service.ts b/apps/portal/src/app/views/providers/providers.service.ts index e0b3d227..376a43f3 100644 --- a/apps/portal/src/app/views/providers/providers.service.ts +++ b/apps/portal/src/app/views/providers/providers.service.ts @@ -1,37 +1,115 @@ import { Injectable } from '@angular/core'; import { Observable, catchError, map } from 'rxjs'; import { GraphqlService } from 'src/app/graphql/graphql.service'; -import { GetProviders } from 'src/app/graphql/graphql.queries'; import { ApolloQueryResult } from '@apollo/client/core'; -import { Provider, ProviderResponse } from './provider.model'; +import { + GetProvidersAndArchivedNotifications, + GetProvidersAndNotifications, +} from 'src/app/graphql/graphql.queries'; +import { Provider, ProviderAndNotificationResponse } from './provider.model'; +import { ArchivedNotification, Notification } from '../notifications/notification.model'; -interface GetProvidersResponse { +interface GetProviderNotificationResponse { providers: { providers?: Provider[]; total?: number; offset?: number; limit?: number; }; + notifications: { + notifications?: Notification[]; + total?: number; + offset?: number; + limit?: number; + }; +} + +interface GetProviderArchivedNotificationResponse { + providers: { + providers?: Provider[]; + total?: number; + offset?: number; + limit?: number; + }; + archivedNotifications: { + archivedNotifications?: ArchivedNotification[]; + total?: number; + offset?: number; + limit?: number; + }; } + @Injectable({ providedIn: 'root', }) export class ProvidersService { constructor(private graphqlService: GraphqlService) {} - getProviders(variables, inputToken): Observable { - return this.graphqlService.query(GetProviders, variables, inputToken).pipe( - map((response: ApolloQueryResult) => { + getProvidersAndNotifications( + variables, + inputToken, + archivedNotificationToggle, + ): Observable { + if (archivedNotificationToggle) { + return this.graphqlService + .query(GetProvidersAndArchivedNotifications, variables, inputToken) + .pipe( + map((response: ApolloQueryResult) => { + const providerArray = response.data?.providers.providers; + const archivedNotificationArray = + response.data?.archivedNotifications.archivedNotifications; + + const convertToNotificationArray = + archivedNotificationArray?.map((item) => ({ + id: item.notificationId, + channelType: item.channelType, + data: item.data, + deliveryStatus: item.deliveryStatus, + result: item.result, + createdOn: item.createdOn, + updatedOn: item.updatedOn, + createdBy: item.createdBy, + updatedBy: item.updatedBy, + status: item.status, + })) ?? []; + + const providerArchivedResponseObject: ProviderAndNotificationResponse = { + providers: providerArray, + providerTotal: response.data?.providers.total, + providerOffset: response.data?.providers.offset, + providerLimit: response.data?.providers.limit, + notifications: convertToNotificationArray, + notificationTotal: response.data?.archivedNotifications.total, + notificationOffset: response.data?.archivedNotifications.offset, + notificationLimit: response.data?.archivedNotifications.limit, + errors: response.errors || null, + }; + return providerArchivedResponseObject; + }), + catchError((error) => { + const errorMessage: string = error.message; + throw new Error(errorMessage); + }), + ); + } + + return this.graphqlService.query(GetProvidersAndNotifications, variables, inputToken).pipe( + map((response: ApolloQueryResult) => { const providerArray = response.data?.providers.providers; + const notificationArray = response.data?.notifications.notifications; - const providerResponseObject: ProviderResponse = { + const providerNotificationResponseObject: ProviderAndNotificationResponse = { providers: providerArray, - total: response.data?.providers.total, - offset: response.data?.providers.offset, - limit: response.data?.providers.limit, + providerTotal: response.data?.providers.total, + providerOffset: response.data?.providers.offset, + providerLimit: response.data?.providers.limit, + notifications: notificationArray, + notificationTotal: response.data?.notifications.total, + notificationOffset: response.data?.notifications.offset, + notificationLimit: response.data?.notifications.limit, errors: response.errors || null, }; - return providerResponseObject; + return providerNotificationResponseObject; }), catchError((error) => { const errorMessage: string = error.message; From 9142f8884f004fbe064d2b44135a58365b102aeb Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Thu, 16 Jan 2025 12:29:08 +0530 Subject: [PATCH 07/12] feat: trigger logout on jwt token error --- .../src/app/views/notifications/notifications.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index fe85c925..a1c2d652 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -111,7 +111,7 @@ export class NotificationsComponent implements OnInit { const loginToken = this.getJWTLoginToken(); if (!loginToken) { - // Handle missing token + this.authService.logoutUser(); return; } @@ -262,7 +262,7 @@ export class NotificationsComponent implements OnInit { const loginToken = this.getJWTLoginToken(); if (!loginToken) { - // Handle missing token + this.authService.logoutUser(); return; } @@ -344,6 +344,7 @@ export class NotificationsComponent implements OnInit { if (!loginToken) { this.loading = false; + this.authService.logoutUser(); return; } From 1c14495549220073de170e90c19d0f63211f425d Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Fri, 17 Jan 2025 11:52:37 +0530 Subject: [PATCH 08/12] feat: add type definitions for method parameters --- .../src/app/views/applications/applications.service.ts | 2 +- .../src/app/views/notifications/notifications.service.ts | 2 +- apps/portal/src/app/views/providers/providers.service.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/portal/src/app/views/applications/applications.service.ts b/apps/portal/src/app/views/applications/applications.service.ts index acc15b21..26835869 100644 --- a/apps/portal/src/app/views/applications/applications.service.ts +++ b/apps/portal/src/app/views/applications/applications.service.ts @@ -19,7 +19,7 @@ interface GetApplicationsResponse { export class ApplicationsService { constructor(private graphqlService: GraphqlService) {} - getApplications(variables, inputToken): Observable { + getApplications(variables: unknown, inputToken: string): Observable { return this.graphqlService.query(GetApplications, variables, inputToken).pipe( map((response: ApolloQueryResult) => { const applicationArray = response.data?.applications.applications; diff --git a/apps/portal/src/app/views/notifications/notifications.service.ts b/apps/portal/src/app/views/notifications/notifications.service.ts index 3110ae55..608b68ca 100644 --- a/apps/portal/src/app/views/notifications/notifications.service.ts +++ b/apps/portal/src/app/views/notifications/notifications.service.ts @@ -29,7 +29,7 @@ interface GetArchivedNotificationsResponse { export class NotificationsService { constructor(private graphqlService: GraphqlService) {} - getNotifications(variables, inputToken): Observable { + getNotifications(variables: unknown, inputToken: string): Observable { return this.graphqlService.query(GetNotifications, variables, inputToken).pipe( map((response: ApolloQueryResult) => { const notificationArray = response.data?.notifications.notifications; diff --git a/apps/portal/src/app/views/providers/providers.service.ts b/apps/portal/src/app/views/providers/providers.service.ts index 376a43f3..fdefc531 100644 --- a/apps/portal/src/app/views/providers/providers.service.ts +++ b/apps/portal/src/app/views/providers/providers.service.ts @@ -46,9 +46,9 @@ export class ProvidersService { constructor(private graphqlService: GraphqlService) {} getProvidersAndNotifications( - variables, - inputToken, - archivedNotificationToggle, + variables: unknown, + inputToken: string, + archivedNotificationToggle: boolean, ): Observable { if (archivedNotificationToggle) { return this.graphqlService From 3fe2a0911f6727b1a448be7ed834656d6ee29694 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Fri, 17 Jan 2025 12:30:23 +0530 Subject: [PATCH 09/12] fix: separate provider and notification handling --- .../notifications/notifications.component.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index fe85c925..d42f98e6 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -313,18 +313,22 @@ export class NotificationsComponent implements OnInit { }); }); } - } else if ( - providerAndNotificationResponse?.providers?.length && - providerAndNotificationResponse?.notifications?.length - ) { + } else if (providerAndNotificationResponse?.providers?.length) { // Fetch list of providers for dropdown this.providers = providerAndNotificationResponse.providers.map((obj) => ({ // Name to display and ID to return upon selection label: `${obj.name} - ${this.channelTypeMap[obj.channelType].altText}`, value: obj.providerId, })); - this.notifications = providerAndNotificationResponse.notifications; - this.totalRecords = providerAndNotificationResponse.notificationTotal; + + // Set notifications + if (providerAndNotificationResponse?.notifications?.length) { + this.notifications = providerAndNotificationResponse.notifications; + this.totalRecords = providerAndNotificationResponse.notificationTotal; + } else { + this.notifications = []; + this.totalRecords = 0; + } } else { this.providers = []; this.selectedProvider = null; From 3f6cb740d109e6164fe9ef2efc24dcc0c0d9672f Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Fri, 17 Jan 2025 12:46:03 +0530 Subject: [PATCH 10/12] feat: handle case when no application is present --- .../app/views/notifications/notifications.component.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index d42f98e6..d4aa851e 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -173,6 +173,9 @@ export class NotificationsComponent implements OnInit { } else { this.applications = []; this.selectedApplication = null; + this.providers = []; + this.notifications = []; + this.totalRecords = 0; } // Now that applications are loaded, load providers and notifications @@ -241,6 +244,13 @@ export class NotificationsComponent implements OnInit { if (!this.selectedApplication) { // Handle missing selected application + this.messageService.add({ + key: 'tst', + severity: 'error', + summary: 'Error', + detail: 'Application is missing for loading providers', + }); + this.loading = false; return; } From 4fd8b8ff412c14a6cf8bb85b304dbab3a6ffbf94 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Fri, 17 Jan 2025 13:15:43 +0530 Subject: [PATCH 11/12] feat: add null check for channelType mapping --- .../src/app/views/notifications/notifications.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index d4aa851e..23753ca4 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -327,7 +327,9 @@ export class NotificationsComponent implements OnInit { // Fetch list of providers for dropdown this.providers = providerAndNotificationResponse.providers.map((obj) => ({ // Name to display and ID to return upon selection - label: `${obj.name} - ${this.channelTypeMap[obj.channelType].altText}`, + label: this.channelTypeMap[obj.channelType]?.altText + ? `${obj.name} - ${this.channelTypeMap[obj.channelType].altText}` + : `${obj.name} - ${this.channelTypeMap[ChannelType.UNKNOWN].altText}`, value: obj.providerId, })); From 8389ddf892f6c66ddebd3c607d1ba0a37e51e241 Mon Sep 17 00:00:00 2001 From: kshitij-k-osmosys Date: Fri, 17 Jan 2025 13:20:10 +0530 Subject: [PATCH 12/12] fix: revert changes for application else block --- .../src/app/views/notifications/notifications.component.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/portal/src/app/views/notifications/notifications.component.ts b/apps/portal/src/app/views/notifications/notifications.component.ts index 23753ca4..7ff44fc4 100644 --- a/apps/portal/src/app/views/notifications/notifications.component.ts +++ b/apps/portal/src/app/views/notifications/notifications.component.ts @@ -173,9 +173,6 @@ export class NotificationsComponent implements OnInit { } else { this.applications = []; this.selectedApplication = null; - this.providers = []; - this.notifications = []; - this.totalRecords = 0; } // Now that applications are loaded, load providers and notifications