mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
Merge pull request #2649 from Start9Labs/cyclic-dep
feat: get rid of cyclic dep between patch-db and api service
This commit is contained in:
@@ -28,7 +28,6 @@ import { PreloaderModule } from './app/preloader/preloader.module'
|
|||||||
import { FooterModule } from './app/footer/footer.module'
|
import { FooterModule } from './app/footer/footer.module'
|
||||||
import { MenuModule } from './app/menu/menu.module'
|
import { MenuModule } from './app/menu/menu.module'
|
||||||
import { APP_PROVIDERS } from './app.providers'
|
import { APP_PROVIDERS } from './app.providers'
|
||||||
import { PatchDbModule } from './services/patch-db/patch-db.module'
|
|
||||||
import { ToastContainerModule } from './components/toast-container/toast-container.module'
|
import { ToastContainerModule } from './components/toast-container/toast-container.module'
|
||||||
import { ConnectionBarComponentModule } from './components/connection-bar/connection-bar.component.module'
|
import { ConnectionBarComponentModule } from './components/connection-bar/connection-bar.component.module'
|
||||||
import { WidgetsPageModule } from './pages/widgets/widgets.module'
|
import { WidgetsPageModule } from './pages/widgets/widgets.module'
|
||||||
@@ -54,7 +53,6 @@ import { environment } from '../environments/environment'
|
|||||||
MonacoEditorModule,
|
MonacoEditorModule,
|
||||||
SharedPipesModule,
|
SharedPipesModule,
|
||||||
MarketplaceModule,
|
MarketplaceModule,
|
||||||
PatchDbModule,
|
|
||||||
ToastContainerModule,
|
ToastContainerModule,
|
||||||
ConnectionBarComponentModule,
|
ConnectionBarComponentModule,
|
||||||
TuiRootModule,
|
TuiRootModule,
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import { UntypedFormBuilder } from '@angular/forms'
|
|||||||
import { Router, RouteReuseStrategy } from '@angular/router'
|
import { Router, RouteReuseStrategy } from '@angular/router'
|
||||||
import { IonicRouteStrategy, IonNav } from '@ionic/angular'
|
import { IonicRouteStrategy, IonNav } from '@ionic/angular'
|
||||||
import { RELATIVE_URL, THEME, WorkspaceConfig } from '@start9labs/shared'
|
import { RELATIVE_URL, THEME, WorkspaceConfig } from '@start9labs/shared'
|
||||||
|
import { PatchDB } from 'patch-db-client'
|
||||||
|
import {
|
||||||
|
PATCH_CACHE,
|
||||||
|
PatchDbSource,
|
||||||
|
} from 'src/app/services/patch-db/patch-db-source'
|
||||||
import { ApiService } from './services/api/embassy-api.service'
|
import { ApiService } from './services/api/embassy-api.service'
|
||||||
import { MockApiService } from './services/api/embassy-mock-api.service'
|
import { MockApiService } from './services/api/embassy-mock-api.service'
|
||||||
import { LiveApiService } from './services/api/embassy-live-api.service'
|
import { LiveApiService } from './services/api/embassy-live-api.service'
|
||||||
@@ -29,6 +34,11 @@ export const APP_PROVIDERS: Provider[] = [
|
|||||||
provide: ApiService,
|
provide: ApiService,
|
||||||
useClass: useMocks ? MockApiService : LiveApiService,
|
useClass: useMocks ? MockApiService : LiveApiService,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: PatchDB,
|
||||||
|
deps: [PatchDbSource, PATCH_CACHE],
|
||||||
|
useClass: PatchDB,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
deps: [StorageService, AuthService, ClientStorageService, Router],
|
deps: [StorageService, AuthService, ClientStorageService, Router],
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
RpcError,
|
RpcError,
|
||||||
RPCOptions,
|
RPCOptions,
|
||||||
} from '@start9labs/shared'
|
} from '@start9labs/shared'
|
||||||
|
import { PATCH_CACHE } from 'src/app/services/patch-db/patch-db-source'
|
||||||
import { ApiService } from './embassy-api.service'
|
import { ApiService } from './embassy-api.service'
|
||||||
import { RR } from './api.types'
|
import { RR } from './api.types'
|
||||||
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
|
||||||
@@ -16,7 +17,7 @@ import { Observable, filter, firstValueFrom } from 'rxjs'
|
|||||||
import { AuthService } from '../auth.service'
|
import { AuthService } from '../auth.service'
|
||||||
import { DOCUMENT } from '@angular/common'
|
import { DOCUMENT } from '@angular/common'
|
||||||
import { DataModel } from '../patch-db/data-model'
|
import { DataModel } from '../patch-db/data-model'
|
||||||
import { PatchDB, pathFromArray } from 'patch-db-client'
|
import { Dump, pathFromArray } from 'patch-db-client'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LiveApiService extends ApiService {
|
export class LiveApiService extends ApiService {
|
||||||
@@ -25,7 +26,7 @@ export class LiveApiService extends ApiService {
|
|||||||
private readonly http: HttpService,
|
private readonly http: HttpService,
|
||||||
private readonly config: ConfigService,
|
private readonly config: ConfigService,
|
||||||
private readonly auth: AuthService,
|
private readonly auth: AuthService,
|
||||||
private readonly patch: PatchDB<DataModel>,
|
@Inject(PATCH_CACHE) private readonly cache$: Observable<Dump<DataModel>>,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
; (window as any).rpcClient = this
|
; (window as any).rpcClient = this
|
||||||
@@ -482,7 +483,7 @@ export class LiveApiService extends ApiService {
|
|||||||
const patchSequence = res.headers.get('x-patch-sequence')
|
const patchSequence = res.headers.get('x-patch-sequence')
|
||||||
if (patchSequence)
|
if (patchSequence)
|
||||||
await firstValueFrom(
|
await firstValueFrom(
|
||||||
this.patch.cache$.pipe(filter(({ id }) => id >= Number(patchSequence))),
|
this.cache$.pipe(filter(({ id }) => id >= Number(patchSequence))),
|
||||||
)
|
)
|
||||||
|
|
||||||
return body.result
|
return body.result
|
||||||
|
|||||||
56
web/projects/ui/src/app/services/patch-db/patch-db-source.ts
Normal file
56
web/projects/ui/src/app/services/patch-db/patch-db-source.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { inject, Injectable, InjectionToken } from '@angular/core'
|
||||||
|
import { Dump, Revision, Update } from 'patch-db-client'
|
||||||
|
import { BehaviorSubject, EMPTY, Observable } from 'rxjs'
|
||||||
|
import {
|
||||||
|
bufferTime,
|
||||||
|
catchError,
|
||||||
|
filter,
|
||||||
|
startWith,
|
||||||
|
switchMap,
|
||||||
|
take,
|
||||||
|
} from 'rxjs/operators'
|
||||||
|
import { StateService } from 'src/app/services/state.service'
|
||||||
|
import { ApiService } from '../api/embassy-api.service'
|
||||||
|
import { AuthService } from '../auth.service'
|
||||||
|
import { DataModel } from './data-model'
|
||||||
|
import { LocalStorageBootstrap } from './local-storage-bootstrap'
|
||||||
|
|
||||||
|
export const PATCH_CACHE = new InjectionToken('', {
|
||||||
|
factory: () =>
|
||||||
|
new BehaviorSubject<Dump<DataModel>>({
|
||||||
|
id: 0,
|
||||||
|
value: {} as DataModel,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PatchDbSource extends Observable<Update<DataModel>[]> {
|
||||||
|
private readonly api = inject(ApiService)
|
||||||
|
private readonly state = inject(StateService)
|
||||||
|
private readonly stream$ = inject(AuthService).isVerified$.pipe(
|
||||||
|
switchMap(verified => (verified ? this.api.subscribeToPatchDB({}) : EMPTY)),
|
||||||
|
switchMap(({ dump, guid }) =>
|
||||||
|
this.api.openWebsocket$<Revision>(guid, {}).pipe(
|
||||||
|
bufferTime(250),
|
||||||
|
filter(revisions => !!revisions.length),
|
||||||
|
startWith([dump]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
catchError((_, original$) => {
|
||||||
|
this.state.retrigger()
|
||||||
|
|
||||||
|
return this.state.pipe(
|
||||||
|
filter(current => current === 'running'),
|
||||||
|
take(1),
|
||||||
|
switchMap(() => original$),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
startWith([inject(LocalStorageBootstrap).init()]),
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(subscriber => this.stream$.subscribe(subscriber))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import { InjectionToken, Injector } from '@angular/core'
|
|
||||||
import { Revision, Update } from 'patch-db-client'
|
|
||||||
import { defer, EMPTY, from, Observable } from 'rxjs'
|
|
||||||
import {
|
|
||||||
bufferTime,
|
|
||||||
catchError,
|
|
||||||
filter,
|
|
||||||
startWith,
|
|
||||||
switchMap,
|
|
||||||
take,
|
|
||||||
} from 'rxjs/operators'
|
|
||||||
import { StateService } from 'src/app/services/state.service'
|
|
||||||
import { ApiService } from '../api/embassy-api.service'
|
|
||||||
import { AuthService } from '../auth.service'
|
|
||||||
import { DataModel } from './data-model'
|
|
||||||
import { LocalStorageBootstrap } from './local-storage-bootstrap'
|
|
||||||
|
|
||||||
export const PATCH_SOURCE = new InjectionToken<Observable<Update<DataModel>[]>>(
|
|
||||||
'',
|
|
||||||
)
|
|
||||||
|
|
||||||
export function sourceFactory(
|
|
||||||
injector: Injector,
|
|
||||||
): Observable<Update<DataModel>[]> {
|
|
||||||
// defer() needed to avoid circular dependency with ApiService, since PatchDB is needed there
|
|
||||||
return defer(() => {
|
|
||||||
const api = injector.get(ApiService)
|
|
||||||
const auth = injector.get(AuthService)
|
|
||||||
const state = injector.get(StateService)
|
|
||||||
const bootstrapper = injector.get(LocalStorageBootstrap)
|
|
||||||
|
|
||||||
return auth.isVerified$.pipe(
|
|
||||||
switchMap(verified =>
|
|
||||||
verified ? from(api.subscribeToPatchDB({})) : EMPTY,
|
|
||||||
),
|
|
||||||
switchMap(({ dump, guid }) =>
|
|
||||||
api.openWebsocket$<Revision>(guid, {}).pipe(
|
|
||||||
bufferTime(250),
|
|
||||||
filter(revisions => !!revisions.length),
|
|
||||||
startWith([dump]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
catchError((_, original$) => {
|
|
||||||
state.retrigger()
|
|
||||||
|
|
||||||
return state.pipe(
|
|
||||||
filter(current => current === 'running'),
|
|
||||||
take(1),
|
|
||||||
switchMap(() => original$),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
startWith([bootstrapper.init()]),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { PatchDB } from 'patch-db-client'
|
|
||||||
import { Injector, NgModule } from '@angular/core'
|
|
||||||
import { PATCH_SOURCE, sourceFactory } from './patch-db.factory'
|
|
||||||
|
|
||||||
// This module is purely for providers organization purposes
|
|
||||||
@NgModule({
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: PATCH_SOURCE,
|
|
||||||
deps: [Injector],
|
|
||||||
useFactory: sourceFactory,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: PatchDB,
|
|
||||||
deps: [PATCH_SOURCE],
|
|
||||||
useClass: PatchDB,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class PatchDbModule {}
|
|
||||||
Reference in New Issue
Block a user