mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-02 05:23:14 +00:00
0.3.0 refactor
ui: adds overlay layer to patch-db-client ui: getting towards mocks ui: cleans up factory init ui: nice type hack ui: live api for patch ui: api service source + http starts up ui: api source + http ui: rework patchdb config, pass stashTimeout into patchDbModel wires in temp patching into api service ui: example of wiring patchdbmodel into page begin integration remove unnecessary method linting first data rendering rework app initialization http source working for ssh delete call temp patches working entire Embassy tab complete not in kansas anymore ripping, saving progress progress for API request response types and endoint defs Update data-model.ts shambles, but in a good way progress big progress progress installed list working big progress progress progress begin marketplace redesign Update api-types.ts Update api-types.ts marketplace improvements cosmetic dependencies and recommendations begin nym auth approach install wizard restore flow and donations
This commit is contained in:
committed by
Aiden McClelland
parent
fd685ae32c
commit
594d93eb3b
@@ -1,32 +1,49 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
|
||||
import { Observable, from, interval, race } from 'rxjs'
|
||||
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
|
||||
import { Observable, from, interval, race, Subject } from 'rxjs'
|
||||
import { map, take } from 'rxjs/operators'
|
||||
import { ConfigService } from './config.service'
|
||||
import { Revision } from 'patch-db-client'
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class HttpService {
|
||||
private unauthorizedApiResponse$ = new Subject()
|
||||
authReqEnabled: boolean = false
|
||||
rootUrl: string
|
||||
|
||||
constructor (
|
||||
private readonly http: HttpClient,
|
||||
private readonly config: ConfigService,
|
||||
) { }
|
||||
|
||||
get raw () : HttpClient {
|
||||
return this.http
|
||||
) {
|
||||
const { url, version } = this.config.api
|
||||
this.rootUrl = `${url}/${version}`
|
||||
}
|
||||
|
||||
async serverRequest<T> (options: HttpOptions, overrides: Partial<{ version: string }> = { }): Promise<T> {
|
||||
options.url = leadingSlash(`${this.config.api.url}${exists(overrides.version) ? overrides.version : this.config.api.version}${options.url}`)
|
||||
if ( this.config.api.root && this.config.api.root !== '' ) {
|
||||
options.url = `${this.config.api.root}${options.url}`
|
||||
watch401$ (): Observable<{ }> {
|
||||
return this.unauthorizedApiResponse$.asObservable()
|
||||
}
|
||||
|
||||
async rpcRequest<T> (rpcOpts: RPCOptions): Promise<T> {
|
||||
rpcOpts.params = rpcOpts.params || { }
|
||||
const httpOpts = {
|
||||
method: Method.POST,
|
||||
data: rpcOpts,
|
||||
url: '',
|
||||
}
|
||||
return this.request<T>(options)
|
||||
|
||||
const res = await this.httpRequest<RPCResponse<T>>(httpOpts)
|
||||
|
||||
if (isRpcError(res)) throw new RpcError(res.error)
|
||||
|
||||
if (isRpcSuccess(res)) return res.result
|
||||
}
|
||||
|
||||
async request<T> (httpOpts: HttpOptions): Promise<T> {
|
||||
const { url, body, timeout, ...rest} = translateOptions(httpOpts)
|
||||
async httpRequest<T> (httpOpts: HttpOptions): Promise<T> {
|
||||
let { url, body, timeout, ...rest} = translateOptions(httpOpts)
|
||||
url = this.rootUrl + url
|
||||
|
||||
let req: Observable<{ body: T }>
|
||||
switch (httpOpts.method){
|
||||
case Method.GET: req = this.http.get(url, rest) as any; break
|
||||
@@ -37,25 +54,42 @@ export class HttpService {
|
||||
}
|
||||
|
||||
return (timeout ? withTimeout(req, timeout) : req)
|
||||
.toPromise()
|
||||
.then(res => res.body)
|
||||
.catch(e => { console.error(e); throw humanReadableErrorMessage(e)})
|
||||
.toPromise()
|
||||
.then(res => res.body)
|
||||
.catch(e => { throw new HttpError(e) })
|
||||
}
|
||||
}
|
||||
|
||||
function humanReadableErrorMessage (e: any): Error {
|
||||
// server up, custom backend error
|
||||
if (e.error && e.error.message) return { ...e, message: e.error.message }
|
||||
if (e.message) return { ...e, message: e.message }
|
||||
if (e.status && e.statusText) return { ...e, message: `${e.status} ${e.statusText}` }
|
||||
return { ...e, message: `Unidentifiable HTTP exception` }
|
||||
function RpcError (e: RPCError['error']): void {
|
||||
const { code, message } = e
|
||||
this.status = code
|
||||
this.message = message
|
||||
if (typeof e.data === 'string') {
|
||||
throw new Error(`unexpected response for RPC Error data: ${e.data}`)
|
||||
}
|
||||
const data = e.data || { message: 'unknown RPC error', revision: null }
|
||||
this.data = { ...data, code }
|
||||
}
|
||||
|
||||
function leadingSlash (url: string): string {
|
||||
let toReturn = url
|
||||
toReturn = toReturn.startsWith('/') ? toReturn : '/' + toReturn
|
||||
toReturn = !toReturn.endsWith('/') ? toReturn : toReturn.slice(0, -1)
|
||||
return toReturn
|
||||
function HttpError (e: HttpErrorResponse): void {
|
||||
const { status, statusText, error } = e
|
||||
this.status = status
|
||||
this.message = statusText
|
||||
this.data = error || { } // error = { code: string, message: string }
|
||||
}
|
||||
|
||||
function isRpcError<Error, Result> (arg: { error: Error } | { result: Result}): arg is { error: Error } {
|
||||
return !!(arg as any).error
|
||||
}
|
||||
|
||||
function isRpcSuccess<Error, Result> (arg: { error: Error } | { result: Result}): arg is { result: Result } {
|
||||
return !!(arg as any).result
|
||||
}
|
||||
|
||||
export interface RequestError {
|
||||
status: number
|
||||
message: string
|
||||
data: { code: string, message: string, revision: Revision | null }
|
||||
}
|
||||
|
||||
export enum Method {
|
||||
@@ -66,27 +100,64 @@ export enum Method {
|
||||
DELETE = 'DELETE',
|
||||
}
|
||||
|
||||
export interface RPCOptions {
|
||||
method: string
|
||||
// @TODO what are valid params? object, bool?
|
||||
params?: {
|
||||
[param: string]: string | number | boolean | object | string[] | number[];
|
||||
}
|
||||
}
|
||||
|
||||
interface RPCBase {
|
||||
jsonrpc: '2.0'
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface RPCRequest<T> extends RPCBase {
|
||||
method: string
|
||||
params?: T
|
||||
}
|
||||
|
||||
export interface RPCSuccess<T> extends RPCBase {
|
||||
result: T
|
||||
}
|
||||
|
||||
export interface RPCError extends RPCBase {
|
||||
error: {
|
||||
code: number,
|
||||
message: string
|
||||
data?: {
|
||||
message: string
|
||||
revision: Revision | null
|
||||
} | string
|
||||
}
|
||||
}
|
||||
|
||||
export type RPCResponse<T> = RPCSuccess<T> | RPCError
|
||||
|
||||
type HttpError = HttpErrorResponse & { error: { code: string, message: string } }
|
||||
|
||||
export interface HttpOptions {
|
||||
withCredentials?: boolean
|
||||
url: string
|
||||
method: Method
|
||||
params?: {
|
||||
[param: string]: string | string[];
|
||||
[param: string]: string | string[]
|
||||
}
|
||||
data?: any
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
}
|
||||
url: string
|
||||
readTimeout?: number
|
||||
}
|
||||
|
||||
export interface HttpJsonOptions {
|
||||
headers?: HttpHeaders | {
|
||||
[header: string]: string | string[];
|
||||
[header: string]: string | string[]
|
||||
}
|
||||
observe: 'events'
|
||||
params?: HttpParams | {
|
||||
[param: string]: string | string[];
|
||||
[param: string]: string | string[]
|
||||
}
|
||||
reportProgress?: boolean
|
||||
responseType?: 'json'
|
||||
@@ -116,7 +187,3 @@ function withTimeout<U> (req: Observable<U>, timeout: number): Observable<U> {
|
||||
interval(timeout).pipe(take(1), map(() => { throw new Error('timeout') })),
|
||||
)
|
||||
}
|
||||
|
||||
function exists (str?: string): boolean {
|
||||
return !!str || str === ''
|
||||
}
|
||||
Reference in New Issue
Block a user