rxjs#timeout TypeScript Examples

The following examples show how to use rxjs#timeout. 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: activator-installer.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 6 votes vote down vote up
/**
   * Creates a Promise that resolves when given activators signal ready.
   */
  private async waitForActivatorsToSignalReady(appSymbolicName: string, activators: Activator[], monitor: ProgressMonitor): Promise<void> {
    const t0 = Date.now();
    const activatorLoadTimeout = Beans.get(ApplicationRegistry).getApplication(appSymbolicName)!.activatorLoadTimeout;
    const readinessPromises: Promise<void>[] = activators
      .reduce((acc, activator) => acc.concat(Arrays.coerce(activator.properties.readinessTopics)), new Array<string>()) // concat readiness topics
      .map(readinessTopic => {
          const onReadinessTimeout = (): Observable<never> => {
            Beans.get(Logger).error(`[ActivatorLoadTimeoutError] Timeout elapsed while waiting for application to signal readiness [app=${appSymbolicName}, timeout=${activatorLoadTimeout}ms, readinessTopic=${readinessTopic}].`);
            return EMPTY;
          };
          return new Promise((resolve, reject) => {
            return Beans.get(MessageClient).observe$<void>(readinessTopic)
              .pipe(
                first(msg => msg.headers.get(MessageHeaders.AppSymbolicName) === appSymbolicName),
                activatorLoadTimeout ? timeout({first: activatorLoadTimeout, with: onReadinessTimeout}) : identity,
              )
              .subscribe({
                error: reject,
                complete: resolve,
              });
          });
        },
      );

    if (!readinessPromises.length) {
      monitor.done();
      return;
    }

    await Promise.all(readinessPromises);
    monitor.done();
    Beans.get(Logger).info(`Activator startup of '${appSymbolicName}' took ${Date.now() - t0}ms.`);
  }
Example #2
Source File: manifest-collector.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 6 votes vote down vote up
private async fetchAndRegisterManifest(appConfig: ApplicationConfig, monitor: ProgressMonitor): Promise<void> {
    try {
      if (!appConfig.manifestUrl) {
        Beans.get(Logger).error(`[AppConfigError] Failed to fetch manifest for application '${appConfig.symbolicName}'. Manifest URL must not be empty.`);
        return;
      }

      const fetchManifest$ = from(Beans.get(HttpClient).fetch(appConfig.manifestUrl));
      const manifestFetchTimeout = appConfig.manifestLoadTimeout ?? Beans.get(MicrofrontendPlatformConfig).manifestLoadTimeout;
      const onManifestFetchTimeout = (): Observable<never> => throwError(() => `Timeout of ${manifestFetchTimeout}ms elapsed.`);
      const manifestFetchResponse = await firstValueFrom(fetchManifest$.pipe(manifestFetchTimeout ? timeout({first: manifestFetchTimeout, with: onManifestFetchTimeout}) : identity));

      if (!manifestFetchResponse.ok) {
        Beans.get(Logger).error(`[ManifestFetchError] Failed to fetch manifest for application '${appConfig.symbolicName}'. Maybe the application is currently unavailable. [httpStatusCode=${manifestFetchResponse.status}, httpStatusText=${manifestFetchResponse.statusText}]`, appConfig, manifestFetchResponse.status);
        return;
      }

      const manifest: Manifest = await manifestFetchResponse.json();

      // Let the host manifest be intercepted before registering it in the platform, for example by libraries integrating the SCION Microfrontend Platform, e.g., to allow the programmatic registration of capabilities or intentions.
      if (appConfig.symbolicName === Beans.get<string>(APP_IDENTITY)) {
        Beans.all(HostManifestInterceptor).forEach(interceptor => interceptor.intercept(manifest));
      }

      Beans.get(ApplicationRegistry).registerApplication(appConfig, manifest);
      Beans.get(Logger).info(`Registered application '${appConfig.symbolicName}' in the SCION Microfrontend Platform.`);
    }
    catch (error) {
      // The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500.
      // It will only reject on network failure or if anything prevented the request from completing.
      // See https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
      Beans.get(Logger).error(`[ManifestFetchError] Failed to fetch manifest for application '${appConfig.symbolicName}'. Maybe the application is currently unavailable.`, error);
    }
    finally {
      monitor.done();
    }
  }
Example #3
Source File: broker-gateway.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 5 votes vote down vote up
public async postMessage(channel: MessagingChannel, message: Message): Promise<void> {
    if (isPlatformStopped()) {
      throw GatewayErrors.PLATFORM_STOPPED_ERROR;
    }

    // If not connected to the broker, wait until connected. If connected, continue execution immediately
    // without spawning a microtask. Otherwise, messages cannot be published during platform shutdown.
    const brokerInfo = this._brokerInfo || await lastValueFrom(this._brokerInfo$);

    const messageId = UUID.randomUUID();
    const envelope: MessageEnvelope = {
      transport: MessagingTransport.ClientToBroker,
      channel: channel,
      message: message,
    };
    envelope.message.headers
      .set(MessageHeaders.MessageId, messageId)
      .set(MessageHeaders.Timestamp, Date.now())
      .set(MessageHeaders.AppSymbolicName, this._appSymbolicName)
      .set(MessageHeaders.ClientId, brokerInfo.clientId);

    // Install Promise that resolves once the broker has acknowledged the message, or that rejects otherwise.
    const postError$ = new Subject<never>();
    const whenPosted = new Promise<void>((resolve, reject) => {
      merge(this.message$, postError$)
        .pipe(
          filterByTopicChannel<MessageDeliveryStatus>(messageId),
          take(1),
          pluckMessage(),
          timeout({first: this._messageDeliveryTimeout, with: () => throwError(() => GatewayErrors.MESSAGE_DISPATCH_ERROR(this._messageDeliveryTimeout, envelope))}),
          mergeMap(statusMessage => statusMessage.body!.ok ? EMPTY : throwError(() => statusMessage.body!.details)),
          takeUntil(this._platformStopping$),
        )
        .subscribe({
          error: reject,
          complete: resolve,
        });
    });

    try {
      brokerInfo.window.postMessage(envelope, brokerInfo.origin);
    }
    catch (error) {
      postError$.error(error);
    }

    await whenPosted;
  }
Example #4
Source File: broker-gateway.ts    From scion-microfrontend-platform with Eclipse Public License 2.0 5 votes vote down vote up
/**
   * Connects this client to the broker by sending a CONNECT message to the current and all parent windows.
   *
   * When the broker receives the CONNECT message and trusts this client, the broker responds with a CONNACK message,
   * or rejects the connect attempt otherwise.
   *
   * @return A Promise that, when connected, resolves to information about the broker, or that rejects if the connect attempt
   * failed, either because the broker could not be found or because the application is not allowed to connect.
   */
  public connectToBroker(): Promise<BrokerInfo> {
    const replyTo = UUID.randomUUID();
    const connectPromise = firstValueFrom(fromEvent<MessageEvent>(window, 'message')
      .pipe(
        filterByTransport(MessagingTransport.BrokerToClient),
        filterByTopicChannel<ConnackMessage>(replyTo),
        mergeMap((messageEvent: MessageEvent<MessageEnvelope<TopicMessage<ConnackMessage>>>) => {
          const response: ConnackMessage | undefined = messageEvent.data.message.body;
          if (response?.returnCode !== 'accepted') {
            return throwError(() => `${response?.returnMessage ?? 'UNEXPECTED: Empty broker discovery response'} [code: '${response?.returnCode ?? 'n/a'}']`);
          }
          return of<BrokerInfo>({
            clientId: response.clientId!,
            heartbeatInterval: response.heartbeatInterval!,
            window: messageEvent.source as Window,
            origin: messageEvent.origin,
          });
        }),
        timeout({first: this._brokerDiscoverTimeout, with: () => throwError(() => GatewayErrors.BROKER_DISCOVER_ERROR(this._brokerDiscoverTimeout))}),
        tap({error: error => Beans.get(Logger, {orElseGet: NULL_LOGGER}).error(stringifyError(error))}), // Fall back using NULL_LOGGER, e.g., when the platform is stopping.
        takeUntil(this._platformStopping$),
      ));

    const connectMessage: MessageEnvelope = {
      transport: MessagingTransport.ClientToBroker,
      channel: MessagingChannel.ClientConnect,
      message: {
        headers: new Map()
          .set(MessageHeaders.MessageId, UUID.randomUUID())
          .set(MessageHeaders.Timestamp, Date.now())
          .set(MessageHeaders.AppSymbolicName, this._appSymbolicName)
          .set(MessageHeaders.ReplyTo, replyTo)
          .set(MessageHeaders.Version, Beans.get(VERSION)),
      },
    };

    const windowHierarchy = this.collectWindowHierarchy();
    timer(0, CONNECT_INTERVAL)
      .pipe(takeUntil(connectPromise.catch(() => null)))
      .subscribe(() => {
        windowHierarchy.forEach(window => window.postMessage(connectMessage, '*'));
      });

    return connectPromise;
  }