mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
squashing the bugs
This commit is contained in:
committed by
Aiden McClelland
parent
7756841b1e
commit
d47dd28384
@@ -20,10 +20,8 @@
|
||||
{{ longMessage }}
|
||||
</div>
|
||||
<div style="margin: 25px 0px;">
|
||||
<div style="border-width: 0px 0px 1px 0px; font-size: unset; text-align: left; font-weight: bold; margin-left: 13px; border-style: solid; border-color: var(--ion-color-light-tint);"
|
||||
*ngIf="hasDependentViolation"
|
||||
>
|
||||
<ion-text color="warning">Will Stop</ion-text>
|
||||
<div *ngIf="hasDependentViolation" style="border-width: 0px 0px 1px 0px; font-size: unset; text-align: left; font-weight: bold; margin-left: 13px; border-style: solid; border-color: var(--ion-color-light-tint);">
|
||||
<ion-text color="warning">Affected Services</ion-text>
|
||||
</div>
|
||||
<ion-item
|
||||
style="--ion-item-background: margin-top: 5px"
|
||||
|
||||
@@ -49,7 +49,7 @@ export class DependentsComponent implements OnInit, Loadable {
|
||||
complete: () => {
|
||||
this.hasDependentViolation = this.dependentBreakages && !isEmptyObject(this.dependentBreakages)
|
||||
if (this.hasDependentViolation) {
|
||||
this.longMessage = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title} will cause the following services to STOP running. Starting them again will require additional actions.`
|
||||
this.longMessage = `${capitalizeFirstLetter(this.params.verb)} ${this.params.title} will prohibit the following services from functioning properly and will cause them to stop if they are currently running.`
|
||||
this.color$.next('warning')
|
||||
} else if (this.params.skipConfirmationDialogue) {
|
||||
this.transitions.next()
|
||||
|
||||
@@ -202,7 +202,7 @@ export class WizardBaker {
|
||||
action,
|
||||
verb: 'uninstalling',
|
||||
title,
|
||||
fetchBreakages: () => this.embassyApi.dryRemovePackage({ id }).then(breakages => breakages),
|
||||
fetchBreakages: () => this.embassyApi.dryUninstallPackage({ id }).then(breakages => breakages),
|
||||
},
|
||||
},
|
||||
bottomBar: { cancel: { whileLoading: { }, afterLoading: { text: 'Cancel' } }, next: 'Uninstall' },
|
||||
@@ -214,7 +214,7 @@ export class WizardBaker {
|
||||
action,
|
||||
verb: 'uninstalling',
|
||||
title,
|
||||
executeAction: () => this.embassyApi.removePackage({ id }),
|
||||
executeAction: () => this.embassyApi.uninstallPackage({ id }),
|
||||
},
|
||||
},
|
||||
bottomBar: { finish: 'Dismiss', cancel: { whileLoading: { } } },
|
||||
|
||||
@@ -9,7 +9,7 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
|
||||
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
import { FormGroup } from '@angular/forms'
|
||||
import { convertToNumberRecursive, FormService } from 'src/app/services/form.service'
|
||||
import { convertValuesRecursive, FormService } from 'src/app/services/form.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-config',
|
||||
@@ -87,9 +87,7 @@ export class AppConfigPage {
|
||||
}
|
||||
|
||||
async save () {
|
||||
convertToNumberRecursive(this.configSpec, this.configForm)
|
||||
|
||||
console.log('SAVING', this.configForm.value)
|
||||
convertValuesRecursive(this.configSpec, this.configForm)
|
||||
|
||||
if (this.configForm.invalid) {
|
||||
document.getElementsByClassName('validation-error')[0].parentElement.parentElement.scrollIntoView({ behavior: 'smooth' })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { FormGroup } from '@angular/forms'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
import { convertToNumberRecursive, FormService } from 'src/app/services/form.service'
|
||||
import { convertValuesRecursive, FormService } from 'src/app/services/form.service'
|
||||
import { ConfigSpec } from 'src/app/pkg-config/config-types'
|
||||
|
||||
export interface ActionButton {
|
||||
@@ -40,7 +40,7 @@ export class GenericFormPage {
|
||||
}
|
||||
|
||||
async handleClick (handler: ActionButton['handler']): Promise<void> {
|
||||
convertToNumberRecursive(this.spec, this.formGroup)
|
||||
convertValuesRecursive(this.spec, this.formGroup)
|
||||
|
||||
if (this.formGroup.invalid) {
|
||||
this.formGroup.markAllAsTouched()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</ion-item>
|
||||
|
||||
<form (ngSubmit)="submit()">
|
||||
<div style="margin-left: 16px;">
|
||||
<div style="margin: 0 0 24px 16px;">
|
||||
<p class="input-label">{{ label }}</p>
|
||||
<ion-item lines="none" color="dark">
|
||||
<ion-input [type]="useMask && !unmasked ? 'password' : 'text'" [(ngModel)]="value" name="value" [placeholder]="placeholder" (ionChange)="error = ''"></ion-input>
|
||||
@@ -19,9 +19,7 @@
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
<!-- error -->
|
||||
<p>
|
||||
<ion-text [color]="error ? 'danger' : 'medium'">{{ error || 'placeholder' }}</ion-text>
|
||||
</p>
|
||||
<p *ngIf="error"><ion-text color="danger">{{ error }}</ion-text></p>
|
||||
</div>
|
||||
|
||||
<div class="ion-text-right">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { IonicSafeString, LoadingController, ModalController } from '@ionic/angular'
|
||||
import { getErrorMessage } from 'src/app/services/error-toast.service'
|
||||
import { ModalController } from '@ionic/angular'
|
||||
|
||||
@Component({
|
||||
selector: 'generic-input',
|
||||
@@ -18,11 +17,10 @@ export class GenericInputComponent {
|
||||
@Input() value = ''
|
||||
@Input() submitFn: (value: string) => Promise<any>
|
||||
unmasked = false
|
||||
error: string | IonicSafeString
|
||||
error: string
|
||||
|
||||
constructor (
|
||||
private readonly modalCtrl: ModalController,
|
||||
private readonly loadingCtrl: LoadingController,
|
||||
) { }
|
||||
|
||||
toggleMask () {
|
||||
@@ -34,19 +32,8 @@ export class GenericInputComponent {
|
||||
}
|
||||
|
||||
async submit () {
|
||||
const loader = await this.loadingCtrl.create({
|
||||
spinner: 'lines',
|
||||
cssClass: 'loader',
|
||||
})
|
||||
loader.present()
|
||||
|
||||
try {
|
||||
await this.submitFn(this.value)
|
||||
this.modalCtrl.dismiss(undefined, 'success')
|
||||
} catch (e) {
|
||||
this.error = getErrorMessage(e)
|
||||
} finally {
|
||||
loader.dismiss()
|
||||
}
|
||||
// @TODO validate input?
|
||||
await this.submitFn(this.value)
|
||||
this.modalCtrl.dismiss(undefined, 'success')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<!-- not loading -->
|
||||
<div *ngIf="!loading">
|
||||
<div *ngIf="pkgs | empty; else list" class="ion-text-center ion-padding">
|
||||
<div *ngIf="empty; else list" class="ion-text-center ion-padding">
|
||||
<div style="display: flex; flex-direction: column; justify-content: center; height: 40vh">
|
||||
<h2>Welcome to <ion-text color="danger" style="font-family: 'Montserrat';">Embassy</ion-text></h2>
|
||||
<p class="ion-text-wrap">Get started by installing your first service.</p>
|
||||
|
||||
@@ -21,11 +21,12 @@ export class AppListPage {
|
||||
connectionFailure: boolean
|
||||
pkgs: { [id: string]: PkgInfo } = { }
|
||||
loading = true
|
||||
empty = false
|
||||
|
||||
constructor (
|
||||
private readonly config: ConfigService,
|
||||
private readonly connectionService: ConnectionService,
|
||||
private readonly installPackageService: PackageLoadingService,
|
||||
private readonly pkgLoading: PackageLoadingService,
|
||||
public readonly patch: PatchDbService,
|
||||
) { }
|
||||
|
||||
@@ -34,11 +35,7 @@ export class AppListPage {
|
||||
this.patch.watch$('package-data')
|
||||
.pipe(
|
||||
filter(obj => {
|
||||
return obj &&
|
||||
(
|
||||
isEmptyObject(obj) ||
|
||||
Object.keys(obj).length !== Object.keys(this.pkgs).length
|
||||
)
|
||||
return obj && Object.keys(obj).length !== Object.keys(this.pkgs).length
|
||||
}),
|
||||
)
|
||||
.subscribe(pkgs => {
|
||||
@@ -46,6 +43,8 @@ export class AppListPage {
|
||||
|
||||
const ids = Object.keys(pkgs)
|
||||
|
||||
this.empty = !ids.length
|
||||
|
||||
Object.keys(this.pkgs).forEach(id => {
|
||||
if (!ids.includes(id)) {
|
||||
this.pkgs[id].sub.unsubscribe()
|
||||
@@ -59,7 +58,7 @@ export class AppListPage {
|
||||
this.pkgs[id] = {
|
||||
entry: pkgs[id],
|
||||
primaryRendering: PrimaryRendering[renderPkgStatus(pkgs[id]).primary],
|
||||
installProgress: !isEmptyObject(pkgs[id]['install-progress']) ? this.installPackageService.transform(pkgs[id]['install-progress']) : undefined,
|
||||
installProgress: !isEmptyObject(pkgs[id]['install-progress']) ? this.pkgLoading.transform(pkgs[id]['install-progress']) : undefined,
|
||||
error: false,
|
||||
sub: null,
|
||||
}
|
||||
@@ -69,7 +68,7 @@ export class AppListPage {
|
||||
const statuses = renderPkgStatus(pkg)
|
||||
const primaryRendering = PrimaryRendering[statuses.primary]
|
||||
this.pkgs[id].entry = pkg
|
||||
this.pkgs[id].installProgress = !isEmptyObject(pkg['install-progress']) ? this.installPackageService.transform(pkg['install-progress']) : undefined
|
||||
this.pkgs[id].installProgress = !isEmptyObject(pkg['install-progress']) ? this.pkgLoading.transform(pkg['install-progress']) : undefined
|
||||
this.pkgs[id].primaryRendering = primaryRendering
|
||||
this.pkgs[id].error = statuses.health === HealthStatus.Failure || [DependencyStatus.Issue, DependencyStatus.Critical].includes(statuses.dependency)
|
||||
})
|
||||
|
||||
@@ -13,8 +13,7 @@ import { DependencyStatus, HealthStatus, PrimaryRendering, PrimaryStatus, render
|
||||
import { ConnectionFailure, ConnectionService } from 'src/app/services/connection.service'
|
||||
import { ErrorToastService } from 'src/app/services/error-toast.service'
|
||||
import { AppConfigPage } from 'src/app/modals/app-config/app-config.page'
|
||||
import { PackageLoadingService } from 'src/app/services/package-loading.service'
|
||||
import { ProgressData } from 'src/app/pipes/install-state.pipe'
|
||||
import { PackageLoadingService, ProgressData } from 'src/app/services/package-loading.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-show',
|
||||
|
||||
@@ -47,7 +47,10 @@ export class ServerMetricsPage {
|
||||
|
||||
async getMetrics (): Promise<void> {
|
||||
try {
|
||||
this.metrics = await this.embassyApi.getServerMetrics({ })
|
||||
const metrics = await this.embassyApi.getServerMetrics({ })
|
||||
Object.entries(metrics).forEach(([key, val]) => {
|
||||
this.metrics[key] = val
|
||||
})
|
||||
} catch (e) {
|
||||
this.errToast.present(e)
|
||||
this.stopDaemon()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { PackageLoadingService, ProgressData } from '../services/package-loading.service'
|
||||
import { InstallProgress } from '../services/patch-db/data-model'
|
||||
|
||||
@Pipe({
|
||||
@@ -6,37 +7,11 @@ import { InstallProgress } from '../services/patch-db/data-model'
|
||||
})
|
||||
export class InstallState implements PipeTransform {
|
||||
|
||||
constructor (
|
||||
private readonly pkgLoading: PackageLoadingService,
|
||||
) { }
|
||||
|
||||
transform (loadData: InstallProgress): ProgressData {
|
||||
let { downloaded, validated, unpacked, size, 'download-complete': downloadComplete, 'validation-complete': validationComplete, 'unpack-complete': unpackComplete } = loadData
|
||||
downloaded = downloadComplete ? size : downloaded
|
||||
validated = validationComplete ? size : validated
|
||||
unpacked = unpackComplete ? size : unpacked
|
||||
|
||||
const downloadWeight = 1
|
||||
const validateWeight = .2
|
||||
const unpackWeight = .7
|
||||
|
||||
const numerator = Math.floor(
|
||||
downloadWeight * downloaded +
|
||||
validateWeight * validated +
|
||||
unpackWeight * unpacked)
|
||||
|
||||
const denominator = Math.floor(loadData.size * (downloadWeight + validateWeight + unpackWeight))
|
||||
|
||||
return {
|
||||
totalProgress: Math.round(100 * numerator / denominator),
|
||||
downloadProgress: Math.round(100 * downloaded / size),
|
||||
validateProgress: Math.round(100 * validated / size),
|
||||
unpackProgress: Math.round(100 * unpacked / size),
|
||||
isComplete: downloadComplete && validationComplete && unpackComplete,
|
||||
}
|
||||
return this.pkgLoading.transform(loadData)
|
||||
}
|
||||
}
|
||||
|
||||
export interface ProgressData {
|
||||
totalProgress: number
|
||||
downloadProgress: number
|
||||
validateProgress: number
|
||||
unpackProgress: number
|
||||
isComplete: boolean
|
||||
}
|
||||
@@ -6,7 +6,6 @@ export class Range {
|
||||
minInclusive: boolean
|
||||
maxInclusive: boolean
|
||||
|
||||
|
||||
static from (s: string): Range {
|
||||
const r = new Range()
|
||||
r.minInclusive = s.startsWith('[')
|
||||
@@ -18,11 +17,23 @@ export class Range {
|
||||
}
|
||||
|
||||
checkIncludes (n: number) {
|
||||
if (this.hasMin() !== undefined && ((!this.minInclusive && this.min == n || (this.min > n)))) {
|
||||
throw new Error(`Value must be ${this.minMessage()}.`)
|
||||
if (
|
||||
this.hasMin() !== undefined &&
|
||||
(
|
||||
(this.min > n) ||
|
||||
(!this.minInclusive && this.min == n)
|
||||
)
|
||||
) {
|
||||
throw new Error(this.minMessage())
|
||||
}
|
||||
if (this.hasMax() && ((!this.maxInclusive && this.max == n || (this.max < n)))) {
|
||||
throw new Error(`Value must be ${this.maxMessage()}.`)
|
||||
if (
|
||||
this.hasMax() &&
|
||||
(
|
||||
(this.max < n) ||
|
||||
(!this.maxInclusive && this.max == n)
|
||||
)
|
||||
) {
|
||||
throw new Error(this.maxMessage())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -979,7 +979,7 @@ export module Mock {
|
||||
'warning': 'Chain will have to resync!',
|
||||
'default': true,
|
||||
},
|
||||
'objectList': {
|
||||
'object-list': {
|
||||
'name': 'Object List',
|
||||
'type': 'list',
|
||||
'subtype': 'object',
|
||||
@@ -987,24 +987,24 @@ export module Mock {
|
||||
'range': '[0,4]',
|
||||
'default': [
|
||||
{
|
||||
'firstName': 'Admin',
|
||||
'lastName': 'User',
|
||||
'first-name': 'Admin',
|
||||
'last-name': 'User',
|
||||
'age': 40,
|
||||
},
|
||||
{
|
||||
'firstName': 'Admin2',
|
||||
'lastName': 'User',
|
||||
'first-name': 'Admin2',
|
||||
'last-name': 'User',
|
||||
'age': 40,
|
||||
},
|
||||
],
|
||||
// the outer spec here, at the list level, says that what's inside (the inner spec) pertains to its inner elements.
|
||||
// it just so happens that ValueSpecObject's have the field { spec: ConfigSpec }
|
||||
// see 'unionList' below for a different example.
|
||||
// see 'union-list' below for a different example.
|
||||
'spec': {
|
||||
'unique-by': 'lastName',
|
||||
'display-as': `I'm {{lastName}}, {{firstName}} {{lastName}}`,
|
||||
'unique-by': 'last-name',
|
||||
'display-as': `I'm {{last-name}}, {{first-name}} {{last-name}}`,
|
||||
'spec': {
|
||||
'firstName': {
|
||||
'first-name': {
|
||||
'name': 'First Name',
|
||||
'type': 'string',
|
||||
'description': 'User first name',
|
||||
@@ -1013,7 +1013,7 @@ export module Mock {
|
||||
'masked': false,
|
||||
'copyable': false,
|
||||
},
|
||||
'lastName': {
|
||||
'last-name': {
|
||||
'name': 'Last Name',
|
||||
'type': 'string',
|
||||
'description': 'User first name',
|
||||
@@ -1040,7 +1040,7 @@ export module Mock {
|
||||
},
|
||||
},
|
||||
},
|
||||
'unionList': {
|
||||
'union-list': {
|
||||
'name': 'Union List',
|
||||
'type': 'list',
|
||||
'subtype': 'union',
|
||||
@@ -1105,7 +1105,7 @@ export module Mock {
|
||||
'unique-by': 'preference',
|
||||
},
|
||||
},
|
||||
'randomEnum': {
|
||||
'random-enum': {
|
||||
'name': 'Random Enum',
|
||||
'type': 'enum',
|
||||
'value-names': {
|
||||
@@ -1124,18 +1124,18 @@ export module Mock {
|
||||
'option3',
|
||||
],
|
||||
},
|
||||
'favoriteNumber': {
|
||||
'favorite-number': {
|
||||
'name': 'Favorite Number',
|
||||
'type': 'number',
|
||||
'integral': false,
|
||||
'description': 'Your favorite number of all time',
|
||||
'warning': 'Once you set this number, it can never be changed without severe consequences.',
|
||||
'nullable': false,
|
||||
'nullable': true,
|
||||
'default': 7,
|
||||
'range': '(-100,100]',
|
||||
'units': 'BTC',
|
||||
},
|
||||
'unluckyNumbers': {
|
||||
'unlucky-numbers': {
|
||||
'name': 'Unlucky Numbers',
|
||||
'type': 'list',
|
||||
'subtype': 'number',
|
||||
@@ -1276,7 +1276,7 @@ export module Mock {
|
||||
},
|
||||
},
|
||||
},
|
||||
'bitcoinNode': {
|
||||
'bitcoin-node': {
|
||||
'name': 'Bitcoin Node Settings',
|
||||
'type': 'union',
|
||||
'unique-by': null,
|
||||
@@ -1324,9 +1324,9 @@ export module Mock {
|
||||
'description': 'the default port for your Bitcoin node. default: 8333, testnet: 18333, regtest: 18444',
|
||||
'nullable': false,
|
||||
'default': 8333,
|
||||
'range': '[0, 9999]',
|
||||
'range': '(0, 9998]',
|
||||
},
|
||||
'favoriteSlogan': {
|
||||
'favorite-slogan': {
|
||||
'name': 'Favorite Slogan',
|
||||
'type': 'string',
|
||||
'description': 'You most favorite slogan in the whole world, used for paying you.',
|
||||
@@ -1367,22 +1367,22 @@ export module Mock {
|
||||
// actual config
|
||||
config: {
|
||||
testnet: false,
|
||||
objectList: [
|
||||
'object-list': [
|
||||
{
|
||||
'firstName': 'Admin',
|
||||
'lastName': 'User',
|
||||
'first-name': 'Admin',
|
||||
'last-name': 'User',
|
||||
'age': 40,
|
||||
},
|
||||
{
|
||||
'firstName': 'Admin2',
|
||||
'lastName': 'User',
|
||||
'first-name': 'Admin2',
|
||||
'last-name': 'User',
|
||||
'age': 40,
|
||||
},
|
||||
],
|
||||
unionList: undefined,
|
||||
randomEnum: 'option1',
|
||||
favoriteNumber: 8,
|
||||
secondaryNumbers: undefined,
|
||||
'union-list': undefined,
|
||||
'random-enum': 'option1',
|
||||
'favorite-number': null,
|
||||
'secondary-numbers': undefined,
|
||||
rpcsettings: {
|
||||
laws: {
|
||||
law1: 'The first law',
|
||||
@@ -1395,7 +1395,7 @@ export module Mock {
|
||||
advanced: {
|
||||
notifications: ['email'],
|
||||
},
|
||||
bitcoinNode: undefined,
|
||||
'bitcoin-node': undefined,
|
||||
port: 5959,
|
||||
rpcallowip: undefined,
|
||||
rpcauth: ['matt: 8273gr8qwoidm1uid91jeh8y23gdio1kskmwejkdnm'],
|
||||
@@ -1403,12 +1403,12 @@ export module Mock {
|
||||
}
|
||||
|
||||
export const mockCupsDependentConfig = {
|
||||
randomEnum: 'option1',
|
||||
'random-enum': 'option1',
|
||||
testnet: false,
|
||||
favoriteNumber: 8,
|
||||
secondaryNumbers: [13, 58, 20],
|
||||
objectList: [],
|
||||
unionList: [],
|
||||
'favorite-number': 8,
|
||||
'secondary-numbers': [13, 58, 20],
|
||||
'object-list': [],
|
||||
'union-list': [],
|
||||
rpcsettings: {
|
||||
laws: null,
|
||||
rpcpass: null,
|
||||
@@ -1418,7 +1418,7 @@ export module Mock {
|
||||
advanced: {
|
||||
notifications: [],
|
||||
},
|
||||
bitcoinNode: { type: 'internal' },
|
||||
'bitcoin-Node': { type: 'internal' },
|
||||
port: 5959,
|
||||
rpcallowip: [],
|
||||
rpcauth: ['matt: 8273gr8qwoidm1uid91jeh8y23gdio1kskmwejkdnm'],
|
||||
|
||||
@@ -175,11 +175,11 @@ export module RR {
|
||||
export type StopPackageReq = WithExpire<{ id: string }> // package.stop
|
||||
export type StopPackageRes = WithRevision<null>
|
||||
|
||||
export type DryRemovePackageReq = RemovePackageReq // package.remove.dry
|
||||
export type DryRemovePackageRes = Breakages
|
||||
export type DryUninstallPackageReq = UninstallPackageReq // package.uninstall.dry
|
||||
export type DryUninstallPackageRes = Breakages
|
||||
|
||||
export type RemovePackageReq = WithExpire<{ id: string }> // package.remove
|
||||
export type RemovePackageRes = WithRevision<null>
|
||||
export type UninstallPackageReq = WithExpire<{ id: string }> // package.uninstall
|
||||
export type UninstallPackageRes = WithRevision<null>
|
||||
|
||||
export type DryConfigureDependencyReq = { 'dependency-id': string, 'dependent-id': string } // package.dependency.configure.dry
|
||||
export type DryConfigureDependencyRes = object
|
||||
|
||||
@@ -171,11 +171,11 @@ export abstract class ApiService implements Source<DataModel>, Http<DataModel> {
|
||||
() => this.stopPackageRaw(params),
|
||||
)()
|
||||
|
||||
abstract dryRemovePackage (params: RR.DryRemovePackageReq): Promise<RR.DryRemovePackageRes>
|
||||
abstract dryUninstallPackage (params: RR.DryUninstallPackageReq): Promise<RR.DryUninstallPackageRes>
|
||||
|
||||
protected abstract removePackageRaw (params: RR.RemovePackageReq): Promise<RR.RemovePackageRes>
|
||||
removePackage = (params: RR.RemovePackageReq) => this.syncResponse(
|
||||
() => this.removePackageRaw(params),
|
||||
protected abstract uninstallPackageRaw (params: RR.UninstallPackageReq): Promise<RR.UninstallPackageRes>
|
||||
uninstallPackage = (params: RR.UninstallPackageReq) => this.syncResponse(
|
||||
() => this.uninstallPackageRaw(params),
|
||||
)()
|
||||
|
||||
abstract dryConfigureDependency (params: RR.DryConfigureDependencyReq): Promise<RR.DryConfigureDependencyRes>
|
||||
|
||||
@@ -260,12 +260,12 @@ export class LiveApiService extends ApiService {
|
||||
return this.http.rpcRequest({ method: 'package.stop', params })
|
||||
}
|
||||
|
||||
async dryRemovePackage (params: RR.DryRemovePackageReq): Promise <RR.DryRemovePackageRes> {
|
||||
return this.http.rpcRequest({ method: 'package.remove.dry', params })
|
||||
async dryUninstallPackage (params: RR.DryUninstallPackageReq): Promise <RR.DryUninstallPackageRes> {
|
||||
return this.http.rpcRequest({ method: 'package.uninstall.dry', params })
|
||||
}
|
||||
|
||||
async removePackageRaw (params: RR.RemovePackageReq): Promise <RR.RemovePackageRes> {
|
||||
return this.http.rpcRequest({ method: 'package.remove', params })
|
||||
async uninstallPackageRaw (params: RR.UninstallPackageReq): Promise <RR.UninstallPackageRes> {
|
||||
return this.http.rpcRequest({ method: 'package.uninstall', params })
|
||||
}
|
||||
|
||||
async dryConfigureDependency (params: RR.DryConfigureDependencyReq): Promise <RR.DryConfigureDependencyRes> {
|
||||
|
||||
@@ -452,12 +452,12 @@ export class MockApiService extends ApiService {
|
||||
return res
|
||||
}
|
||||
|
||||
async dryRemovePackage (params: RR.DryRemovePackageReq): Promise<RR.DryRemovePackageRes> {
|
||||
async dryUninstallPackage (params: RR.DryUninstallPackageReq): Promise<RR.DryUninstallPackageRes> {
|
||||
await pauseFor(2000)
|
||||
return { }
|
||||
}
|
||||
|
||||
async removePackageRaw (params: RR.RemovePackageReq): Promise<RR.RemovePackageRes> {
|
||||
async uninstallPackageRaw (params: RR.UninstallPackageReq): Promise<RR.UninstallPackageRes> {
|
||||
await pauseFor(2000)
|
||||
const patch = [
|
||||
{
|
||||
|
||||
@@ -153,12 +153,13 @@ function isFullUnion (spec: ValueSpecUnion | ListValueSpecUnion): spec is ValueS
|
||||
|
||||
export function numberInRange (stringRange: string): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
if (!control.value) return null
|
||||
const value = control.value
|
||||
if (!value) return null
|
||||
try {
|
||||
Range.from(stringRange).checkIncludes(control.value)
|
||||
Range.from(stringRange).checkIncludes(value)
|
||||
return null
|
||||
} catch (e) {
|
||||
return { numberNotInRange: { value: getRangeMessage(stringRange) } }
|
||||
return { numberNotInRange: { value: `Number must be ${e.message}` } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,30 +182,15 @@ export function isInteger (): ValidatorFn {
|
||||
|
||||
export function listInRange (stringRange: string): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
const range = Range.from(stringRange)
|
||||
const min = range.integralMin()
|
||||
const max = range.integralMax()
|
||||
const length = control.value.length
|
||||
if ((min && length < min) || (max && length > max)) {
|
||||
return { listNotInRange: { value: getRangeMessage(stringRange, true) } }
|
||||
} else {
|
||||
try {
|
||||
Range.from(stringRange).checkIncludes(control.value.length)
|
||||
return null
|
||||
} catch (e) {
|
||||
return { numberNotInRange: { value: `List must be ${e.message}` } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRangeMessage (stringRange: string, isList = false): string {
|
||||
const range = Range.from(stringRange)
|
||||
const min = range.integralMin()
|
||||
const max = range.integralMax()
|
||||
const messageStart = isList ? 'List length must be ' : 'Must be '
|
||||
const minMessage = !!min ? `greater than ${min}` : ''
|
||||
const and = !!min && !!max ? ' and ' : ''
|
||||
const maxMessage = !!max ? `less than ${max}` : ''
|
||||
|
||||
return `${messageStart} ${minMessage}${and}${maxMessage}`
|
||||
}
|
||||
|
||||
export function listUnique (spec: ValueSpecList): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
for (let idx = 0; idx < control.value.length; idx++) {
|
||||
@@ -391,48 +377,41 @@ function isUnion (spec: any): spec is ListValueSpecUnion {
|
||||
return !!spec.tag
|
||||
}
|
||||
|
||||
const sampleUniqueBy: UniqueBy = {
|
||||
all: [
|
||||
'last name',
|
||||
{ any: [
|
||||
'favorite color',
|
||||
null,
|
||||
] },
|
||||
{ any: [
|
||||
'favorite color',
|
||||
'first name',
|
||||
null,
|
||||
] },
|
||||
],
|
||||
}
|
||||
|
||||
export function convertToNumberRecursive (configSpec: ConfigSpec, group: FormGroup) {
|
||||
export function convertValuesRecursive (configSpec: ConfigSpec, group: FormGroup) {
|
||||
Object.entries(configSpec).forEach(([key, valueSpec]) => {
|
||||
if (valueSpec.type === 'number') {
|
||||
const control = group.get(key)
|
||||
control.setValue(Number(control.value))
|
||||
control.setValue(control.value ? Number(control.value) : null)
|
||||
} else if (valueSpec.type === 'string') {
|
||||
const control = group.get(key)
|
||||
if (!control.value) control.setValue(null)
|
||||
} else if (valueSpec.type === 'object') {
|
||||
convertToNumberRecursive(valueSpec.spec, group.get(key) as FormGroup)
|
||||
convertValuesRecursive(valueSpec.spec, group.get(key) as FormGroup)
|
||||
} else if (valueSpec.type === 'union') {
|
||||
const control = group.get(key) as FormGroup
|
||||
const spec = valueSpec.variants[control.controls[valueSpec.tag.id].value]
|
||||
convertToNumberRecursive(spec, control)
|
||||
convertValuesRecursive(spec, control)
|
||||
} else if (valueSpec.type === 'list') {
|
||||
const formArr = group.get(key) as FormArray
|
||||
if (valueSpec.subtype === 'number') {
|
||||
formArr.controls.forEach(control => {
|
||||
control.setValue(Number(control.value))
|
||||
control.setValue(control.value ? Number(control.value) : null)
|
||||
})
|
||||
} else if (valueSpec.subtype === 'string') {
|
||||
formArr.controls.forEach(control => {
|
||||
if (!control.value) control.setValue(null)
|
||||
control.setValue(control.value ? Number(control.value) : null)
|
||||
})
|
||||
} else if (valueSpec.subtype === 'object') {
|
||||
formArr.controls.forEach((formGroup: FormGroup) => {
|
||||
const objectSpec = valueSpec.spec as ListValueSpecObject
|
||||
convertToNumberRecursive(objectSpec.spec, formGroup)
|
||||
convertValuesRecursive(objectSpec.spec, formGroup)
|
||||
})
|
||||
} else if (valueSpec.subtype === 'union') {
|
||||
formArr.controls.forEach((formGroup: FormGroup) => {
|
||||
const unionSpec = valueSpec.spec as ListValueSpecUnion
|
||||
const spec = unionSpec.variants[formGroup.controls[unionSpec.tag.id].value]
|
||||
convertToNumberRecursive(spec, formGroup)
|
||||
convertValuesRecursive(spec, formGroup)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user