diff --git a/appmgr/src/error.rs b/appmgr/src/error.rs index c884c6b4f..1b62b3615 100644 --- a/appmgr/src/error.rs +++ b/appmgr/src/error.rs @@ -192,7 +192,7 @@ impl From for Error { impl From for RpcError { fn from(e: Error) -> Self { let mut data_object = serde_json::Map::with_capacity(2); - data_object.insert("message".to_owned(), format!("{}", e.source).into()); + data_object.insert("details".to_owned(), format!("{}", e.source).into()); data_object.insert( "revision".to_owned(), match serde_json::to_value(&e.revision) { diff --git a/ui/package.json b/ui/package.json index d95699bba..fa48c14cd 100644 --- a/ui/package.json +++ b/ui/package.json @@ -8,7 +8,7 @@ "ng": "ng", "start": "ng serve", "build": "ng build", - "build-prod": "ng build --prod && tsc postprocess.ts && node postprocess.js && cp client-manifest.yaml www && git log | head -n1 > www/git-hash.txt", + "build-prod": "ng build --prod && tsc postprocess.ts && node postprocess.js && git log | head -n1 > www/git-hash.txt", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 10d19e4cd..b61280f24 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -13,16 +13,13 @@ import { PatchDbServiceFactory } from './services/patch-db/patch-db.factory' import { HttpService } from './services/http.service' import { ConfigService } from './services/config.service' import { QRCodeModule } from 'angularx-qrcode' -import { appConfigComponents } from './modals/app-config-injectable' import { OSWelcomePageModule } from './modals/os-welcome/os-welcome.module' import { MarkdownPageModule } from './modals/markdown/markdown.module' import { PatchDbService } from './services/patch-db/patch-db.service' import { LocalStorageBootstrap } from './services/patch-db/local-storage-bootstrap' import { SharingModule } from './modules/sharing.module' import { MarketplaceApiService } from './services/api/marketplace/marketplace-api.service' -import { APP_CONFIG_COMPONENT_MAPPING } from './services/sub-nav.service' import { FormBuilder } from '@angular/forms' -import { FormService } from './services/form.service' @NgModule({ declarations: [AppComponent], @@ -51,7 +48,6 @@ import { FormService } from './services/form.service' { provide: ApiService , useFactory: ApiServiceFactory, deps: [ConfigService, HttpService] }, { provide: ApiService , useFactory: ApiServiceFactory, deps: [ConfigService, HttpService] }, { provide: MarketplaceApiService , useFactory: MarketplaceApiServiceFactory, deps: [ConfigService, HttpService, PatchDbService] }, { provide: PatchDbService, useFactory: PatchDbServiceFactory, deps: [ConfigService, LocalStorageBootstrap, ApiService] }, - { provide: APP_CONFIG_COMPONENT_MAPPING, useValue: appConfigComponents }, ], bootstrap: [AppComponent], schemas: [ CUSTOM_ELEMENTS_SCHEMA ], diff --git a/ui/src/app/modals/app-config-injectable.ts b/ui/src/app/modals/app-config-injectable.ts deleted file mode 100644 index f5e19519f..000000000 --- a/ui/src/app/modals/app-config-injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { AppConfigObjectPage } from './app-config-object/app-config-object.page' -import { AppConfigListPage } from './app-config-list/app-config-list.page' -import { AppConfigUnionPage } from './app-config-union/app-config-union.page' -import { AppConfigValuePage } from './app-config-value/app-config-value.page' -import { Type } from '@angular/core' -import { ValueType } from 'src/app/pkg-config/config-types' - -export const appConfigComponents: AppConfigComponentMapping = { - 'string': AppConfigValuePage, - 'number': AppConfigValuePage, - 'enum': AppConfigValuePage, - 'boolean': AppConfigValuePage, - 'list': AppConfigListPage, - 'object': AppConfigObjectPage, - 'union': AppConfigUnionPage, - 'pointer': undefined, -} - -export type AppConfigComponentMapping = { [k in ValueType]: Type } diff --git a/ui/src/app/modals/app-config-list/app-config-list.module.ts b/ui/src/app/modals/app-config-list/app-config-list.module.ts deleted file mode 100644 index a8ceacaec..000000000 --- a/ui/src/app/modals/app-config-list/app-config-list.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { AppConfigListPage } from './app-config-list.page' -import { SharingModule } from 'src/app/modules/sharing.module' -import { ConfigHeaderComponentModule } from 'src/app/components/config-header/config-header.component.module' -import { FormsModule } from '@angular/forms' - -@NgModule({ - declarations: [AppConfigListPage], - imports: [ - CommonModule, - IonicModule, - SharingModule, - FormsModule, - ConfigHeaderComponentModule, - ], - entryComponents: [AppConfigListPage], - exports: [AppConfigListPage], -}) -export class AppConfigListPageModule { } \ No newline at end of file diff --git a/ui/src/app/modals/app-config-list/app-config-list.page.html b/ui/src/app/modals/app-config-list/app-config-list.page.html deleted file mode 100644 index 176542c04..000000000 --- a/ui/src/app/modals/app-config-list/app-config-list.page.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - {{ value.length }} selected -  (min: {{ min }}) -  (max: {{ max }}) - {{ selectAll ? 'All' : 'None' }} - - - {{ option.value }} - - - - - -
- - - - {{ value.length }}  - Entry - Entries -  (min: {{ min }}) -  (max: {{ max }}) - - -
- - - - - - - {{ valueString[i] }} - - - - - -
-
-
- -
diff --git a/ui/src/app/modals/app-config-list/app-config-list.page.scss b/ui/src/app/modals/app-config-list/app-config-list.page.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/ui/src/app/modals/app-config-list/app-config-list.page.ts b/ui/src/app/modals/app-config-list/app-config-list.page.ts deleted file mode 100644 index d7a5ed162..000000000 --- a/ui/src/app/modals/app-config-list/app-config-list.page.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Component, Input } from '@angular/core' -import { AlertController, IonNav } from '@ionic/angular' -import { Annotations, Range } from '../../pkg-config/config-utilities' -import { ConfigCursor } from 'src/app/pkg-config/config-cursor' -import { ValueSpecList, isValueSpecListOf } from 'src/app/pkg-config/config-types' -import { SubNavService } from 'src/app/services/sub-nav.service' - -@Component({ - selector: 'app-config-list', - templateUrl: './app-config-list.page.html', - styleUrls: ['./app-config-list.page.scss'], -}) -export class AppConfigListPage { - @Input() cursor: ConfigCursor<'list'> - - spec: ValueSpecList - value: string[] | number[] | object[] - valueString: string[] - annotations: Annotations<'list'> - - // enum only - options: { value: string, checked: boolean }[] = [] - selectAll = true - - min: number | undefined - max: number | undefined - - minMessage: string - maxMessage: string - - error: string - - constructor ( - private readonly alertCtrl: AlertController, - private readonly subNav: SubNavService, - private readonly nav: IonNav, - ) { } - - ngOnInit () { - this.spec = this.cursor.spec() - this.value = this.cursor.config() - const range = Range.from(this.spec.range) - this.min = range.integralMin() - this.max = range.integralMax() - this.minMessage = `The minimum number of ${this.cursor.key()} is ${this.min}.` - this.maxMessage = `The maximum number of ${this.cursor.key()} is ${this.max}.` - // enum list only - if (isValueSpecListOf(this.spec, 'enum')) { - for (let val of this.spec.spec.values) { - this.options.push({ - value: val, - checked: (this.value as string[]).includes(val), - }) - } - } - this.updateCaches() - } - - // enum only - toggleSelectAll () { - if (!isValueSpecListOf(this.spec, 'enum')) { throw new Error('unreachable') } - - this.value.length = 0 - if (this.selectAll) { - for (let v of this.spec.spec.values) { - (this.value as string[]).push(v) - } - for (let option of this.options) { - option.checked = true - } - } else { - for (let option of this.options) { - option.checked = false - } - } - this.updateCaches() - } - - // enum only - async toggleSelected (value: string) { - const index = (this.value as string[]).indexOf(value) - - // if present, delete - if (index > -1) { - (this.value as string[]).splice(index, 1) - // if not present, add - } else { - (this.value as string[]).push(value) - } - - this.updateCaches() - } - - async createOrEdit (index?: number) { - const nextCursor = this.cursor.seekNext(index === undefined ? this.value.length : index) - nextCursor.createFirstEntryForList() - this.subNav.push(String(index), nextCursor, this.nav) - } - - async presentAlertDelete (key: number, e: Event) { - e.stopPropagation() - - const alert = await this.alertCtrl.create({ - backdropDismiss: false, - header: 'Caution', - message: `Are you sure you want to delete this entry?`, - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Delete', - handler: () => { - if (typeof key === 'number') { - (this.value as any[]).splice(key, 1) - } else { - delete this.value[key] - } - this.updateCaches() - }, - }, - ], - }) - await alert.present() - } - - asIsOrder () { - return 0 - } - - private updateCaches () { - if (isValueSpecListOf(this.spec, 'enum')) { - this.selectAll = this.value.length !== this.spec.spec.values.length - } - this.error = this.cursor.checkInvalid() - this.annotations = this.cursor.getAnnotations() - this.valueString = (this.value as any[]).map((_, idx) => this.cursor.seekNext(idx).toString()) - } -} diff --git a/ui/src/app/modals/app-config-object/app-config-object.module.ts b/ui/src/app/modals/app-config-object/app-config-object.module.ts deleted file mode 100644 index 2cc678819..000000000 --- a/ui/src/app/modals/app-config-object/app-config-object.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { AppConfigObjectPage } from './app-config-object.page' -import { ObjectConfigComponentModule } from 'src/app/components/object-config/object-config.component.module' -import { ConfigHeaderComponentModule } from 'src/app/components/config-header/config-header.component.module' - -@NgModule({ - declarations: [AppConfigObjectPage], - imports: [ - CommonModule, - IonicModule, - ObjectConfigComponentModule, - ConfigHeaderComponentModule, - ], - entryComponents: [AppConfigObjectPage], - exports: [AppConfigObjectPage], -}) -export class AppConfigObjectPageModule { } \ No newline at end of file diff --git a/ui/src/app/modals/app-config-object/app-config-object.page.html b/ui/src/app/modals/app-config-object/app-config-object.page.html deleted file mode 100644 index 31204991c..000000000 --- a/ui/src/app/modals/app-config-object/app-config-object.page.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ui/src/app/modals/app-config-object/app-config-object.page.scss b/ui/src/app/modals/app-config-object/app-config-object.page.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/ui/src/app/modals/app-config-object/app-config-object.page.ts b/ui/src/app/modals/app-config-object/app-config-object.page.ts deleted file mode 100644 index d5bac463c..000000000 --- a/ui/src/app/modals/app-config-object/app-config-object.page.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Component, Input } from '@angular/core' -import { ModalController, AlertController } from '@ionic/angular' -import { ConfigCursor } from 'src/app/pkg-config/config-cursor' -import { ValueSpecObject } from 'src/app/pkg-config/config-types' - -@Component({ - selector: 'app-config-object', - templateUrl: './app-config-object.page.html', - styleUrls: ['./app-config-object.page.scss'], -}) -export class AppConfigObjectPage { - @Input() cursor: ConfigCursor<'object'> - spec: ValueSpecObject - value: object - error: string - - constructor ( - private readonly modalCtrl: ModalController, - private readonly alertCtrl: AlertController, - ) { } - - ngOnInit () { - this.spec = this.cursor.spec() - this.value = this.cursor.config() - this.error = this.cursor.checkInvalid() - } - - async dismiss (nullify = false) { - this.modalCtrl.dismiss(nullify ? null : this.value) - } - - async presentAlertDestroy () { - const alert = await this.alertCtrl.create({ - backdropDismiss: false, - header: 'Caution', - message: 'Are you sure you want to delete this record?', - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Delete', - handler: () => { - this.dismiss(true) - }, - }, - ], - }) - await alert.present() - } - - handleObjectEdit () { - this.error = this.cursor.checkInvalid() - } -} diff --git a/ui/src/app/modals/app-config-union/app-config-union.module.ts b/ui/src/app/modals/app-config-union/app-config-union.module.ts deleted file mode 100644 index 14880d693..000000000 --- a/ui/src/app/modals/app-config-union/app-config-union.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { AppConfigUnionPage } from './app-config-union.page' -import { ObjectConfigComponentModule } from 'src/app/components/object-config/object-config.component.module' -import { FormsModule } from '@angular/forms' -import { ConfigHeaderComponentModule } from 'src/app/components/config-header/config-header.component.module' - - -@NgModule({ - declarations: [AppConfigUnionPage], - imports: [ - CommonModule, - IonicModule, - FormsModule, - ObjectConfigComponentModule, - ConfigHeaderComponentModule, - ], - entryComponents: [AppConfigUnionPage], - exports: [AppConfigUnionPage], -}) -export class AppConfigUnionPageModule { } \ No newline at end of file diff --git a/ui/src/app/modals/app-config-union/app-config-union.page.html b/ui/src/app/modals/app-config-union/app-config-union.page.html deleted file mode 100644 index abadbccc2..000000000 --- a/ui/src/app/modals/app-config-union/app-config-union.page.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - {{ spec.tag.name }} - - - {{ spec.tag['variant-names'][option.key] }} - (default) - - - - - - - \ No newline at end of file diff --git a/ui/src/app/modals/app-config-union/app-config-union.page.scss b/ui/src/app/modals/app-config-union/app-config-union.page.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/ui/src/app/modals/app-config-union/app-config-union.page.ts b/ui/src/app/modals/app-config-union/app-config-union.page.ts deleted file mode 100644 index 2c386da5f..000000000 --- a/ui/src/app/modals/app-config-union/app-config-union.page.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Component, Input, ViewChild } from '@angular/core' -import { ModalController } from '@ionic/angular' -import { ConfigCursor } from 'src/app/pkg-config/config-cursor' -import { ValueSpecUnion } from 'src/app/pkg-config/config-types' -import { ObjectConfigComponent } from 'src/app/components/object-config/object-config.component' -import { mapUnionSpec } from '../../pkg-config/config-utilities' - -@Component({ - selector: 'app-config-union', - templateUrl: './app-config-union.page.html', - styleUrls: ['./app-config-union.page.scss'], -}) -export class AppConfigUnionPage { - @Input() cursor: ConfigCursor<'union'> - - @ViewChild(ObjectConfigComponent) - objectConfig: ObjectConfigComponent - - spec: ValueSpecUnion - value: object - error: string - edited: boolean - - constructor ( - private readonly modalCtrl: ModalController, - ) { } - - ngOnInit () { - this.spec = this.cursor.spec() - this.value = this.cursor.config() - this.error = this.cursor.checkInvalid() - this.edited = this.cursor.seekNext(this.spec.tag.id).isEdited() - } - - async dismiss () { - this.modalCtrl.dismiss(this.value) - } - - async handleUnionChange () { - this.value = mapUnionSpec(this.spec, this.value) - this.objectConfig.annotations = this.objectConfig.cursor.getAnnotations() - this.error = this.cursor.checkInvalid() - this.edited = this.cursor.seekNext(this.spec.tag.id).isEdited() - } - - setSelectOptions () { - return { - header: this.spec.tag.name, - subHeader: this.spec['change-warning'] ? 'Warning!' : undefined, - message: this.spec['change-warning'] ? `${this.spec['change-warning']}` : undefined, - cssClass: 'select-change-warning', - } - } - - handleObjectEdit () { - this.error = this.cursor.checkInvalid() - } - - asIsOrder () { - return 0 - } -} diff --git a/ui/src/app/modals/app-config-value/app-config-value.module.ts b/ui/src/app/modals/app-config-value/app-config-value.module.ts deleted file mode 100644 index 5d08de325..000000000 --- a/ui/src/app/modals/app-config-value/app-config-value.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { FormsModule } from '@angular/forms' -import { IonicModule } from '@ionic/angular' -import { AppConfigValuePage } from './app-config-value.page' -import { ConfigHeaderComponentModule } from 'src/app/components/config-header/config-header.component.module' - -@NgModule({ - declarations: [AppConfigValuePage], - imports: [ - CommonModule, - FormsModule, - IonicModule, - ConfigHeaderComponentModule, - ], - entryComponents: [AppConfigValuePage], - exports: [AppConfigValuePage], -}) -export class AppConfigValuePageModule { } diff --git a/ui/src/app/modals/app-config-value/app-config-value.page.html b/ui/src/app/modals/app-config-value/app-config-value.page.html deleted file mode 100644 index 009084ca8..000000000 --- a/ui/src/app/modals/app-config-value/app-config-value.page.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - -
- - - - - - -
-
- - - - {{ spec.units }} - - - - - - - {{ spec.name }} - - - - - - - {{ spec['value-names'][option] }} - - - - - -
-

- {{ spec['pattern-description'] }} -

-

- {{ integralDescription }} -

-

- {{ rangeDescription }} -

- -

Default: {{ defaultDescription }}

-

Units: {{ spec.units }}

-
-
-
- -
\ No newline at end of file diff --git a/ui/src/app/modals/app-config-value/app-config-value.page.scss b/ui/src/app/modals/app-config-value/app-config-value.page.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/ui/src/app/modals/app-config-value/app-config-value.page.ts b/ui/src/app/modals/app-config-value/app-config-value.page.ts deleted file mode 100644 index e5f120d81..000000000 --- a/ui/src/app/modals/app-config-value/app-config-value.page.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { Component, Input } from '@angular/core' -import { getDefaultConfigValue, getDefaultDescription, Range } from 'src/app/pkg-config/config-utilities' -import { AlertController, LoadingController, ModalController, ToastController } from '@ionic/angular' -import { ConfigCursor } from 'src/app/pkg-config/config-cursor' -import { ValueSpecOf } from 'src/app/pkg-config/config-types' -import { copyToClipboard } from 'src/app/util/web.util' -import { ErrorToastService } from 'src/app/services/error-toast.service' - -@Component({ - selector: 'app-config-value', - templateUrl: 'app-config-value.page.html', - styleUrls: ['app-config-value.page.scss'], -}) -export class AppConfigValuePage { - @Input() cursor: ConfigCursor<'string' | 'number' | 'boolean' | 'enum'> - @Input() saveFn?: (value: string | number | boolean) => Promise - - spec: ValueSpecOf<'string' | 'number' | 'boolean' | 'enum'> - value: string | number | boolean | null - - edited: boolean - error: string - unmasked = false - - defaultDescription: string - integralDescription = 'Value must be a whole number.' - - range: Range - rangeDescription: string - - constructor ( - private readonly loadingCtrl: LoadingController, - private readonly modalCtrl: ModalController, - private readonly alertCtrl: AlertController, - private readonly toastCtrl: ToastController, - private readonly errToast: ErrorToastService, - ) { } - - ngOnInit () { - this.spec = this.cursor.spec() - this.value = this.cursor.config() - this.error = this.cursor.checkInvalid() - - this.defaultDescription = getDefaultDescription(this.spec) - if (this.spec.type === 'number') { - this.range = Range.from(this.spec.range) - this.rangeDescription = this.range.description() - } - } - - async dismiss () { - if (this.value === '') this.value = null - - if (this.spec.type === 'number' && this.value !== null) { - this.value = Number(this.value) - } - - if ((!!this.saveFn && this.edited) || (!this.saveFn && this.error)) { - await this.presentAlert() - } else { - await this.modalCtrl.dismiss(this.value) - } - } - - async save () { - if (this.value === '') this.value = null - - if (this.spec.type === 'number' && this.value !== null) { - this.value = Number(this.value) - } - - const loader = await this.loadingCtrl.create({ - spinner: 'lines', - message: 'Saving...', - cssClass: 'loader', - }) - await loader.present() - - try { - await this.saveFn(this.value) - this.modalCtrl.dismiss(this.value) - } catch (e) { - this.errToast.present(e) - } finally { - loader.dismiss() - } - } - - refreshDefault () { - this.value = getDefaultConfigValue(this.spec) as any - this.handleInput() - } - - handleInput () { - this.validate() - this.edited = true - } - - clear () { - this.value = null - this.edited = true - } - - toggleMask () { - this.unmasked = !this.unmasked - } - - async copy (): Promise { - let message = '' - await copyToClipboard(String(this.value)).then(success => { message = success ? 'copied to clipboard!' : 'failed to copy'}) - - const toast = await this.toastCtrl.create({ - header: message, - position: 'bottom', - duration: 1000, - }) - await toast.present() - } - - private validate (): boolean { - if (this.spec.type === 'boolean') return true - - // test blank - if (this.value === '' && !(this.spec as any).nullable) { - this.error = 'Value cannot be blank' - return false - } - // test pattern if string - if (this.spec.type === 'string' && this.value) { - const { pattern, 'pattern-description' : patternDescription } = this.spec - if (pattern && !RegExp(pattern).test(this.value as string)) { - this.error = patternDescription || `Must match ${pattern}` - return false - } - } - // test range if number - if (this.spec.type === 'number' && this.value) { - if (this.spec.integral && !RegExp(/^[-+]?[0-9]+$/).test(String(this.value))) { - this.error = this.integralDescription - return false - } else if (!this.spec.integral && !RegExp(/^[0-9]*\.?[0-9]+$/).test(String(this.value))) { - this.error = 'Value must be a number.' - return false - } else { - try { - this.range.checkIncludes(Number(this.value)) - } catch (e) { - console.warn(e) //an invalid spec is not an error - this.error = e.message - return false - } - } - } - - this.error = '' - return true - } - - private async presentAlert () { - const header = this.error ? - 'Invalid Entry' : - 'Unsaved Changes' - - const message = this.error ? - 'Value will not be saved' : - 'You have unsaved changes. Are you sure you want to leave?' - - const alert = await this.alertCtrl.create({ - backdropDismiss: false, - header, - message, - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: `Leave`, - handler: () => { - this.modalCtrl.dismiss() - }, - }, - ], - }) - await alert.present() - } -} - diff --git a/ui/src/app/modals/enum-list/enum-list.page.ts b/ui/src/app/modals/enum-list/enum-list.page.ts index 65f59d3b9..790e892ad 100644 --- a/ui/src/app/modals/enum-list/enum-list.page.ts +++ b/ui/src/app/modals/enum-list/enum-list.page.ts @@ -53,4 +53,8 @@ export class EnumListPage { async toggleSelected (key: string) { this.options[key] = !this.options[key] } + + asIsOrder () { + return 0 + } } diff --git a/ui/src/app/modules/subnav.module.ts b/ui/src/app/modules/subnav.module.ts deleted file mode 100644 index 2a97dc466..000000000 --- a/ui/src/app/modules/subnav.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core' -import { AppConfigListPageModule } from 'src/app/modals/app-config-list/app-config-list.module' -import { AppConfigObjectPageModule } from 'src/app/modals/app-config-object/app-config-object.module' -import { AppConfigUnionPageModule } from 'src/app/modals/app-config-union/app-config-union.module' -import { AppConfigValuePageModule } from 'src/app/modals/app-config-value/app-config-value.module' -import { SubNavComponentModule } from '../components/sub-nav/sub-nav.component.module' - -@NgModule({ - imports: [ - AppConfigListPageModule, - AppConfigObjectPageModule, - AppConfigUnionPageModule, - AppConfigValuePageModule, - SubNavComponentModule, - ], - exports: [ - AppConfigListPageModule, - AppConfigObjectPageModule, - AppConfigUnionPageModule, - AppConfigValuePageModule, - SubNavComponentModule, - ], -}) -export class SubNavModule { } \ No newline at end of file diff --git a/ui/src/app/services/api/embassy/embassy-api.service.ts b/ui/src/app/services/api/embassy/embassy-api.service.ts index 6cc69620e..93fea9732 100644 --- a/ui/src/app/services/api/embassy/embassy-api.service.ts +++ b/ui/src/app/services/api/embassy/embassy-api.service.ts @@ -2,6 +2,7 @@ import { Subject, Observable } from 'rxjs' import { Http, Update, Operation, Revision, Source, Store } from 'patch-db-client' import { RR } from '../api.types' import { DataModel } from 'src/app/services/patch-db/data-model' +import { RequestError, RPCError } from '../../http.service' export abstract class ApiService implements Source, Http { protected readonly sync = new Subject>() @@ -189,10 +190,15 @@ export abstract class ApiService implements Source, Http { // this.sync.next({ patch: [temp], expiredBy: expireId }) // } - return f(a).then(({ response, revision }) => { - if (revision) this.sync.next(revision) - return response - }) as any + return f(a) + .catch((e: RequestError) => { + if (e.revision) this.sync.next(e.revision) + throw e + }) + .then(({ response, revision }) => { + if (revision) this.sync.next(revision) + return response + }) } } } diff --git a/ui/src/app/services/error-toast.service.ts b/ui/src/app/services/error-toast.service.ts index 218e9e2fd..83fe7caf3 100644 --- a/ui/src/app/services/error-toast.service.ts +++ b/ui/src/app/services/error-toast.service.ts @@ -19,9 +19,9 @@ export class ErrorToastService { let message: string | IonicSafeString - if (e.status) message = String(e.status) + if (e.code) message = String(e.code) if (e.message) message = `${message ? message + ' ' : ''}${e.message}` - if (e.data) message = `${message ? message + '. ' : ''}${e.data.code}: ${e.data.message}` + if (e.details) message = `${message ? message + ': ' : ''}${e.details}` if (!message) { message = 'Unknown Error.' diff --git a/ui/src/app/services/http.service.ts b/ui/src/app/services/http.service.ts index 02357acb3..2068cce0f 100644 --- a/ui/src/app/services/http.service.ts +++ b/ui/src/app/services/http.service.ts @@ -16,9 +16,8 @@ export class HttpService { private readonly http: HttpClient, private readonly config: ConfigService, ) { - const { url, version } = this.config.api const port = config.mocks.enabled ? this.config.mocks.rpcPort : window.location.port - this.fullUrl = `${window.location.protocol}//${window.location.hostname}:${port}/${url}/${version}` + this.fullUrl = `${window.location.protocol}//${window.location.hostname}:${port}` } watchUnauth$ (): Observable<{ }> { @@ -26,11 +25,12 @@ export class HttpService { } async rpcRequest (rpcOpts: RPCOptions): Promise { + const { url, version } = this.config.api rpcOpts.params = rpcOpts.params || { } const httpOpts = { method: Method.POST, data: rpcOpts, - url: '', + url: `/${url}/${version}`, } const res = await this.httpRequest>(httpOpts) @@ -91,21 +91,27 @@ export class HttpService { } function RpcError (e: RPCError['error']): void { - const { code, message } = e - this.status = code + const { code, message, data } = e + + this.code = code this.message = message - if (typeof e.data === 'string') { - throw new Error(`unexpected response for RPC Error data: ${e.data}`) + + if (typeof data === 'string') { + this.details = e.data + this.revision = null + } else { + this.details = data.details + this.revision = data.revision } - const data = e.data || { message: 'unknown RPC error', revision: null } - this.data = { ...data, code } } function HttpError (e: HttpErrorResponse): void { - const { status, statusText, error } = e - this.status = status + const { status, statusText } = e + + this.code = status this.message = statusText - this.data = error || { } // error = { code: string, message: string } + this.details = null + this.revision = null } function isRpcError (arg: { error: Error } | { result: Result}): arg is { error: Error } { @@ -117,9 +123,10 @@ function isRpcSuccess (arg: { error: Error } | { result: Result}) } export interface RequestError { - status: number + code: number message: string - data: { code: string, message: string, revision: Revision | null } + details: string + revision: Revision | null } export enum Method { @@ -157,7 +164,7 @@ export interface RPCError extends RPCBase { code: number, message: string data?: { - message: string + details: string revision: Revision | null } | string } diff --git a/ui/src/app/services/sub-nav.service.ts b/ui/src/app/services/sub-nav.service.ts index c50811d83..57872dd19 100644 --- a/ui/src/app/services/sub-nav.service.ts +++ b/ui/src/app/services/sub-nav.service.ts @@ -1,24 +1,16 @@ -import { Inject, Injectable, InjectionToken } from '@angular/core' +import { Injectable } from '@angular/core' import { IonNav } from '@ionic/angular' -import { AppConfigComponentMapping } from '../modals/app-config-injectable' import { ConfigCursor } from '../pkg-config/config-cursor' -export const APP_CONFIG_COMPONENT_MAPPING = new InjectionToken('APP_CONFIG_COMPONENTS') - @Injectable({ providedIn: 'root', }) export class SubNavService { path: string[] - constructor ( - @Inject(APP_CONFIG_COMPONENT_MAPPING) private readonly appConfigComponentMapping: AppConfigComponentMapping, - ) { } - async push (key: string, cursor: ConfigCursor, nav: IonNav) { - const component = this.appConfigComponentMapping[cursor.spec().type] this.path.push(key) - nav.push(component, { cursor }, { mode: 'ios' }) + // nav.push(component, { cursor }, { mode: 'ios' }) } async pop (nav: IonNav) {