update bootstrapper and startup flow

This commit is contained in:
Matt Hill
2021-06-16 18:02:06 -06:00
committed by Aiden McClelland
parent f364202ae9
commit 12c44565ff
12 changed files with 114 additions and 84 deletions

34
ui/package-lock.json generated
View File

@@ -27,7 +27,7 @@
"json-pointer": "^0.6.1",
"jsonpointerx": "^1.0.30",
"marked": "^2.0.0",
"patch-db-client": "file: ../../../../patch-db-client",
"patch-db-client": "file: ../../../../patch-db/client",
"rxjs": "^6.6.0",
"uuid": "^8.3.0",
"zone.js": "^0.11.2"
@@ -50,7 +50,23 @@
"typescript": "4.1.5"
}
},
"../../patch-db-client": {},
"../../patch-db/client": {
"name": "patch-db",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"rxjs": "^6.6.3",
"sorted-btree": "^1.5.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/node": "^15.0.0",
"@types/uuid": "^8.3.0",
"ts-node": "^9.1.1",
"tslint": "^6.1.0",
"typescript": "4.1.5"
}
},
"node_modules/@angular-devkit/architect": {
"version": "0.1102.14",
"dev": true,
@@ -9816,7 +9832,7 @@
}
},
"node_modules/patch-db-client": {
"resolved": "../../patch-db-client",
"resolved": "../../patch-db/client",
"link": true
},
"node_modules/path-browserify": {
@@ -22163,7 +22179,17 @@
"dev": true
},
"patch-db-client": {
"version": "file:../../patch-db-client"
"version": "file:../../patch-db/client",
"requires": {
"@types/node": "^15.0.0",
"@types/uuid": "^8.3.0",
"rxjs": "^6.6.3",
"sorted-btree": "^1.5.0",
"ts-node": "^9.1.1",
"tslint": "^6.1.0",
"typescript": "4.1.5",
"uuid": "^8.3.2"
}
},
"path-browserify": {
"version": "0.0.1",

View File

@@ -34,7 +34,7 @@
"json-pointer": "^0.6.1",
"jsonpointerx": "^1.0.30",
"marked": "^2.0.0",
"patch-db-client": "file: ../../../../patch-db-client",
"patch-db-client": "file: ../../../../patch-db/client",
"rxjs": "^6.6.0",
"uuid": "^8.3.0",
"zone.js": "^0.11.2"

View File

@@ -1,5 +1,8 @@
<ion-app *ngIf="patch.initialized">
<ion-split-pane [disabled]="!showMenu" (ionSplitPaneVisible)="splitPaneVisible($event)" contentId="main-content">
<ion-app>
<ion-spinner *ngIf="!patch.initialized" class="center" name="lines" color="warning"></ion-spinner>
<ion-split-pane *ngIf="patch.initialized" [disabled]="!showMenu" (ionSplitPaneVisible)="splitPaneVisible($event)" contentId="main-content">
<ion-menu contentId="main-content" type="overlay">
<ion-header>
<ion-toolbar style="--background: var(--ion-background-color);">

View File

@@ -68,9 +68,9 @@ export class AppComponent {
async init () {
await this.storage.create()
await this.patch.init()
await this.authService.init()
await this.emver.init()
await this.patch.init()
this.router.initialNavigation()

View File

@@ -2,6 +2,7 @@ import { Bootstrapper, DBCache } from 'patch-db-client'
import { DataModel } from './data-model'
import { Injectable } from '@angular/core'
import { Storage } from '@ionic/storage'
import { ApiService } from 'src/app/services/api/api.service'
@Injectable({
providedIn: 'root',
@@ -11,19 +12,22 @@ export class LocalStorageBootstrap implements Bootstrapper<DataModel> {
constructor (
private readonly storage: Storage,
private readonly apiService: ApiService,
) { }
async init (): Promise<DBCache<DataModel>> {
const cache = await this.storage.get(LocalStorageBootstrap.CONTENT_KEY)
if (!cache) return { sequence: 0, data: { } as DataModel }
let cache = await this.storage.get(LocalStorageBootstrap.CONTENT_KEY) as DBCache<DataModel>
if (!cache || cache.sequence === 0) {
console.log('No cached data, getting dump from server')
const { id, value } = await this.apiService.getDump()
cache.sequence = id
cache.data = value
await this.update(cache)
}
return cache
}
async update (cache: DBCache<DataModel>): Promise<void> {
return this.storage.set(LocalStorageBootstrap.CONTENT_KEY, cache)
}
async clear (): Promise<void> {
return this.storage.remove(LocalStorageBootstrap.CONTENT_KEY)
}
}

View File

@@ -11,7 +11,7 @@ export function PatchDbModelFactory (
http: ApiService,
): PatchDbModel {
const { mocks, patchDb: { poll, timeoutForMissingRevision }, isConsulate, isLan } = config
const { mocks, patchDb: { poll, timeoutForMissingRevision }, isConsulate } = config
let source: Source<DataModel>
@@ -19,7 +19,7 @@ export function PatchDbModelFactory (
if (mocks.connection === 'poll') {
source = new PollSource({ ...poll }, http)
} else {
source = new WebsocketSource('ws://localhost:8081')
source = new WebsocketSource(`ws://localhost:${config.mocks.wsPort}/db`)
}
} else {
if (isConsulate) {

View File

@@ -20,8 +20,12 @@ export class PatchDbModel {
async init (): Promise<void> {
if (this.patchDb) return console.warn('Cannot re-init patchDbModel')
this.patchDb = await PatchDB.init<DataModel>(this.conf)
this.initialized = true
try {
this.patchDb = await PatchDB.init<DataModel>(this.conf)
this.initialized = true
} catch (e) {
console.log('Failed to initialize PatchDB', e)
}
}
start (): void {

View File

@@ -32,12 +32,13 @@
</div>
</div>
<img style="position: absolute" class="main-img" [src]="pkg.value['static-files'].icon" [alt]="icon" />
<img style="position: absolute" class="main-img" [src]="pkg.value['static-files'].icon" alt="icon" />
<img class="main-img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'green' : connection" src="assets/img/running-bulb.png"/>
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'red' : connection" src="assets/img/issue-bulb.png"/>
<img class="bulb-on" *ngIf="pkg.value | displayBulb: 'yellow' : connection" src="assets/img/warning-bulb.png"/>
<img class="bulb-off" *ngIf="pkg.value | displayBulb: 'off' : connection" src="assets/img/off-bulb.png"/>
<ion-card-header>
<status [pkg]="pkg.value" [connection]="connection" size="small"></status>
<p>{{ (pkg.value | manifest).title }}</p>

View File

@@ -8,6 +8,7 @@ import { RR } from './api-types'
import { parsePropertiesPermissive } from 'src/app/util/properties.util'
import { Mock } from './mock-app-fixures'
import { HttpService } from '../http.service'
import { ConfigService } from '../config.service'
@Injectable()
export class MockApiService extends ApiService {
@@ -36,40 +37,40 @@ export class MockApiService extends ApiService {
// db
async getRevisions (since: number): Promise<RR.GetRevisionsRes> {
await pauseFor(2000)
return {
...Mock.DbDump,
id: this.nextSequence(),
}
// return this.http.rpcRequest({ method: 'db.revisions', params: { since } })
// await pauseFor(2000)
// return {
// ...Mock.DbDump,
// id: this.nextSequence(),
// }
return this.http.rpcRequest({ method: 'db.revisions', params: { since } })
}
async getDump (): Promise<RR.GetDumpRes> {
await pauseFor(2000)
return {
...Mock.DbDump,
id: this.nextSequence(),
}
// return this.http.rpcRequest({ method: 'db.dump' })
// await pauseFor(2000)
// return {
// ...Mock.DbDump,
// id: this.nextSequence(),
// }
return this.http.rpcRequest({ method: 'db.dump' })
}
async setDbValueRaw (params: RR.SetDBValueReq): Promise<RR.SetDBValueRes> {
await pauseFor(2000)
return {
response: null,
revision: {
id: this.nextSequence(),
patch: [
{
op: PatchOp.REPLACE,
path: params.pointer,
value: params.value,
},
],
expireId: null,
},
}
// return this.http.rpcRequest({ method: 'db.put.ui', params })
// await pauseFor(2000)
// return {
// response: null,
// revision: {
// id: this.nextSequence(),
// patch: [
// {
// op: PatchOp.REPLACE,
// path: params.pointer,
// value: params.value,
// },
// ],
// expireId: null,
// },
// }
return this.http.rpcRequest({ method: 'db.put.ui', params })
}
// auth

View File

@@ -19,19 +19,11 @@ export class AuthService {
constructor (
private readonly api: ApiService,
private readonly storage: Storage,
) {
this.storage.create()
}
) { }
async init (): Promise<AuthState> {
async init (): Promise<void> {
const loggedIn = await this.storage.get(StorageKeys.LOGGED_IN_KEY)
if (loggedIn) {
this.authState$.next(AuthState.VERIFIED)
return AuthState.VERIFIED
} else {
this.authState$.next(AuthState.UNVERIFIED)
return AuthState.UNVERIFIED
}
this.authState$.next( loggedIn ? AuthState.VERIFIED : AuthState.UNVERIFIED)
}
watch$ (): Observable<AuthState> {

View File

@@ -11,14 +11,15 @@ import { Revision } from 'patch-db-client'
export class HttpService {
private unauthorizedApiResponse$ = new Subject()
authReqEnabled: boolean = false
rootUrl: string
fullUrl: string
constructor (
private readonly http: HttpClient,
private readonly config: ConfigService,
) {
const { url, version } = this.config.api
this.rootUrl = `${url}/${version}`
const port = config.mocks.enabled ? this.config.mocks.rpcPort : window.location.port
this.fullUrl = `${window.location.protocol}//${window.location.hostname}:${port}/${url}/${version}`
}
watch401$ (): Observable<{ }> {
@@ -41,16 +42,14 @@ export class HttpService {
}
async httpRequest<T> (httpOpts: HttpOptions): Promise<T> {
let { url, body, timeout, ...rest} = translateOptions(httpOpts)
url = this.rootUrl + url
let { body, timeout, ...rest} = this.translateOptions(httpOpts)
let req: Observable<{ body: T }>
switch (httpOpts.method){
case Method.GET: req = this.http.get(url, rest) as any; break
case Method.POST: req = this.http.post(url, body, rest) as any; break
case Method.PUT: req = this.http.put(url, body, rest) as any; break
case Method.PATCH: req = this.http.patch(url, body, rest) as any; break
case Method.DELETE: req = this.http.delete(url, rest) as any; break
case Method.GET: req = this.http.get(this.fullUrl, rest) as any; break
case Method.POST: req = this.http.post(this.fullUrl, body, rest) as any; break
case Method.PUT: req = this.http.put(this.fullUrl, body, rest) as any; break
case Method.PATCH: req = this.http.patch(this.fullUrl, body, rest) as any; break
case Method.DELETE: req = this.http.delete(this.fullUrl, rest) as any; break
}
return (timeout ? withTimeout(req, timeout) : req)
@@ -58,6 +57,20 @@ export class HttpService {
.then(res => res.body)
.catch(e => { throw new HttpError(e) })
}
translateOptions (httpOpts: HttpOptions): HttpJsonOptions {
return {
observe: 'events',
responseType: 'json',
reportProgress: false,
withCredentials: this.config.mocks.enabled ? false : true,
headers: httpOpts.headers,
params: httpOpts.params,
body: httpOpts.data || { },
url: httpOpts.url,
timeout: httpOpts.readTimeout,
}
}
}
function RpcError (e: RPCError['error']): void {
@@ -167,20 +180,6 @@ export interface HttpJsonOptions {
timeout: number
}
function translateOptions (httpOpts: HttpOptions): HttpJsonOptions {
return {
observe: 'events',
responseType: 'json',
reportProgress: false,
withCredentials: true,
headers: httpOpts.headers,
params: httpOpts.params,
body: httpOpts.data || { },
url: httpOpts.url,
timeout: httpOpts.readTimeout,
}
}
function withTimeout<U> (req: Observable<U>, timeout: number): Observable<U> {
return race(
from(req.toPromise()), // this guarantees it only emits on completion, intermediary emissions are suppressed.

View File

@@ -2,11 +2,11 @@
"patchDb": {
"timeoutForMissingRevision": 5000,
"poll": {
"cooldown": 40000
"cooldown": 10000
}
},
"api": {
"url": "/rpc",
"url": "rpc",
"version": "v1"
},
"mocks": {