A RxJS takeWhen operator

If you needed to poll a service and then take the response only when a specific property of the response was equal to a predicate, then the following RxJS operator might help you.

function takeWhen<T>(predicate: (value: T, index: number) => boolean) {
  return (source: Observable<T>): Observable<T> => source.pipe(filter(predicate), take(1));
}

Example:

interval(500).pipe(
  concatMap(() => this.http.post(...)),
  takeWhen((res: IRpcBgResponse) => {
    return res.running === false;
  })
);

Dynamically inject routes in lazy loaded modules using Angular 11

If you would like to dynamically inject routes in lazy loaded modules in your Angular 11 app, then you need to do the following.

Make sure your service that is responsible for loading the route configuration via HTTP from the server is loading while the app is bootstrapped. This way we can be sure the route configuration is available after the app is bootstrapped.

const routes: Routes = [
  ...,
  { path: '**', redirectTo: '/404' }
];

@NgModule({
  exports: [RouterModule],
  imports: [
    RouterModule.forRoot(routes, {
      useHash: true
    })
  ],
  providers: [
    RouteConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: (routeConfigService: RouteConfigService) => {
        return () => {
          return routeConfigService.load().toPromise();
        };
      },
      multi: true,
      deps: [RouteConfigService]
    }
  ]
})
export class AppRoutingModule {}

You also need to ensure that your service is a singleton across the whole app.

@NgModule({
  declarations: [],
  imports: [...],
  exports: [...]
})
export class CoreModule {
  static forRoot(): ModuleWithProviders<CoreModule> {
    return {
      ngModule: CoreModule,
      providers: [
        RouteConfigService
      ]
    };
  }
}
@NgModule({
  declarations: [AppComponent],
  imports: [
    ...
    CoreModule.forRoot(),
    AppRoutingModule
  ],
  providers: [...],
  bootstrap: [AppComponent]
})
export class AppModule {}

And finally append the following provider in your lazy loaded module.

const routes: Routes = [
  ...
];

@NgModule({
  exports: [RouterModule],
  imports: [RouterModule.forChild(routes)],
  providers: [
    {
      provide: ROUTES,
      multi: true,
      useFactory: (routeConfigService: RouteConfigService) => {
        routeConfigService.inject('someArgsToIdentifyWhatToInject', routes);
        return routes;
      },
      deps: [RouteConfigService]
    }
  ]
})
export class MyLayzRoutingModule {}
@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...,
    MyLayzRoutingModule
  ]
})
export class LazyModule {}

PAM feature request for PHP8

Today i created a feature request for PHP8 because i fear that the new upcoming major release will not support PAM anymore. With PHP7 this is already a pain because the PAM PECL extension needs to be patched to get it working. I fear this workaround will become a dead end for PHP8. This will be a major issue because OMV is requiring this feature. And i don’t think OMV is the only one that wants to use PAM for authentication. IMO this is an essential requirement for a programming language that is used in professional applications.

Hopefully this issue will become a broader attention due this blog post and feature request to increase the possibility to fully integrate PAM support in PHP8. If you want to support that please feel free to add a comment to the feature request.

concatJoin RxJS operator

Are you searching for a RxJS concat operator that works like forkJoin? Then have a look at the following simple code that will do that for you.

import { concat, EMPTY, Observable } from 'rxjs';

/**
 * Joins last values emitted by passed Observables.
 *
 * `concatJoin` is an operator that takes any number of Observables which
 * can be passed either as an array or directly as arguments. If no input
 * Observables are provided, resulting stream will complete immediately.
 *
 * `concatJoin` will wait for all passed Observables to complete one by
 * one and then it will emit an array with last values from corresponding
 * Observables. So if you pass `n` Observables to the operator, resulting
 * array will have `n` values, where first value is the last thing emitted
 * by the first Observable, second value is the last thing emitted by the
 * second Observable and so on. That means `concatJoin` will not emit more
 * than once and it will complete after that.
 *
 * @param {...Observable} sources Any number of Observables provided either
 * as an array or as an arguments passed directly to the operator.
 * @return {Observable} Observable emitting either an array of last values
 * emitted by passed Observables.
 */
export function concatJoin<T>(...sources: Array<Observable<T> | Observable<T>[]>): Observable<T[]> {
  // If the first and only other argument is an array assume it's been
  // called with `concatJoin([obs1, obs2, obs3])`
  if (sources.length === 1 && Array.isArray(sources[0])) {
    sources = sources[0] as Array<Observable<T>>;
  }

  if (sources.length === 0) {
    return EMPTY;
  }

  return new Observable((subscriber) => {
    const values: Array<T> = [];
    concat(...sources).subscribe(
      (value: T) => {
        values.push(value);
      },
      (err) => {
        subscriber.error(err);
      },
      () => {
        subscriber.next(values);
        subscriber.complete();
      }
    );
  });
}

Force Salt service.running state to restart the service

If you want to restart a service using salt.states.service.running and you can’t use watch because there is simply nothing to watch for changes, then use the following code:

restart_phpfpm_service:
  # Force service.running to always restart the service.
  test.succeed_with_changes:
    - watch_in:
      - service: restart_phpfpm_service
  service.running:
    - name: php7.0-fpm
    - enable: True