rxjs#concat TypeScript Examples

The following examples show how to use rxjs#concat. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: setup-account-preferences.page.ts    From fyle-mobile-app with MIT License 6 votes vote down vote up
ngOnInit() {
    const networkWatcherEmitter = new EventEmitter<boolean>();
    this.networkService.connectivityWatcher(networkWatcherEmitter);
    this.isConnected$ = concat(this.networkService.isOnline(), networkWatcherEmitter.asObservable());
    this.isConnected$.subscribe(noop);

    this.eou$ = from(this.authService.getEou());
    this.org$ = this.orgService.getCurrentOrg().pipe(shareReplay(1));
    this.companyName$ = this.org$.pipe(map((org) => org.name));

    this.orgSettings$ = this.orgSettingsService.get();

    this.fg = this.fb.group({
      mileage: [true],
      per_diem: [false],
      ccc: [true],
      advances: [true],
    });
  }
Example #2
Source File: index.ts    From angular-git-info with MIT License 6 votes vote down vote up
function updateDependencies(): Rule {
    return (tree: Tree, context: SchematicContext): Observable<Tree> => {
        context.logger.debug('Updating dependencies...');
        context.addTask(new NodePackageInstallTask());

        const fixedDependencies = of({name: 'fs-extra', version: '6.0.1'})
            .pipe(
                map((packageFromRegistry: NodePackage) => {
                    const {name, version} = packageFromRegistry;
                    context.logger.debug(`Adding ${name}:${version} to ${NodeDependencyType.Dev}`);

                    addPackageJsonDependency(tree, {
                        type: NodeDependencyType.Dev,
                        name,
                        version,
                    });

                    return tree;
                })
            );
        const addLatestDependencies = of('git-describe').pipe(
            concatMap((packageName: string) => getLatestNodeVersion(packageName)),
            map((packageFromRegistry: NodePackage) => {
                const {name, version} = packageFromRegistry;
                context.logger.debug(`Adding ${name}:${version} to ${NodeDependencyType.Dev}`);

                addPackageJsonDependency(tree, {
                    type: NodeDependencyType.Dev,
                    name,
                    version,
                });

                return tree;
            })
        );

        return concat(addLatestDependencies, fixedDependencies);
    };
}
Example #3
Source File: corporate-card-expenses.page.ts    From fyle-mobile-app with MIT License 6 votes vote down vote up
setupNetworkWatcher() {
    const networkWatcherEmitter = new EventEmitter<boolean>();
    this.networkService.connectivityWatcher(networkWatcherEmitter);
    this.isConnected$ = concat(this.networkService.isOnline(), networkWatcherEmitter.asObservable()).pipe(
      takeUntil(this.onPageExit),
      shareReplay(1)
    );

    this.isConnected$.subscribe((isOnline) => {
      if (!isOnline) {
        this.router.navigate(['/', 'enterprise', 'my_dashboard']);
      }
    });
  }
Example #4
Source File: scanned-item-manager.service.ts    From pantry_party with Apache License 2.0 6 votes vote down vote up
newScanResults(r: ScanResult) {
    if (this.indexOfBarcode(r.text) > -1) {
      const quantity = this.foundBarcodes[r.text]?.amount;
      this.changeQuantityBy(r.text, +quantity || 1);
    } else {
      const newItem: ScannedItem = {
        barcode: r.text,
        quantity: 0,
        currentVersion: this.generateVersion(),
        lastSavedVersion: "",
        autoSave: true,
        saveInProgress: false,
        saveCoutdown: this.defaultWaitToSave,
        bestBeforeDate: toDateString(new Date()),
        location: this.defaultLocation
      };

      this.findGrocyProduct(newItem);

      this.scannedItems = [newItem].concat(this.scannedItems);
    }
  }
Example #5
Source File: connectivity-status.component.ts    From storm with MIT License 6 votes vote down vote up
constructor() {
    this.$connected = new Subject<boolean>();
    this.$visible = this.$connected.pipe(
      tap(_ => {
        this.animate = false;
        this.closing = false;
      }),
      switchMap(ok => {
        if (!ok) {
          // If not connected then make the status visible
          return of(true);
        }

        // Otherwise, if connected
        // Make the status visible, then after 2 seconds disable the visibility
        return concat(
          of(true),
          // After two seconds begin the transition
          timer(2000, 0).pipe(
            switchMap(_ => {
              this.animate = true;
              this.closing = true;
              return timer(1000, 0).pipe(
                map(_ => false)
              );
            })
          )
        );
      })
    );
  }
Example #6
Source File: scanned-item-manager.service.ts    From pantry_party with Apache License 2.0 6 votes vote down vote up
removeScannedItemByBarcode(barcode: string) {
    const loc = this.indexOfBarcode(barcode);

    if (loc < 0) {
      return;
    }

    this.scannedItems = this.scannedItems.slice(0, loc).concat(
      this.scannedItems.slice(loc + 1)
    );
  }
Example #7
Source File: data-studio.service.ts    From visualization with MIT License 6 votes vote down vote up
constructor(
    @Inject(WINDOW) private readonly window: Window,
    @Inject(MERMAID) private readonly mermaid: Mermaid,
    private readonly alert: AlertService) {

    this.initializeMermaid();
    const azEvent$ = fromEvent<Event>(this.window, 'message').pipe(
      tap(event => {
        if (event.data.status === Status.Error) {
          this.alert.showError({
            status: event.data.status,
            errors: event.data.errors,
            rawData: JSON.stringify(event.data.rawData)
          });
        }
      })
    );

    this.status$ = concat(azEvent$.pipe(
      map(e => e.data?.status),

    ));

    this.database$ = azEvent$.pipe(
      filter(event => event.data?.status === Status.Complete),
      map(event => event.data?.chart),
      switchMap(r => this.buildSvg(r))
    );



    this.databaseName$ = azEvent$.pipe(
      filter(event => event.data?.status === Status.Complete),
      map(event => event.data?.databaseName));
  }
Example #8
Source File: intl.service.ts    From sba-angular with MIT License 6 votes vote down vote up
/**
     * Initialize the service. The current locale is initialized to either the `sinequa-locale` local
     * storage value, the browser language or the default locale.
     *
     * This method is called automatically by the {@link IntlModule} at application startup.
     *
     * @return An observable of the current locale
     */
    init(): Observable<string> {
        // Set up formats
        this.formats = Utils.merge(DEFAULT_FORMATS, this.intlConfig.formats);
        // Load default locale
        let observable = this.use(this.localesConfig.defaultLocale.name, false);
        const initialLocale = this.getInitialLocale();
        if (initialLocale !== this.localesConfig.defaultLocale) {
            // Load initial locale if different to default
            console.log("Setting initial locale: ", initialLocale.name);
            observable = concat<string>(observable, this.use(initialLocale.name, false)).pipe(last<string>());
        }
        Utils.subscribe(observable,
            (value) => {
                console.log("Initial locale set: ", value);
            });
        return observable;
    }
Example #9
Source File: context-service.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 6 votes vote down vote up
public observe$<T>(name: string, options?: ContextLookupOptions): Observable<T | T[] | null> {
    if (Beans.get(IS_PLATFORM_HOST)) {
      return concat(of(options?.collect ? [] : null), NEVER);
    }

    return this._contextTreeChange$
      .pipe(
        filter(event => event.name === name),
        startWith(undefined as void),
        switchMap(() => this.lookupContextValue$<T>(name, options)),
      );
  }
Example #10
Source File: get-connected-status.ts    From sdk with MIT License 6 votes vote down vote up
export function getConnectedStatus(provider: WalletConnectProvider): Observable<ConnectStatus> {
	if ("on" in provider) {
		return new Observable<ConnectStatus>(subscriber => {
			subscriber.next("connected")
			function handler() {
				subscriber.next("disconnected")
			}
			provider.on("disconnected", handler)
			if ("removeListener" in provider) {
				subscriber.add(() => {
					provider.removeListener("disconnected", handler)
				})
			}
		})
	} else {
		return concat(of("connected" as const), NEVER)
	}
}
Example #11
Source File: scanned-item-manager.service.ts    From pantry_party with Apache License 2.0 6 votes vote down vote up
changeQuantityBy(barcode: string, quantity: number) {
    const originalItem = this.scannedItems[this.indexOfBarcode(barcode)];
    const newItem = {
      ...originalItem,
      quantity: originalItem.quantity + quantity,
      currentVersion: this.generateVersion(),
      saveCoutdown: this.defaultWaitToSave
    };

    this.scannedItems = [newItem].concat(
      this.scannedItems.slice(0, this.indexOfBarcode(barcode)),
      this.scannedItems.slice(this.indexOfBarcode(barcode) + 1)
    );
  }
Example #12
Source File: bind.plugin.ts    From ng-event-plugins with Apache License 2.0 6 votes vote down vote up
addEventListener(
        element: HTMLElement & Record<string, Observable<unknown>>,
        event: string,
    ): Function {
        element[event] = element[event] || EMPTY;

        const method = this.getMethod(element, event);
        const zone$ = this.manager.getZone().onStable;
        const sub = concat(
            zone$.pipe(takeWhile(() => element[event] === EMPTY)),
            defer(() => element[event]),
        ).subscribe(method);

        return () => sub.unsubscribe();
    }
Example #13
Source File: authentication.epic.ts    From rn-clean-architecture-template with MIT License 6 votes vote down vote up
signInEpic$: Epic<AuthenticationEpicActions> = (action$) =>
  action$.pipe(
    filter(signIn.match),
    switchMap((action) => {
      const useCase = container.resolve<SignInUseCase>(
        AppDependencies.StoreContainer,
      );
      return concat(
        of(signInBegin()),
        useCase.call(action.payload).pipe(
          map(signInSuccess),
          catchError(() => of(signInFailed())),
        ),
      );
    }),
  )
Example #14
Source File: RestApi.ts    From majsoul-api with MIT License 6 votes vote down vote up
private getSessions(contest: store.Contest<ObjectId>): Observable<Session> {
		return concat(
			defer(() => from(
				this.mongoStore.sessionsCollection.find(
					{ contestId: contest._id },
					{ sort: { scheduledTime: 1 } }
				).toArray()
			)).pipe(
				mergeAll(),
			),
			of<store.Session<ObjectId>>(null)
		).pipe(
			pairwise(),
			map(([session, nextSession]) =>
				defer(() => from(this.getSessionSummary(contest, session, nextSession)))
					.pipe(
						map(totals => {
							return { ...session, totals, aggregateTotals: totals };
						})
					)
			),
			mergeAll(),
		);
	}
Example #15
Source File: fresh-chat.service.ts    From fyle-mobile-app with MIT License 6 votes vote down vote up
setupNetworkWatcher() {
    const that = this;
    const networkWatcherEmitter = new EventEmitter<boolean>();
    this.networkService.connectivityWatcher(networkWatcherEmitter);
    concat(that.networkService.isOnline(), networkWatcherEmitter.asObservable()).subscribe(async (isOnline) => {
      const eou = await that.authService.getEou();
      if (eou && isOnline) {
        const orgUserSettings = await that.getOrgUserSettings();
        if (
          orgUserSettings &&
          orgUserSettings.in_app_chat_settings &&
          orgUserSettings.in_app_chat_settings.allowed &&
          orgUserSettings.in_app_chat_settings.enabled
        ) {
          await that.storageService.set('inAppChatRestoreId', orgUserSettings.in_app_chat_settings.restore_id);
          that.initiateCall();
        }
      }
    });
  }
Example #16
Source File: RestApi.ts    From majsoul-api with MIT License 5 votes vote down vote up
private async getLeaguePhaseData({
		contest,
		transitions,
		phases
	}: PhaseInfo): Promise<LeaguePhase<ObjectID>[]> {
		const sessions = (await this.getSessions(contest).pipe(toArray()).toPromise())
			.sort((a, b) => a.scheduledTime - b.scheduledTime);
		return from(phases.concat(null)).pipe(
			pairwise(),
			mergeScan((completePhase, [phase, nextPhase]) => {
				const transition = transitions[phase.index];
				const phaseSessions = sessions.filter(
					session =>
						session.scheduledTime >= phase.startTime
						&& (nextPhase == null || session.scheduledTime < nextPhase.startTime)
				);
				const startingTotals = {
					...completePhase.sessions[completePhase.sessions.length - 1]?.aggregateTotals ?? {}
				};

				const rankedTeams = Object.entries(startingTotals)
					.map(([team, score]) => ({ team, score }))
					.sort((a, b) => b.score - a.score);

				const allowedTeams = transition.teams?.top
					? rankedTeams.slice(0, transition.teams.top)
						.reduce((total, next) => (total[next.team] = true, total), {} as Record<string, true>)
					: null;

				for (const team of Object.keys(startingTotals)) {
					if (allowedTeams && !(team in allowedTeams)) {
						delete startingTotals[team];
						continue;
					}

					if (transition.score?.half) {
						startingTotals[team] = Math.floor(startingTotals[team] / 2);
					} else if (transition.score?.nil) {
						startingTotals[team] = 0;
					}
				}

				return of({
					...phase,
					sessions: phaseSessions.reduce((total, next) => {
						const aggregateTotals = { ...(total[total.length - 1]?.aggregateTotals ?? startingTotals) };
						const filteredTotals = Object.entries(next.totals)
							.filter(([key]) => !allowedTeams || key in allowedTeams)
							.reduce((total, [key, value]) => (total[key] = value, total), {} as Record<string, number>);

						for (const team in filteredTotals) {
							if (aggregateTotals[team] == null) {
								aggregateTotals[team] = 0;
							}
							aggregateTotals[team] += filteredTotals[team];
						}

						total.push({
							...next,
							totals: filteredTotals,
							aggregateTotals,
						})
						return total;
					}, [] as Session<ObjectID>[]),
					aggregateTotals: startingTotals,
				} as LeaguePhase<ObjectID>);
			}, {
				sessions: [{
					aggregateTotals: (contest.teams ?? []).reduce(
						(total, next) => (total[next._id.toHexString()] = 0, total),
						{} as Record<string, number>
					)
				}]
			} as LeaguePhase<ObjectID>, 1),
			toArray(),
		).toPromise();
	}
Example #17
Source File: expenses-card.component.ts    From fyle-mobile-app with MIT License 5 votes vote down vote up
setupNetworkWatcher() {
    const networkWatcherEmitter = new EventEmitter<boolean>();
    this.networkService.connectivityWatcher(networkWatcherEmitter);
    this.isConnected$ = concat(this.networkService.isOnline(), networkWatcherEmitter.asObservable()).pipe(
      startWith(true)
    );
  }
Example #18
Source File: connector.ts    From sdk with MIT License 5 votes vote down vote up
constructor(
		private readonly providers: ConnectionProvider<Option, Connection>[],
		private readonly stateProvider?: IConnectorStateProvider,
	) {
		Connector.initPageUnloadProtection()

		this.add = this.add.bind(this)
		this.connect = this.connect.bind(this)

		this.connection = concat(
			of(STATE_INITIALIZING),
			defer(() => this.checkAutoConnect()),
			this.provider.pipe(
				distinctUntilChanged(),
				switchMap(provider => {
					if (provider) {
						return concat(provider.getConnection(), NEVER).pipe(
							catchError(error => concat(of(getStateDisconnected({ error })), NEVER)),
						)
					} else {
						return concat(of(getStateDisconnected()), NEVER)
					}
				}),
			),
		).pipe(
			distinctUntilChanged((c1, c2) => {
				if (Connector.pageUnloading) return true
				if (c1 === c2) return true
				if (c1.status === "connected" && c2.status === "connected") {
					return c1.connection === c2.connection
				} else if (c1.status === "connecting" && c2.status === "connecting") {
					return c1.providerId === c2.providerId
				}
				return c1.status === c2.status
			}),
			shareReplay(1),
			map(conn => {
				if (conn.status === "connected") {
					return {
						...conn,
						disconnect: async () => {
							if (conn.disconnect !== undefined) {
								try {
									await conn.disconnect()
								} catch (e) {
									console.warn("caught on disconnect", e)
								}
							}
							this.provider.next(undefined)
						},
					}
				} else {
					return conn
				}
			}),
			tap(async conn => {
				if (conn.status === "disconnected" && !Connector.pageUnloading) {
					this.provider.next(undefined)
					const current = await this.stateProvider?.getValue()
					if (current !== undefined) {
						this.stateProvider?.setValue(undefined)
					}
				}
			}),
		)
	}
Example #19
Source File: platform.ts    From homebridge-esphome-ts with GNU General Public License v3.0 5 votes vote down vote up
protected onHomebridgeDidFinishLaunching(): void {
        let devices: Observable<IEsphomeDeviceConfig> = from(this.config.devices ?? []);
        if (this.config.discover) {
            const excludeConfigDevices: Set<string> = new Set();
            devices = concat(
                discoverDevices(this.config.discoveryTimeout ?? DEFAULT_DISCOVERY_TIMEOUT, this.log).pipe(
                    map((discoveredDevice) => {
                        const configDevice = this.config.devices?.find(({ host }) => host === discoveredDevice.host);
                        let deviceConfig = discoveredDevice;
                        if (configDevice) {
                            excludeConfigDevices.add(configDevice.host);
                            deviceConfig = { ...discoveredDevice, ...configDevice };
                        }

                        return {
                            ...deviceConfig,
                            // Override hostname with ip address when available
                            // to avoid issues with mDNS resolution at OS level
                            host: discoveredDevice.address ?? discoveredDevice.host,
                        };
                    }),
                ),
                // Feed into output remaining devices from config that haven't been discovered
                devices.pipe(filter(({ host }) => !excludeConfigDevices.has(host))),
            );
        }

        this.subscription.add(
            devices
                .pipe(
                    mergeMap((deviceConfig) => {
                        const device = new EspDevice(deviceConfig.host, deviceConfig.password, deviceConfig.port);
                        if (this.config.debug) {
                            this.log('Writing the raw data from your ESP Device to /tmp');
                            writeReadDataToLogFile(deviceConfig.host, device);
                        }
                        device.provideRetryObservable(
                            interval(deviceConfig.retryAfter ?? this.config.retryAfter ?? DEFAULT_RETRY_AFTER).pipe(
                                tap(() => this.log.info(`Trying to reconnect now to device ${deviceConfig.host}`)),
                            ),
                        );
                        return device.discovery$.pipe(
                            filter((value: boolean) => value),
                            take(1),
                            timeout(10 * 1000),
                            tap(() => this.addAccessories(device)),
                            catchError((err) => {
                                if (err.name === 'TimeoutError') {
                                    this.log.warn(
                                        `The device under the host ${deviceConfig.host} could not be reached.`,
                                    );
                                }
                                return of(err);
                            }),
                        );
                    }),
                )
                .subscribe(),
        );
    }
Example #20
Source File: sidemenu.component.ts    From fyle-mobile-app with MIT License 5 votes vote down vote up
setupNetworkWatcher() {
    const networkWatcherEmitter = new EventEmitter<boolean>();
    this.networkService.connectivityWatcher(networkWatcherEmitter);
    this.isConnected$ = concat(this.networkService.isOnline(), networkWatcherEmitter.asObservable()).pipe(
      shareReplay(1)
    );
  }
Example #21
Source File: index.ts    From dbm with Apache License 2.0 5 votes vote down vote up
build$ =
  process.argv.includes("--dirty")
    ? templates$
    : concat(assets$, merge(templates$, index$))
Example #22
Source File: scanned-item-manager.service.ts    From pantry_party with Apache License 2.0 5 votes vote down vote up
updateItem(newItem: ScannedItem) {
    const loc = this.indexOfBarcode(newItem.barcode);

    this.scannedItems = this.scannedItems.slice(0, loc).concat(
      [newItem],
      this.scannedItems.slice(loc + 1)
    );
  }
Example #23
Source File: message-handler.script.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 5 votes vote down vote up
export async function connectToHostThenIntentClientOnIntent({symbolicName}): Promise<void> { // eslint-disable-line @typescript-eslint/typedef
  await MicrofrontendPlatform.connectToHost(symbolicName);
  Beans.get(IntentClient).onIntent<void>({type: 'capability'}, () => concat(of('initial'), NEVER));
}
Example #24
Source File: app.component.ts    From fyle-mobile-app with MIT License 5 votes vote down vote up
setupNetworkWatcher() {
    const networkWatcherEmitter = new EventEmitter<boolean>();
    this.networkService.connectivityWatcher(networkWatcherEmitter);
    this.isConnected$ = concat(this.networkService.isOnline(), networkWatcherEmitter.asObservable()).pipe(
      shareReplay(1)
    );
  }
Example #25
Source File: index.ts    From dbm with Apache License 2.0 5 votes vote down vote up
assets$ = concat(

  /* Copy Material Design icons */
  ...["*.svg", "../LICENSE"]
    .map(pattern => copyAll(pattern, {
      from: "node_modules/@mdi/svg/svg",
      to: `${base}/.icons/material`,
      transform: async data => minsvg(data)
    })),

  /* Copy GitHub octicons */
  ...["*.svg", "../../LICENSE"]
    .map(pattern => copyAll(pattern, {
      from: "node_modules/@primer/octicons/build/svg",
      to: `${base}/.icons/octicons`,
      transform: async data => minsvg(data)
    })),

  /* Copy FontAwesome icons */
  ...["**/*.svg", "../LICENSE.txt"]
    .map(pattern => copyAll(pattern, {
      from: "node_modules/@fortawesome/fontawesome-free/svgs",
      to: `${base}/.icons/fontawesome`,
      transform: async data => minsvg(data)
    })),

  /* Copy Lunr.js search stemmers and segmenters */
  ...["min/*.js", "tinyseg.js", "wordcut.js"]
    .map(pattern => copyAll(pattern, {
      from: "node_modules/lunr-languages",
      to: `${base}/assets/javascripts/lunr`
    })),

  /* Copy images and configurations */
  ...[".icons/*.svg", "assets/images/*", "**/*.{py,yml}"]
    .map(pattern => copyAll(pattern, {
      from: "src",
      to: base
    }))
)
Example #26
Source File: my-reports.page.ts    From fyle-mobile-app with MIT License 4 votes vote down vote up
ionViewWillEnter() {
    this.tasksService.getReportsTaskCount().subscribe((reportsTaskCount) => {
      this.reportsTaskCount = reportsTaskCount;
    });

    this.isLoading = true;
    this.setupNetworkWatcher();

    this.searchText = '';
    this.navigateBack = !!this.activatedRoute.snapshot.params.navigateBack;
    this.acc = [];

    this.currentPageNumber = 1;
    this.loadData$ = new BehaviorSubject({
      pageNumber: 1,
    });
    this.homeCurrency$ = this.currencyService.getHomeCurrency();

    this.simpleSearchInput.nativeElement.value = '';
    fromEvent(this.simpleSearchInput.nativeElement, 'keyup')
      .pipe(
        map((event: any) => event.srcElement.value as string),
        distinctUntilChanged(),
        debounceTime(1000)
      )
      .subscribe((searchString) => {
        const currentParams = this.loadData$.getValue();
        currentParams.searchString = searchString;
        this.currentPageNumber = 1;
        currentParams.pageNumber = this.currentPageNumber;
        this.loadData$.next(currentParams);
      });

    const paginatedPipe = this.loadData$.pipe(
      switchMap((params) => {
        let queryParams = params.queryParams || {
          rp_state: 'in.(DRAFT,APPROVED,APPROVER_PENDING,APPROVER_INQUIRY,PAYMENT_PENDING,PAYMENT_PROCESSING,PAID)',
        };
        const orderByParams = params.sortParam && params.sortDir ? `${params.sortParam}.${params.sortDir}` : null;
        queryParams = this.apiV2Service.extendQueryParamsForTextSearch(queryParams, params.searchString);
        this.isLoadingDataInInfiniteScroll = true;
        return this.reportService.getMyReportsCount(queryParams).pipe(
          switchMap((count) => {
            if (count > (params.pageNumber - 1) * 10) {
              return this.reportService.getMyReports({
                offset: (params.pageNumber - 1) * 10,
                limit: 10,
                queryParams,
                order: orderByParams,
              });
            } else {
              return of({
                data: [],
              });
            }
          })
        );
      }),
      map((res) => {
        this.isLoadingDataInInfiniteScroll = false;
        if (this.currentPageNumber === 1) {
          this.acc = [];
        }
        this.acc = this.acc.concat(res.data);
        return this.acc;
      })
    );

    this.myReports$ = paginatedPipe.pipe(shareReplay(1));

    this.count$ = this.loadData$.pipe(
      switchMap((params) => {
        let queryParams = params.queryParams || {
          rp_state: 'in.(DRAFT,APPROVED,APPROVER_PENDING,APPROVER_INQUIRY,PAYMENT_PENDING,PAYMENT_PROCESSING,PAID)',
        };
        queryParams = this.apiV2Service.extendQueryParamsForTextSearch(queryParams, params.searchString);
        return this.reportService.getMyReportsCount(queryParams);
      }),
      shareReplay(1)
    );

    const paginatedScroll$ = this.myReports$.pipe(
      switchMap((erpts) => this.count$.pipe(map((count) => count > erpts.length)))
    );

    this.isInfiniteScrollRequired$ = this.loadData$.pipe(switchMap((_) => paginatedScroll$));

    this.loadData$.subscribe((params) => {
      const queryParams: Params = { filters: JSON.stringify(this.filters) };
      this.router.navigate([], {
        relativeTo: this.activatedRoute,
        queryParams,
        replaceUrl: true,
      });
    });

    this.expensesAmountStats$ = this.loadData$.pipe(
      switchMap((_) =>
        this.transactionService
          .getTransactionStats('count(tx_id),sum(tx_amount)', {
            scalar: true,
            tx_report_id: 'is.null',
            tx_state: 'in.(COMPLETE)',
            or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)',
          })
          .pipe(
            map((stats) => {
              const sum =
                stats && stats[0] && stats[0].aggregates.find((stat) => stat.function_name === 'sum(tx_amount)');
              const count =
                stats && stats[0] && stats[0].aggregates.find((stat) => stat.function_name === 'count(tx_id)');
              return {
                sum: (sum && sum.function_value) || 0,
                count: (count && count.function_value) || 0,
              };
            })
          )
      )
    );

    this.myReports$.subscribe(noop);
    this.count$.subscribe(noop);
    this.isInfiniteScrollRequired$.subscribe(noop);

    if (this.activatedRoute.snapshot.queryParams.filters) {
      this.filters = Object.assign({}, this.filters, JSON.parse(this.activatedRoute.snapshot.queryParams.filters));
      this.currentPageNumber = 1;
      const params = this.addNewFiltersToParams();
      this.loadData$.next(params);
      this.filterPills = this.generateFilterPills(this.filters);
    } else if (this.activatedRoute.snapshot.params.state) {
      const filters = {
        rp_state: `in.(${this.activatedRoute.snapshot.params.state.toLowerCase()})`,
        state: this.activatedRoute.snapshot.params.state.toUpperCase(),
      };

      this.filters = Object.assign({}, this.filters, filters);
      this.currentPageNumber = 1;
      const params = this.addNewFiltersToParams();
      this.loadData$.next(params);
      this.filterPills = this.generateFilterPills(this.filters);
    } else {
      this.clearFilters();
    }

    setTimeout(() => {
      this.isLoading = false;
    }, 500);
  }
Example #27
Source File: neo4j.spec.ts    From opentelemetry-ext-js with Apache License 2.0 4 votes vote down vote up
/**
 * Tests require neo4j to run, and expose bolt port of 11011
 *
 * Use this command to run the required neo4j using docker:
 * docker run --name testneo4j -p7474:7474 -p11011:7687 -d --env NEO4J_AUTH=neo4j/test neo4j:4.2.3
 * */

describe('neo4j instrumentation', function () {
    this.timeout(10000);
    let driver: Driver;

    const getSingleSpan = () => {
        const spans = getTestSpans();
        expect(spans.length).toBe(1);
        return spans[0];
    };

    before(async () => {
        driver = neo4j.driver('bolt://localhost:11011', neo4j.auth.basic('neo4j', 'test'), {
            disableLosslessIntegers: true,
        });

        let keepChecking = true;
        const timeoutId = setTimeout(() => {
            keepChecking = false;
        }, 8000);
        while (keepChecking) {
            try {
                await driver.verifyConnectivity();
                clearTimeout(timeoutId);
                return;
            } catch (err) {
                await new Promise((res) => setTimeout(res, 1000));
            }
        }
        throw new Error('Could not connect to neo4j in allowed time frame');
    });

    after(async () => {
        await driver.close();
    });

    beforeEach(async () => {
        await driver.session().run('MATCH (n) DETACH DELETE n');
        instrumentation.enable();
    });

    afterEach(async () => {
        instrumentation.disable();
        instrumentation.setConfig({});
    });

    describe('session', () => {
        it('instruments "run" with promise', async () => {
            const res = await driver.session().run('CREATE (n:MyLabel) RETURN n');

            expect(res.records.length).toBe(1);
            expect((res.records[0].toObject() as any).n.labels).toEqual(['MyLabel']);

            const span = getSingleSpan();
            assertSpan(span as ReadableSpan);
            expect(span.name).toBe('CREATE neo4j');
            expect(span.attributes[SemanticAttributes.DB_OPERATION]).toBe('CREATE');
            expect(span.attributes[SemanticAttributes.DB_STATEMENT]).toBe('CREATE (n:MyLabel) RETURN n');
        });

        it('instruments "run" with subscribe', (done) => {
            driver
                .session()
                .run('CREATE (n:MyLabel) RETURN n')
                .subscribe({
                    onCompleted: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(span.attributes[SemanticAttributes.DB_OPERATION]).toBe('CREATE');
                        expect(span.attributes[SemanticAttributes.DB_STATEMENT]).toBe('CREATE (n:MyLabel) RETURN n');
                        done();
                    },
                });
        });

        it('handles "run" exceptions with promise', async () => {
            try {
                await driver.session().run('NOT_EXISTS_OPERATION');
            } catch (err) {
                const span = getSingleSpan();
                expect(span.status.code).toBe(SpanStatusCode.ERROR);
                expect(span.status.message).toBe(err.message);
                return;
            }
            throw Error('should not be here');
        });

        it('handles "run" exceptions with subscribe', (done) => {
            driver
                .session()
                .run('NOT_EXISTS_OPERATION')
                .subscribe({
                    onError: (err) => {
                        const span = getSingleSpan();
                        expect(span.status.code).toBe(SpanStatusCode.ERROR);
                        expect(span.status.message).toBe(err.message);
                        done();
                    },
                });
        });

        it('closes span when on "onKeys" event', (done) => {
            driver
                .session()
                .run('MATCH (n) RETURN n')
                .subscribe({
                    onKeys: (keys) => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(keys).toEqual(['n']);
                        done();
                    },
                });
        });

        it('when passing "onKeys" and onCompleted, span is closed in onCompleted, and response hook is called', (done) => {
            instrumentation.disable();
            instrumentation.setConfig({ responseHook: (span) => span.setAttribute('test', 'cool') });
            instrumentation.enable();

            driver
                .session()
                .run('MATCH (n) RETURN n')
                .subscribe({
                    onKeys: () => {
                        const spans = getTestSpans();
                        expect(spans.length).toBe(0);
                    },
                    onCompleted: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(span.attributes['test']).toBe('cool');
                        done();
                    },
                });
        });

        it('handles multiple promises', async () => {
            await Promise.all([
                driver.session().run('MATCH (n) RETURN n'),
                driver.session().run('MATCH (k) RETURN k'),
                driver.session().run('MATCH (d) RETURN d'),
            ]);
            const spans = getTestSpans();
            expect(spans.length).toBe(3);
            for (let span of spans) {
                assertSpan(span as ReadableSpan);
                expect(span.attributes[SemanticAttributes.DB_OPERATION]).toBe('MATCH');
            }
        });

        it('captures operation with trailing white spaces', async () => {
            await driver.session().run('  MATCH (k) RETURN k ');
            const span = getSingleSpan();
            expect(span.attributes[SemanticAttributes.DB_OPERATION]).toBe('MATCH');
        });

        it('set module versions when config is set', async () => {
            instrumentation.disable();
            instrumentation.setConfig({ moduleVersionAttributeName: 'module.version' });
            instrumentation.enable();
            await driver.session().run('CREATE (n:MyLabel) RETURN n');

            const span = getSingleSpan();
            expect(span.attributes['module.version']).toMatch(/\d{1,4}\.\d{1,4}\.\d{1,5}.*/);
        });

        it('does not capture any span when ignoreOrphanedSpans is set to true', async () => {
            instrumentation.disable();
            instrumentation.setConfig({ ignoreOrphanedSpans: true });
            instrumentation.enable();
            await context.with(ROOT_CONTEXT, async () => {
                await driver.session().run('CREATE (n:MyLabel) RETURN n');
            });

            const spans = getTestSpans();
            expect(spans.length).toBe(0);
        });

        it('does capture span when ignoreOrphanedSpans is set to true and has parent span', async () => {
            instrumentation.disable();
            instrumentation.setConfig({ ignoreOrphanedSpans: true });
            instrumentation.enable();
            const parent = trace.getTracerProvider().getTracer('test-tracer').startSpan('main');
            await context.with(trace.setSpan(context.active(), parent), () =>
                driver.session().run('CREATE (n:MyLabel) RETURN n')
            );

            const spans = getTestSpans();
            expect(spans.length).toBe(1);
        });

        it('responseHook works with promise', async () => {
            instrumentation.disable();
            instrumentation.setConfig({
                responseHook: (span, response) => {
                    span.setAttribute('db.response', normalizeResponse(response));
                },
            });
            instrumentation.enable();

            const res = await driver
                .session()
                .run('CREATE (n:Rick), (b:Meeseeks { purpose: "help"}), (c:Morty) RETURN *');
            expect(res.records.length).toBe(1);

            const span = getSingleSpan();
            assertSpan(span as ReadableSpan);
            expect(JSON.parse(span.attributes['db.response'] as string)).toEqual([
                {
                    b: { labels: ['Meeseeks'], properties: { purpose: 'help' } },
                    c: { labels: ['Morty'], properties: {} },
                    n: { labels: ['Rick'], properties: {} },
                },
            ]);
        });

        it('responseHook works with subscribe', (done) => {
            instrumentation.disable();
            instrumentation.setConfig({
                responseHook: (span, response) => {
                    span.setAttribute('db.response', normalizeResponse(response));
                },
            });
            instrumentation.enable();

            driver
                .session()
                .run('CREATE (n:Rick), (b:Meeseeks { purpose: "help"}), (c:Morty) RETURN *')
                .subscribe({
                    onCompleted: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(JSON.parse(span.attributes['db.response'] as string)).toEqual([
                            {
                                b: { labels: ['Meeseeks'], properties: { purpose: 'help' } },
                                c: { labels: ['Morty'], properties: {} },
                                n: { labels: ['Rick'], properties: {} },
                            },
                        ]);
                        done();
                    },
                });
        });

        it('does not fail when responseHook throws', async () => {
            instrumentation.disable();
            instrumentation.setConfig({
                responseHook: () => {
                    throw new Error('I throw..');
                },
            });
            instrumentation.enable();
            await driver.session().run('CREATE (n:MyLabel) RETURN n');
            const span = getSingleSpan();
            assertSpan(span as ReadableSpan);
        });
    });

    describe('transaction', async () => {
        it('instruments session readTransaction', async () => {
            await driver.session().readTransaction((txc) => {
                return txc.run('MATCH (person:Person) RETURN person.name AS name');
            });
            const span = getSingleSpan();
            assertSpan(span as ReadableSpan);
            expect(span.attributes[SemanticAttributes.DB_OPERATION]).toBe('MATCH');
            expect(span.attributes[SemanticAttributes.DB_STATEMENT]).toBe(
                'MATCH (person:Person) RETURN person.name AS name'
            );
        });

        it('instruments session writeTransaction', async () => {
            await driver.session().writeTransaction((txc) => {
                return txc.run('MATCH (person:Person) RETURN person.name AS name');
            });
            const span = getSingleSpan();
            assertSpan(span as ReadableSpan);
            expect(span.attributes[SemanticAttributes.DB_OPERATION]).toBe('MATCH');
            expect(span.attributes[SemanticAttributes.DB_STATEMENT]).toBe(
                'MATCH (person:Person) RETURN person.name AS name'
            );
        });

        it('instruments explicit transactions', async () => {
            const txc = driver.session().beginTransaction();
            await txc.run('MERGE (bob:Person {name: "Bob"}) RETURN bob.name AS name');
            await txc.run('MERGE (adam:Person {name: "Adam"}) RETURN adam.name AS name');
            await txc.commit();

            const spans = getTestSpans();
            expect(spans.length).toBe(2);
        });
    });

    describe('rxSession', () => {
        it('instruments "run"', (done) => {
            driver
                .rxSession()
                .run('MERGE (n:MyLabel) RETURN n')
                .records()
                .subscribe({
                    complete: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        done();
                    },
                });
        });

        it('works when piping response', (done) => {
            const rxSession = driver.rxSession();
            rxSession
                .run('MERGE (james:Person {name: $nameParam}) RETURN james.name AS name', {
                    nameParam: 'Bob',
                })
                .records()
                .pipe(map((record) => record.get('name')))
                .subscribe({
                    next: () => {},
                    complete: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(span.attributes[SemanticAttributes.DB_STATEMENT]).toBe(
                            'MERGE (james:Person {name: $nameParam}) RETURN james.name AS name'
                        );
                        done();
                    },
                    error: () => {},
                });
        });

        it('works with response hook', (done) => {
            instrumentation.disable();
            instrumentation.setConfig({
                responseHook: (span, response) => {
                    span.setAttribute('db.response', normalizeResponse(response));
                },
            });
            instrumentation.enable();

            driver
                .rxSession()
                .run('MERGE (n:MyLabel) RETURN n')
                .records()
                .subscribe({
                    complete: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(span.attributes['db.response']).toBe(`[{"n":{"labels":["MyLabel"],"properties":{}}}]`);
                        done();
                    },
                });
        });
    });

    describe('reactive transaction', () => {
        it('instruments rx session readTransaction', (done) => {
            driver
                .rxSession()
                .readTransaction((txc) =>
                    txc
                        .run('MATCH (person:Person) RETURN person.name AS name')
                        .records()
                        .pipe(map((record) => record.get('name')))
                )
                .subscribe({
                    next: () => {},
                    complete: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(span.attributes[SemanticAttributes.DB_STATEMENT]).toBe(
                            'MATCH (person:Person) RETURN person.name AS name'
                        );
                        done();
                    },
                    error: () => {},
                });
        });

        it('instruments rx session writeTransaction', (done) => {
            driver
                .rxSession()
                .writeTransaction((txc) =>
                    txc
                        .run('MATCH (person:Person) RETURN person.name AS name')
                        .records()
                        .pipe(map((record) => record.get('name')))
                )
                .subscribe({
                    next: () => {},
                    complete: () => {
                        const span = getSingleSpan();
                        assertSpan(span as ReadableSpan);
                        expect(span.attributes[SemanticAttributes.DB_STATEMENT]).toBe(
                            'MATCH (person:Person) RETURN person.name AS name'
                        );
                        done();
                    },
                    error: () => {},
                });
        });

        it('instruments rx explicit transactions', (done) => {
            driver
                .rxSession()
                .beginTransaction()
                .pipe(
                    mergeMap((txc) =>
                        concat(
                            txc
                                .run('MERGE (bob:Person {name: $nameParam}) RETURN bob.name AS name', {
                                    nameParam: 'Bob',
                                })
                                .records()
                                .pipe(map((r: any) => r.get('name'))),
                            txc
                                .run('MERGE (adam:Person {name: $nameParam}) RETURN adam.name AS name', {
                                    nameParam: 'Adam',
                                })
                                .records()
                                .pipe(map((r: any) => r.get('name'))),
                            txc.commit()
                        )
                    )
                )
                .subscribe({
                    next: () => {},
                    complete: () => {
                        const spans = getTestSpans();
                        expect(spans.length).toBe(2);
                        done();
                    },
                    error: () => {},
                });
        });
    });

    describe('routing mode', () => {
        // When the connection string starts with "neo4j" routing mode is used
        let routingDriver: Driver;
        const version = require('neo4j-driver/package.json').version;
        const shouldCheck = !['4.0.0', '4.0.1', '4.0.2'].includes(version);

        before(() => {
            if (shouldCheck) {
                routingDriver = neo4j.driver('neo4j://localhost:11011', neo4j.auth.basic('neo4j', 'test'));
            }
        });

        after(async () => {
            shouldCheck && (await routingDriver.close());
        });

        it('instruments as expected in routing mode', async () => {
            if (!shouldCheck) {
                // Versions 4.0.0, 4.0.1 and 4.0.2 of neo4j-driver don't allow connection to local neo4j in routing mode.
                console.log(`Skipping unsupported test for version ${version}`);
                return;
            }

            await routingDriver.session().run('CREATE (n:MyLabel) RETURN n');

            const span = getSingleSpan();
            assertSpan(span as ReadableSpan);
        });
    });
});
Example #28
Source File: InviteMembers.tsx    From argo-react with MIT License 4 votes vote down vote up
InviteMembers = () => {
  const history = useHistory();
  const { selectedOrg, user } = useContext(StateContext);
  const [inviteMembers, setInviteMembers] = useState<string>("");
  const [inviteMemberLoading, setInviteMembersLoading] = useState<boolean>(false);
  const [inviteData, setInviteData] = useState<boolean>();
  const [validateEmail, setValidateEmail] = useState<boolean>(false);
  const [popupIsOpen, setPopupIsOpen] = useState<boolean>(false);

  useEffect(() => {
    if (
      inviteMembers.match("^([a-zA-Z0-9+_.-]+@[a-zA-Z0-9.-]+,?)*$") &&
      inviteMembers !== ""
    ) {
      setValidateEmail(true);
    } else {
      setValidateEmail(false);
    }
  }, [inviteMembers]);

  const sendInvite = () => {
    setInviteMembersLoading(true);
    const members = inviteMembers.split(",").map((member) => member.trim());
    const invites = members.map((member) => ({
      organization: selectedOrg?._id,
      orgName: selectedOrg?.profile.name,
      userEmail: member,
      invitingUser: user?.argoProfile.name,
    }));
    concat(invites.map((invite) => ApiService.sendMemberInvite(invite))).subscribe(
      (res) =>
        res.subscribe((data) => {
          setInviteData(data.success);
          setInviteMembersLoading(false);
        }),
    );
  };

  return (
    <div className="InviteMembers">
      <div className="invite-members-container">
        <div className="invite-members-details">
          <div className="invite-members-inner">
            <h1 className="invite-members-title">Add team members</h1>
            <div className="invite-members-form">
              <label className="invite-members-form-title">
                Emails of the new members
              </label>
              <label className="invite-members-form-subtitle">
                New team members will get an email with a link to accept the
                invitation.
              </label>
              <div className="invite-input-container">
                <span className="mail-icon">
                  <FontAwesomeIcon icon={faEnvelope}></FontAwesomeIcon>
                </span>
                <input
                  type="text"
                  className="invite-members-form-input"
                  placeholder="[email protected]"
                  value={inviteMembers}
                  onChange={(e) => setInviteMembers(e.target.value)}
                />
              </div>
              {!validateEmail && (
                <label className="invite-members-form-alert">
                  <span className="alert-icon-container">
                    <FontAwesomeIcon icon={faExclamationCircle}></FontAwesomeIcon>
                  </span>
                  Please enter the email in a valid format.
                </label>
              )}
              <label className="invite-members-form-subtitle-bottom">
                You can enter several email addresses separated by commas <br />
                (without spaces).
              </label>
            </div>
            <div className="button-container">
              <div>
                <button
                  type="button"
                  className="primary-button"
                  onClick={() => {
                    sendInvite();
                    setPopupIsOpen(true);
                  }}
                  disabled={!validateEmail}
                >
                  {inviteMemberLoading && (
                    <BounceLoader size={20} color={"#fff"} loading={true} />
                  )}
                  Send
                </button>
              </div>
              <InvitePopup
                isOpen={popupIsOpen}
                memberLoading={inviteMemberLoading}
                isData={inviteData!}
              />
              <button
                type="button"
                className="cancel-button"
                onClick={(e) => history.goBack()}
              >
                Cancel
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
Example #29
Source File: message-handler.spec.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 4 votes vote down vote up
describe('Intent Handler', () => {

  const disposables = new Set<Disposable>();

  beforeEach(async () => await MicrofrontendPlatform.destroy());
  afterEach(async () => {
    await MicrofrontendPlatform.destroy();
    disposables.forEach(disposable => disposable());
  });

  describe('pub/sub', () => {

    it('should receive intents', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collector = new Array<string>();
      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        collector.push(intentMessage.body);
      });

      await Beans.get(IntentClient).publish({type: 'capability'}, 'A');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'B');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'C');

      await waitForCondition(() => collector.length === 3);
      await expect(collector).toEqual(['A', 'B', 'C']);
    });

    it('should not unregister the callback on error', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collector = new Array<string>();
      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        collector.push(intentMessage.body);
        throw Error('some error');
      });

      await Beans.get(IntentClient).publish({type: 'capability'}, 'A');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'B');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'C');

      await waitForCondition(() => collector.length === 3);
      await expect(collector).toEqual(['A', 'B', 'C']);
    });

    it('should not unregister the callback on async error', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collector = new Array<string>();
      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, async intentMessage => {
        collector.push(intentMessage.body);
        await Promise.reject('some-error');
      });

      await Beans.get(IntentClient).publish({type: 'capability'}, 'A');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'B');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'C');

      await waitForCondition(() => collector.length === 3);
      await expect(collector).toEqual(['A', 'B', 'C']);
    });

    it('should ignore values returned by the callback', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collector = new Array<string>();
      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        collector.push(intentMessage.body);
        return 'some-value';
      });

      await Beans.get(IntentClient).publish({type: 'capability'}, 'A');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'B');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'C');

      await waitForCondition(() => collector.length === 3);
      await expect(collector).toEqual(['A', 'B', 'C']);
    });

    it('should ignore async values returned by the callback', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collector = new Array<string>();
      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        collector.push(intentMessage.body);
        return Promise.resolve('some-value');
      });

      await Beans.get(IntentClient).publish({type: 'capability'}, 'A');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'B');
      await Beans.get(IntentClient).publish({type: 'capability'}, 'C');

      await waitForCondition(() => collector.length === 3);
      await expect(collector).toEqual(['A', 'B', 'C']);
    });

    it('should unregister the handler when cancelling its subscription', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collector = new Array<string>();
      const subscription = Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        collector.push(intentMessage.body);
        return Promise.resolve('some-value');
      });

      await Beans.get(IntentClient).publish({type: 'capability'}, 'A');
      await waitForCondition(() => collector.length === 1);
      await expect(collector).toEqual(['A']);

      subscription.unsubscribe();
      await Beans.get(IntentClient).publish({type: 'capability'}, 'B');
      await waitFor(1000);
      await expect(collector).toEqual(['A']);
    });
  });

  describe('request/response', () => {

    it('should reply with a single response and then complete the requestor\'s Observable', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        return intentMessage.body.toUpperCase();
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual(['A']);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should reply with a single response (Promise) and then complete the requestor\'s Observable', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        return Promise.resolve(intentMessage.body.toUpperCase());
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual(['A']);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should reply with a single response (Observable) and then complete the requestor\'s Observable', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        return of(intentMessage.body.toUpperCase());
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual(['A']);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should reply with multiple responses and then complete the requestor\'s Observable', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        const body = intentMessage.body.toUpperCase();
        return of(`${body}-1`, `${body}-2`, `${body}-3`);
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual(['A-1', 'A-2', 'A-3']);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should reply with multiple responses without completing the requestor\'s Observable', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        const body = intentMessage.body.toUpperCase();
        const subject = new ReplaySubject(3);
        subject.next(`${body}-1`);
        subject.next(`${body}-2`);
        subject.next(`${body}-3`);
        return subject;
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilEmitCount(3);
      await expect(captor.getValues()).toEqual(['A-1', 'A-2', 'A-3']);
      await waitFor(1000);
      await expect(captor.hasCompleted()).toBeFalse();
      await expect(captor.hasErrored()).toBeFalse();
      await expect(captor.getValues()).toEqual(['A-1', 'A-2', 'A-3']);
    });

    it('should immediately complete the requestor\'s Observable when not returning a value', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        // not returning a value
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual([]);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should immediately complete the requestor\'s Observable when returning `undefined`', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        return undefined;
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual([]);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should treat `null` as valid reply', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        return null;
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual([null]);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should ignore `undefined` values, but not `null` values', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        const body = intentMessage.body.toUpperCase();
        return of(`${body}-1`, undefined, `${body}-2`, null, `${body}-3`);
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.getValues()).toEqual(['A-1', 'A-2', null, 'A-3']);
      await expect(captor.hasCompleted()).toBeTrue();
    });

    it('should error the requestor\'s Observable when throwing an error', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        throw Error('some error');
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.hasErrored()).toBeTrue();
      expect(captor.getError().name).toEqual('RequestError');
      expect(captor.getError().message).toEqual('some error');
      expect(captor.getValues()).toEqual([]);
    });

    it('should error the requestor\'s Observable when returning a Promise that rejects', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        return Promise.reject('some error');
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.hasErrored()).toBeTrue();
      expect(captor.getError().name).toEqual('RequestError');
      expect(captor.getError().message).toEqual('some error');
      expect(captor.getValues()).toEqual([]);
    });

    it('should error the requestor\'s Observable when returning an Observable that errors', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        return throwError(() => 'some error');
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.hasErrored()).toBeTrue();
      expect(captor.getError().name).toEqual('RequestError');
      expect(captor.getError().message).toEqual('some error');
      expect(captor.getValues()).toEqual([]);
    });

    it('should reply values until encountering an error', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        return concat(
          of(intentMessage.body.toUpperCase()),
          throwError(() => 'some error'),
        );
      });

      const captor = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor);

      await captor.waitUntilCompletedOrErrored();
      await expect(captor.hasErrored()).toBeTrue();
      expect(captor.getError().name).toEqual('RequestError');
      expect(captor.getError().message).toEqual('some error');
      expect(captor.getValues()).toEqual(['A']);
    });

    it('should not unregister the handler if the replier Observable errors', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        return concat(
          of(intentMessage.body.toUpperCase()),
          throwError(() => 'some error'),
        );
      });

      const captor1 = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor1);

      await captor1.waitUntilCompletedOrErrored();
      await expect(captor1.hasErrored()).toBeTrue();
      expect(captor1.getError().name).toEqual('RequestError');
      expect(captor1.getError().message).toEqual('some error');
      expect(captor1.getValues()).toEqual(['A']);

      const captor2 = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'b').subscribe(captor2);

      await captor2.waitUntilCompletedOrErrored();
      await expect(captor2.hasErrored()).toBeTrue();
      expect(captor2.getError().name).toEqual('RequestError');
      expect(captor2.getError().message).toEqual('some error');
      expect(captor2.getValues()).toEqual(['B']);
    });

    it('should not unregister the handler on error', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collected = [];
      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        collected.push(intentMessage.body);
        throw Error('some error');
      });

      const captor1 = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor1);
      await captor1.waitUntilCompletedOrErrored();
      await expect(captor1.hasErrored()).toBeTrue();
      expect(captor1.getError().name).toEqual('RequestError');
      expect(captor1.getError().message).toEqual('some error');
      expect(captor1.getValues()).toEqual([]);
      expect(collected).toEqual(['a']);

      const captor2 = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'b').subscribe(captor2);
      await captor2.waitUntilCompletedOrErrored();
      await expect(captor2.hasErrored()).toBeTrue();
      expect(captor2.getError().name).toEqual('RequestError');
      expect(captor2.getError().message).toEqual('some error');
      expect(captor2.getValues()).toEqual([]);
      expect(collected).toEqual(['a', 'b']);
    });

    it('should not unregister the handler on async error', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const collected = [];
      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, intentMessage => {
        collected.push(intentMessage.body);
        return Promise.reject('some error');
      });

      const captor1 = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'a').subscribe(captor1);
      await captor1.waitUntilCompletedOrErrored();
      await expect(captor1.hasErrored()).toBeTrue();
      expect(captor1.getError().name).toEqual('RequestError');
      expect(captor1.getError().message).toEqual('some error');
      expect(captor1.getValues()).toEqual([]);
      expect(collected).toEqual(['a']);

      const captor2 = new ObserveCaptor(bodyExtractFn);
      Beans.get(IntentClient).request$({type: 'capability'}, 'b').subscribe(captor2);
      await captor2.waitUntilCompletedOrErrored();
      await expect(captor2.hasErrored()).toBeTrue();
      expect(captor2.getError().name).toEqual('RequestError');
      expect(captor2.getError().message).toEqual('some error');
      expect(captor2.getValues()).toEqual([]);
      expect(collected).toEqual(['a', 'b']);
    });

    it('should unsubscribe from the replier Observable when the requestor unsubscribes', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const replierCaptor = new ObservableCaptor();

      Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        return new Observable(() => {
          replierCaptor.onConstruct();
          return () => replierCaptor.onUnsubscribe();
        })
          .pipe(finalize(() => replierCaptor.onFinalize()));
      });

      const subscription = Beans.get(IntentClient).request$({type: 'capability'}).subscribe();
      await expectPromise(replierCaptor.constructed).toResolve();
      subscription.unsubscribe();
      await expectPromise(replierCaptor.unsubscribed).toResolve();
      await expectPromise(replierCaptor.finalized).toResolve();
    });

    it('should unsubscribe the replier\'s and requestor\'s Observable when unregistering the handler', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {
            name: 'Host App',
            capabilities: [{type: 'capability'}],
          },
        },
        applications: [],
      });

      const replierCaptor = new ObservableCaptor();
      const requestorCaptor = new ObservableCaptor();

      const handlerSubscription = Beans.get(IntentClient).onIntent<string>({type: 'capability'}, () => {
        return new Observable(() => {
          replierCaptor.onConstruct();
          return () => replierCaptor.onUnsubscribe();
        }).pipe(finalize(() => replierCaptor.onFinalize()));
      });

      const requestorSubscription = Beans.get(IntentClient).request$({type: 'capability'})
        .pipe(finalize(() => requestorCaptor.onFinalize()))
        .subscribe();

      await expectPromise(replierCaptor.constructed).toResolve();
      expect(requestorSubscription.closed).toBeFalse();

      handlerSubscription.unsubscribe();
      await expectPromise(replierCaptor.unsubscribed).toResolve();
      await expectPromise(replierCaptor.finalized).toResolve();
      await expectPromise(requestorCaptor.finalized).toResolve();
      expect(requestorSubscription.closed).toBeTrue();
    });

    it('should complete the requestor\'s Observable when the replier\'s platform is shut down', async () => {
      await MicrofrontendPlatform.startHost({
        host: {
          manifest: {name: 'Host', intentions: [{type: 'capability'}]},
        },
        applications: [
          {
            symbolicName: 'replier',
            manifestUrl: new ManifestFixture({name: 'Replier', capabilities: [{type: 'capability', private: false}]}).serve(),
          },
        ],
      });

      // Mount the replier microfrontend.
      const microfrontendFixture = registerFixture(new MicrofrontendFixture());
      await microfrontendFixture.insertIframe().loadScript('./lib/client/messaging/message-handler.script.ts', 'connectToHostThenIntentClientOnIntent', {symbolicName: 'replier'});

      // Initiate request-reply communication.
      const replyCaptor = new ObserveCaptor();
      Beans.get(IntentClient).request$({type: 'capability'}).subscribe(replyCaptor);

      // Wait until the replier has subscribed to the request.
      await replyCaptor.waitUntilEmitCount(1);
      expect(replyCaptor.hasCompleted()).withContext('hasCompleted').toBeFalse();

      // Destroy the replier microfrontend.
      microfrontendFixture.removeIframe();

      // Wait until the replier has been destroyed.
      await replyCaptor.waitUntilCompletedOrErrored();

      // Expect the requestor's Observable to be completed.
      expect(replyCaptor.hasCompleted()).withContext('hasCompleted').toBeTrue();
    });
  });

  /**
   * Registers the fixture for destruction after test execution.
   */
  function registerFixture(fixture: MicrofrontendFixture): MicrofrontendFixture {
    disposables.add(() => fixture.removeIframe());
    return fixture;
  }
});