import {Action, Selector, State, StateContext} from '@ngxs/store'; import {Injectable} from '@angular/core'; import {ConfigService} from '../services/config.service'; import {catchError, map, tap} from 'rxjs/operators'; import {forkJoin, Observable, of} from 'rxjs'; import {Service, Status} from '../interfaces/services.interface'; import {CheckAllServicesStatus, CheckSpecifiedServiceStatus, LoadConfig} from './service-status.action'; import {CheckServiceStatusService} from '../check-service-status.service'; export interface ServiceStatusModel { config: any | null; loading: boolean; error: any | null; services: Service[]; } @State({ name: 'serviceStatus', defaults: { config: null, loading: false, error: null, services: [] } }) @Injectable({providedIn: 'root'}) export class ServiceStatusState { constructor(private configService: ConfigService, private checkServiceStatus: CheckServiceStatusService) { } @Selector() static config(state: ServiceStatusModel) { return state.config; } @Selector() static loading(state: ServiceStatusModel) { return state.loading; } @Selector() static error(state: ServiceStatusModel) { return state.error; } @Selector() static services(state: ServiceStatusModel): Service[] { return state.services; } @Action(LoadConfig) loadConfig(ctx: StateContext): Observable { ctx.patchState({loading: true, error: null}); return this.configService.getConfig().pipe( tap((data) => { ctx.patchState({config: data, loading: false}); }), catchError((err) => { ctx.patchState({error: err, loading: false}); return of(null); }) ); } @Action(CheckAllServicesStatus) checkServicesStatus(ctx: StateContext) { const config = ctx.getState().config; if (!config || !Array.isArray(config)) { ctx.patchState({ services: [], loading: false }); return; } const currentServices = ctx.getState().services; const initialServices = config.map((service: Service) => { const existing = currentServices.find(s => s.name === service.name); return existing ? { ...existing, status: 'loading' } : { ...service, status: 'loading' }; }); ctx.patchState({ services: initialServices, loading: true }); const statusObservables = config.map((service: Service) => this.checkServiceStatus.checkStatus(service.name, service.url, service.headers).pipe( map((status: Status) => ({ ...service, status, lastChecked: Date.now() })) ) ); return forkJoin(statusObservables).pipe( tap((services: Service[]) => { ctx.patchState({ services, loading: false }); }) ); } @Action(CheckSpecifiedServiceStatus) checkSpecifiedServiceStatus(ctx: StateContext, action: CheckSpecifiedServiceStatus) { const config = ctx.getState().config; if (!config || !Array.isArray(config)) return; const service = config.find((s: Service) => s.name === action.serviceName); if (!service) return; const services = ctx.getState().services.slice(); const index = services.findIndex(s => s.name === action.serviceName); if (index !== -1) { services[index] = { ...services[index], status: 'loading' }; ctx.patchState({ services }); } this.checkServiceStatus.checkStatus(service.name, service.url, service.headers).subscribe((status: Status) => { const updatedServices = ctx.getState().services.map(s => s.name === action.serviceName ? { ...s, status, lastChecked: Date.now() } : s ); ctx.patchState({ services: updatedServices }); }); } }