iopa
Version:
API-first, Internet of Things (IoT) stack for Typescript, official implementation of the Internet Open Protocols Alliance (IOPA) reference pattern
178 lines (164 loc) • 4.95 kB
text/typescript
/*
* Internet Open Protocol Abstraction (IOPA)
* Copyright (c) 2016-2022 Internet Open Protocols Alliance
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type {
HandlerResult,
IAppCapability,
IContextCore,
IContextIopaLegacy,
ILogger,
IopaApp,
IRef,
ITraceEvent,
ITraceEvents
} from '@iopa/types'
import { ExtendableEventTarget } from '../util/events'
import { VERSION } from './constants'
import logger from './logging'
import IopaMap from './map'
/** Represents IOPA Context object for any State Flow or REST Request/Response */
export class ContextCore<C>
extends IopaMap<IContextIopaLegacy<C & IAppCapability>>
implements IContextCore<C>
{
public 'iopa.Version': string
public dispose?: () => void
public createChild: (urlPath?: string, defaults?: any) => this
public log: ILogger
public app: IopaApp<any, C & IAppCapability>
/** Initialize blank IopaContext object; Generic properties common to all server types included */
public init(): this {
const abortController = new AbortController()
this.set('server.Timestamp', Date.now())
this.set('iopa.Events', new ExtendableEventTarget() as any)
this.set('iopa.Version', VERSION)
this.set('server.AbortController', abortController)
this.set('server.AbortSignal', abortController.signal)
this.set('server.CurrentMiddleware', '')
this.set('log', logger)
return this
}
public dispatchEventExtendable(event: ITraceEvent): Promise<boolean> {
return this.get('iopa.Events').dispatchEventExtendable(event)
}
public addEventListener(
type: keyof ITraceEvents,
callback: (ev: ITraceEvent) => any,
options?: boolean | AddEventListenerOptions
): void {
return this.get('iopa.Events').addEventListener(
type as any,
callback,
options
)
}
public dispatchEvent(event: ITraceEvent): boolean {
return this.get('iopa.Events').dispatchEvent(event)
}
public removeEventListener(
type: string,
callback: (ev: ITraceEvent) => any,
options?: boolean | EventListenerOptions
): void {
return this.get('iopa.Events').removeEventListener(
type as any,
callback,
options
)
}
public using(appFuncPromiseOrValue: Function | Promise<any>): Promise<void> {
if (typeof appFuncPromiseOrValue === 'function') {
return _using<C>({
context: this,
p: appFuncPromiseOrValue(this)
})
}
return _using({
context: this as IContextCore<C>,
p: appFuncPromiseOrValue
})
}
public capability<T>(keyOrRef: IRef<T>): T | undefined
public capability<K extends keyof C>(keyOrRef: K): C[K] {
if (typeof keyOrRef === 'string') {
return this._capability(keyOrRef as keyof C) as any
} else {
return this._capability(
(keyOrRef as unknown as IRef<any>).id as unknown as keyof C
) as any
}
}
private _capability<K extends keyof C>(keyOrRef: K): C[K] {
return this.get('server.Capabilities')[keyOrRef]
}
public setCapability<K extends keyof C>(keyOrRef: K, value: C[K])
public setCapability<T, I extends T>(keyOrRef: IRef<T>, value: I): void {
if (typeof keyOrRef === 'string') {
return this._setCapability(keyOrRef as keyof C, value)
} else {
return this._setCapability(
(keyOrRef as unknown as IRef<any>).id as unknown as keyof C,
value
)
}
}
private _setCapability<K extends keyof C>(keyOrRef: K, value: any): void {
this.get('server.Capabilities')[keyOrRef] = value
}
public respondWith(res: HandlerResult, status?: number): this {
if (res || status) {
throw new Error('responses Not Implemented in Core')
}
return this
}
}
/* ES6 finally/dispose pattern for IOPA Context */
function _using<C>({
context,
p
}: {
context: IContextCore<C>
p?: Promise<any> | null
}): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (typeof p === 'undefined') {
p = null
}
resolve(p)
}).then(
(value) => {
return Promise.resolve(
(() => {
setTimeout(() => {
if (context.dispose) {
context.dispose()
}
}, 0)
return value
})()
)
},
(err) => {
console.error(err)
setTimeout(() => {
if (context.dispose) {
context.dispose()
}
}, 0)
throw err
}
)
}