import {Id, IdMap, Obj, request, RequestBody, RequestOptions, Response} from '../shared'; import { Mapping, SystemError, ReturnType, DataValue, PrimitiveMapping, ListMapping, ObjectType, JSONValue, JSONDataValue, KeyedListMapping } from './types'; import {ResourceInstance, ResourceSpecials, ResourceSchema} from './resource'; import { BooleanFormatType, FieldType, FormatType, JSONByteFormatType, JSONFormatType, NodeDef, NumberFormatType, StringFormatType } from '../modeling'; import {LayerProfile, LayerToken} from './access'; export type ResourceValue = { [_: string]: JSONDataValue | JSONDataValue[] | null }; const defaultRequestOptions: RequestOptions = { request: 'json', response: 'json', } export interface AccessOptions { accessNames?: string[]; noCache?: boolean; } export interface LayerInfo { [_: string]: JSONValue; layerId: Id; userId: Id; permissions: string; } export interface SystemInfo { [_: string]: JSONValue; apiVersion: string; serverVersion: string; unixTime: number; layers: LayerInfo[]; } export class SystemClient { private _layerProfile: LayerProfile; private _defaultOptions?: AccessOptions; private _decoder = new TextDecoder(); public constructor(private url = 'https://api.vyze.io/system') { this._layerProfile = new LayerProfile(); } public setUrl(url: string) { this.url = url; } // Access public set defaultOptions(options: AccessOptions | undefined) { this._defaultOptions = options; } public get defaultOptions(): AccessOptions | undefined { return this._defaultOptions; } public get layerProfile(): LayerProfile { return this._layerProfile; } public set layerProfile(layerProfile: LayerProfile) { this._layerProfile = layerProfile; } // General public async getInfo(options?: AccessOptions): Promise { const endpoint = this.buildUrl('info'); return this.get(endpoint, defaultRequestOptions, options); } public async verifyToken(tokenStr: string): Promise { const endpoint = this.buildUrl('token', {'token': tokenStr}); const response = await this.get<{ layer: Id; user: Id; granted: number; mandatory: number; exclusive: number; created: number; expiry: number; admin: boolean; signature: string; }>(endpoint, defaultRequestOptions, {accessNames: []}); if (response instanceof SystemError) { return response; } const created = new Date(response.created * 1000); return new LayerToken(response.user, response.layer, tokenStr, response.granted, response.mandatory, response.exclusive, created, response.expiry, response.admin, response.signature); } // Objects public async getObject(objectId: Id, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}`); return await this.get(endpoint, defaultRequestOptions, options); } public async createObject(abstracts: Id[] | Id, name = '', dependent = false, accessName = 'main_full', options?: AccessOptions): Promise { const layerId = this.layerProfile.getAccessGroup(accessName)?.layerId; if (!layerId) { throw new Error(`access group layer not found: ${accessName}`); } if (typeof abstracts !== 'object') { abstracts = [abstracts]; } const endpoint = this.buildUrl('object'); return await this.post(endpoint, { abstracts, name, dependent, layer: layerId, }, defaultRequestOptions, options); } public async deleteObject(objectId: Id, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}`); const resp = await this.delete(endpoint, defaultRequestOptions, options); if (resp instanceof SystemError) { return resp; } } // Name public async getName(objectId: Id, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}/name`); return await this.get(endpoint, defaultRequestOptions, options); } public async setName(objectId: Id, name: string, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}/name`); const resp = await this.post(endpoint, name, defaultRequestOptions, options); if (resp instanceof SystemError) { return resp; } } // Data async getData(objectId: Id, formatType: StringFormatType): Promise async getData(objectId: Id, formatType: NumberFormatType): Promise async getData(objectId: Id, formatType: BooleanFormatType): Promise async getData(objectId: Id, formatType: 'raw', chunks?: number, update?: (chunk: ArrayBuffer, offset: number, length: number) => void): Promise public async getData(objectId: Id, formatType: FormatType, chunks?: number, update?: (chunk: ArrayBuffer, offset: number, length: number) => void, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}/data`, { 'format': formatType, }); if (formatType === 'raw') { if (chunks) { let offset = 0; let responseBytes = new Uint8Array(); let length: number | undefined; while (!length || offset < length) { const resp = await this.request('GET', endpoint, undefined, {request: 'raw', response: 'raw'}, (options ?? this.defaultOptions)?.accessNames ?? this.layerProfile.accessGroups.map((ag) => ag.name), options?.noCache, { Range: `bytes=${offset}-${offset + chunks}`, }); const response = resp[1]; const responseBody = resp[0] as ArrayBuffer; const byteRange = response.headers['content-range']; if (!byteRange) { throw new Error('expected byte range'); } const byteRanges = byteRange.split('/'); if (byteRanges.length !== 2) { throw new Error('unexpected byte range'); } if (!response.body) { throw new Error('unexpected nil body'); } length = parseInt(byteRanges[1]); if (update) { update(responseBody, offset, length); } const extendedBytes = new Uint8Array(responseBytes.length + responseBody.byteLength); extendedBytes.set(responseBytes, 0); extendedBytes.set(new Uint8Array(responseBody), responseBytes.length); responseBytes = extendedBytes; offset = responseBytes.length; } return responseBytes.buffer as T; } else { return await this.get(endpoint, {request: 'raw', response: 'raw'}, options); } } else { return await this.get(endpoint, defaultRequestOptions, options); } } async setData(objectId: Id, value: string, formatType: StringFormatType): Promise async setData(objectId: Id, value: number, formatType: NumberFormatType): Promise async setData(objectId: Id, value: boolean, formatType: BooleanFormatType): Promise async setData(objectId: Id, value: ArrayBuffer, formatType: 'raw'): Promise public async setData(objectId: Id, value: T, formatType: FormatType, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}/data`, { 'format': formatType, }); if (formatType === 'raw') { return await this.post(endpoint, value, {request: 'raw', response: 'json'}, options); } else { return await this.post(endpoint, value, defaultRequestOptions, options); } } public async deleteData(objectId: Id, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}/data`); return await this.delete(endpoint, defaultRequestOptions, options); } // Hierarchy public async getAbstracts(objectId: Id, includeSelf = false, includeDirect = true, includeIndirect = true, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}/abstracts`, { 'self': includeSelf ? '1' : '0', 'direct': includeDirect ? '1' : '0', 'transitive': includeIndirect ? '1' : '0', }); return this.get(endpoint, defaultRequestOptions, options); } public async getSpecials(objectId: Id, includeSelf = false, includeDirect = true, includeIndirect = true, options?: AccessOptions): Promise { const endpoint = this.buildUrl(`object/${objectId}/specials`, { 'self': includeSelf ? '1' : '0', 'direct': includeDirect ? '1' : '0', 'transitive': includeIndirect ? '1' : '0', }); return this.get(endpoint, defaultRequestOptions, options); } // Relations async getTargets(objectId: Id, returnType?: 'map'): Promise async getTargets(objectId: Id, returnType: 'objects' | 'relations'): Promise async getTargets(objectId: Id, returnType: 'pairs'): Promise<[Id, Id][] | SystemError> public async getTargets(objectId: Id, returnType: ReturnType = 'map', options?: AccessOptions): Promise { const params: { [_: string]: string } = { 'return': returnType, } const endpoint = this.buildUrl(`object/${objectId}/targets`, params); return this.get(endpoint, defaultRequestOptions, options); } async getKeyedTargets(objectId: Id, relationId: Id, returnType?: 'map'): Promise async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'objects' | 'relations'): Promise async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'pairs'): Promise<[Id, Id][] | SystemError> async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'keyed_objects' | 'keyed_relations', formatType: StringFormatType): Promise<[string, Id][] | SystemError> async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'keyed_pairs', formatType: StringFormatType): Promise<[string, Id, Id][] | SystemError> async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'keyed_objects' | 'keyed_relations', formatType: NumberFormatType): Promise<[number, Id][] | SystemError> async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'keyed_pairs', formatType: NumberFormatType): Promise<[number, Id, Id][] | SystemError> async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'keyed_objects' | 'keyed_relations', formatType: BooleanFormatType): Promise<[boolean, Id][] | SystemError> async getKeyedTargets(objectId: Id, relationId: Id, returnType: 'keyed_pairs', formatType: BooleanFormatType): Promise<[boolean, Id, Id][] | SystemError> public async getKeyedTargets(objectId: Id, relationId: Id, returnType: ReturnType = 'map', formatType?: FormatType, options?: AccessOptions): Promise { const params: { [_: string]: string } = { 'return': returnType, 'format': formatType ?? '', } const endpoint = this.buildUrl(`object/${objectId}/targets/${relationId}`, params); return this.get(endpoint, defaultRequestOptions, options); } async getOrigins(objectId: Id, returnType?: 'map'): Promise async getOrigins(objectId: Id, returnType: 'objects' | 'relations'): Promise async getOrigins(objectId: Id, returnType: 'pairs'): Promise<[Id, Id][] | SystemError> public async getOrigins(objectId: Id, returnType: ReturnType = 'map', options?: AccessOptions): Promise { const params: { [_: string]: string } = { 'return': returnType, } const endpoint = this.buildUrl(`object/${objectId}/origins`, params); return this.get(endpoint, defaultRequestOptions, options); } async getKeyedOrigins(objectId: Id, relationId: Id, returnType?: 'map'): Promise async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'objects' | 'relations'): Promise async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'pairs'): Promise<[Id, Id][] | SystemError> async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'keyed_objects' | 'keyed_relations', formatType: StringFormatType): Promise<[string, Id][] | SystemError> async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'keyed_pairs', formatType: StringFormatType): Promise<[string, Id, Id][] | SystemError> async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'keyed_objects' | 'keyed_relations', formatType: NumberFormatType): Promise<[number, Id][] | SystemError> async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'keyed_pairs', formatType: NumberFormatType): Promise<[number, Id, Id][] | SystemError> async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'keyed_objects' | 'keyed_relations', formatType: BooleanFormatType): Promise<[boolean, Id][] | SystemError> async getKeyedOrigins(objectId: Id, relationId: Id, returnType: 'keyed_pairs', formatType: BooleanFormatType): Promise<[boolean, Id, Id][] | SystemError> public async getKeyedOrigins(objectId: Id, relationId: Id, returnType: ReturnType = 'map', formatType?: FormatType, options?: AccessOptions): Promise { const params: { [_: string]: string } = { 'return': returnType, 'format': formatType ?? '', } const endpoint = this.buildUrl(`object/${objectId}/origins/${relationId}`, params); return this.get(endpoint, defaultRequestOptions, options); } public async createRelation(originId: Id, targetId: Id, abstractIds: Id | Id[], key?: T, keyFormat?: JSONFormatType, name = '', accessName = 'main_full', options?: AccessOptions): Promise { const layerId = this.layerProfile.getAccessGroup(accessName)?.layerId; if (!layerId) { throw new Error(`access group layer not found: ${accessName}`); } if (typeof abstractIds === 'string') { abstractIds = [abstractIds]; } const endpoint = this.buildUrl(`relation`); const params: { [_: string]: JSONValue } = { 'abstracts': abstractIds, 'name': name, 'origin': originId, 'target': targetId, 'layer': layerId, } if (key) { params['key'] = key; } if (keyFormat) { params['keyFormat'] = keyFormat; } return this.post(endpoint, params, defaultRequestOptions, options); } // Values async getValue(originId: Id, relationId: Id, fieldType: 'id', formatType: JSONByteFormatType, mapping: PrimitiveMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'id', formatType: JSONByteFormatType, mapping: ListMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'id', formatType: JSONByteFormatType, mapping: KeyedListMapping): Promise<[unknown, Id][] | SystemError> async getValue(originId: Id, relationId: Id, fieldType: 'name', formatType: 'string', mapping: PrimitiveMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'name', formatType: 'string', mapping: ListMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'name', formatType: 'string', mapping: KeyedListMapping): Promise<[unknown, string][] | SystemError> async getValue(originId: Id, relationId: Id, fieldType: 'created', formatType: 'integer', mapping: PrimitiveMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'created', formatType: 'integer', mapping: ListMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'created', formatType: 'integer', mapping: KeyedListMapping): Promise<[unknown, number][] | SystemError> async getValue(originId: Id, relationId: Id, fieldType: 'size', formatType: 'integer', mapping: PrimitiveMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'size', formatType: 'integer', mapping: ListMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'size', formatType: 'integer', mapping: KeyedListMapping): Promise<[unknown, number][] | SystemError> async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: StringFormatType, mapping: PrimitiveMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: StringFormatType, mapping: ListMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: StringFormatType, mapping: KeyedListMapping): Promise<[unknown, string][] | SystemError> async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: NumberFormatType, mapping: PrimitiveMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: NumberFormatType, mapping: ListMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: NumberFormatType, mapping: KeyedListMapping): Promise<[unknown, number][] | SystemError> async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: BooleanFormatType, mapping: PrimitiveMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: BooleanFormatType, mapping: ListMapping): Promise async getValue(originId: Id, relationId: Id, fieldType: 'data', formatType: BooleanFormatType, mapping: KeyedListMapping): Promise<[unknown, boolean][] | SystemError> public async getValue(originId: Id, relationId: Id, fieldType: FieldType, formatType: FormatType, mapping: Mapping, options?: AccessOptions): Promise { const params: { [_: string]: any } = { 'relation': relationId, 'field': fieldType, 'format': formatType, } mapping.setParams(params); const endpoint = this.buildUrl(`object/${originId}/value`, params); return this.get(endpoint, defaultRequestOptions, options); } async putValue(originId: Id, relationId: Id, value: string, fieldType: 'id', formatType: JSONByteFormatType, mapping: PrimitiveMapping): Promise async putValue(originId: Id, relationId: Id, value: string[], fieldType: 'id', formatType: JSONByteFormatType, mapping: ListMapping): Promise async putValue(originId: Id, relationId: Id, value: string, fieldType: 'data', formatType: StringFormatType, mapping: PrimitiveMapping, spaceId?: Id): Promise async putValue(originId: Id, relationId: Id, value: string[], fieldType: 'data', formatType: StringFormatType, mapping: ListMapping, spaceId?: Id): Promise async putValue(originId: Id, relationId: Id, value: number, fieldType: 'data', formatType: NumberFormatType, mapping: PrimitiveMapping, spaceId?: Id): Promise async putValue(originId: Id, relationId: Id, value: number[], fieldType: 'data', formatType: NumberFormatType, mapping: ListMapping, spaceId?: Id): Promise async putValue(originId: Id, relationId: Id, value: boolean, fieldType: 'data', formatType: BooleanFormatType, mapping: PrimitiveMapping, spaceId?: Id): Promise async putValue(originId: Id, relationId: Id, value: boolean[], fieldType: 'data', formatType: BooleanFormatType, mapping: ListMapping, spaceId?: Id): Promise public async putValue(originId: Id, relationId: Id, value: T, fieldType: FieldType, formatType: FormatType, mapping: Mapping, accessName = 'main_full', options?: AccessOptions): Promise { const layerId = this.layerProfile.getAccessGroup(accessName)?.layerId; if (!layerId) { throw new Error(`access group layer not found: ${accessName}`); } const params: { [_: string]: any } = { 'relation': relationId, 'field': fieldType, 'format': formatType, 'layer': layerId, } mapping.setParams(params); const endpoint = this.buildUrl(`object/${originId}/value`, params); return this.post(endpoint, value, defaultRequestOptions, options); } // Lookup async lookupData(abstractId: Id, data: string, formatType: StringFormatType, mapping: PrimitiveMapping): Promise async lookupData(abstractId: Id, data: string, formatType: StringFormatType, mapping: ListMapping): Promise async lookupData(abstractId: Id, data: number, formatType: NumberFormatType, mapping: PrimitiveMapping): Promise async lookupData(abstractId: Id, data: number, formatType: NumberFormatType, mapping: ListMapping): Promise async lookupData(abstractId: Id, data: boolean, formatType: BooleanFormatType, mapping: PrimitiveMapping): Promise async lookupData(abstractId: Id, data: boolean, formatType: BooleanFormatType, mapping: ListMapping): Promise async lookupData(abstractId: Id, data: ArrayBuffer, formatType: 'raw', mapping: PrimitiveMapping): Promise async lookupData(abstractId: Id, data: ArrayBuffer, formatType: 'raw', mapping: ListMapping): Promise public async lookupData(abstractId: Id, data: T, formatType: FormatType, mapping: Mapping, options?: AccessOptions): Promise { const params = { 'format': formatType, } mapping.setParams(params); const endpoint = this.buildUrl(`lookup/data/${abstractId}`, params); if (formatType == 'raw') { return this.post(endpoint, data, {request: 'raw', response: 'json'}, options); } else { return this.post(endpoint, data, defaultRequestOptions, options); } } async lookupValue(relationId: Id, data: string, formatType: StringFormatType, mapping: PrimitiveMapping): Promise async lookupValue(relationId: Id, data: string, formatType: StringFormatType, mapping: ListMapping): Promise async lookupValue(relationId: Id, data: number, formatType: NumberFormatType, mapping: PrimitiveMapping): Promise async lookupValue(relationId: Id, data: number, formatType: NumberFormatType, mapping: ListMapping): Promise async lookupValue(relationId: Id, data: boolean, formatType: BooleanFormatType, mapping: PrimitiveMapping): Promise async lookupValue(relationId: Id, data: boolean, formatType: BooleanFormatType, mapping: ListMapping): Promise public async lookupValue(relationId: Id, data: T, formatType: FormatType, mapping: Mapping, options?: AccessOptions): Promise { const params = { 'format': formatType, } mapping.setParams(params); const endpoint = this.buildUrl(`lookup/value/${relationId}`, params); if (formatType == 'raw') { return this.post(endpoint, data, {request: 'raw', response: 'json'}, options); } else { return this.post(endpoint, data, defaultRequestOptions, options); } } // Resource public async getResourceInstance(resource: ResourceInstance, options?: AccessOptions): Promise { const endpoint = this.buildUrl('resource/get'); return this.post(endpoint, resource.toGetRequest(), defaultRequestOptions, options); } public async getResourceSpecials(resource: ResourceSpecials, options?: AccessOptions): Promise { const endpoint = this.buildUrl('resource/get'); return this.post(endpoint, resource.toGetRequest(), defaultRequestOptions, options); } public async putResourceInstance(resource: ResourceInstance, value: ResourceValue | ResourceValue[], accessName = 'main_full', options?: AccessOptions): Promise { return this.putResource(resource.objectId, resource.schema, value, resource.objectType, accessName, options) as Promise; } public async putResourceSpecials(resource: ResourceSpecials, value: ResourceValue | ResourceValue[], accessName = 'main_full', options?: AccessOptions): Promise { return this.putResource(resource.objectId, resource.schema, value, resource.objectType, accessName, options) as Promise; } private async putResource(objectId: Id, schema: ResourceSchema, value: ResourceValue | ResourceValue[], objectType: ObjectType, accessName: string, options?: AccessOptions): Promise { const layerId = this.layerProfile.getAccessGroup(accessName)?.layerId; if (!layerId) { throw new Error(`access group layer not found: ${accessName}`); } const endpoint = this.buildUrl('resource/put'); const params = { objectId: objectId, object: objectType, schema: schema.object, value: value, layer: layerId, } return this.post(endpoint, params, defaultRequestOptions, options); } // Nodes public async getNode(node: NodeDef, options?: AccessOptions): Promise { const endpoint = this.buildUrl('node/get'); return this.post(endpoint, { node: node as unknown as JSONValue, }, defaultRequestOptions, options); } public async putNode(node: NodeDef, value: any, accessName = 'main_full', options?: AccessOptions): Promise { const layerId = this.layerProfile.getAccessGroup(accessName)?.layerId; if (!layerId) { throw new Error(`access group layer not found: ${accessName}`); } const endpoint = this.buildUrl('node/put'); return this.post(endpoint, { node: node as unknown as JSONValue, value: value, layer: layerId, }, defaultRequestOptions, options); } // Helpers private buildUrl(endpoint: string, params?: { [key: string]: string }) { let url = `${this.url}/v1/${endpoint}`; if (params) { const paramStr: string[] = (Array.from(Object.entries(params)) .filter((e) => typeof e[1] !== 'undefined') as [string, string][]) .map((e) => [e[0], encodeURIComponent(e[1])] as [string, string]) .map((e) => `${e[0]}=${e[1]}`); url += '?' + paramStr.join('&'); } return url; } async get(url: string, requestOptions: RequestOptions, accessOptions?: AccessOptions): Promise { const resp = await this.request('GET', url, undefined, requestOptions, accessOptions?.accessNames, accessOptions?.noCache); if (resp[1].status !== 200) { return this.handleError(resp[1]); } return resp[0] as T; } async post(url: string, body: JSONValue | ArrayBuffer, requestOptions: RequestOptions, accessOptions?: AccessOptions): Promise { const resp = await this.request('POST', url, body, requestOptions, accessOptions?.accessNames, accessOptions?.noCache); if (resp[1].status !== 200) { return this.handleError(resp[1]); } return resp[0] as T; } async put(url: string, body: JSONValue | ArrayBuffer, requestOptions: RequestOptions, accessOptions?: AccessOptions): Promise { const resp = await this.request('PUT', url, body, requestOptions, accessOptions?.accessNames, accessOptions?.noCache); if (resp[1].status !== 200) { return this.handleError(resp[1]); } return resp[0] as T; } async delete(url: string, requestOptions: RequestOptions, accessOptions?: AccessOptions): Promise { const resp = await this.request('DELETE', url, undefined, requestOptions, accessOptions?.accessNames, accessOptions?.noCache); if (resp[1].status !== 200) { return this.handleError(resp[1]); } return resp[0] as T; } async request( method: 'GET' | 'POST' | 'PUT' | 'DELETE', url: string, body: JSONValue | ArrayBuffer | undefined, options: RequestOptions, accesses?: string[], noCache = false, headers?: { [_: string]: string }): Promise<[T | undefined, Response]> { if (!accesses) { accesses = this.layerProfile.accessGroups.map((ag) => ag.name); } let reqBody = body; if (typeof reqBody !== 'undefined' && options.request === 'json') { reqBody = JSON.stringify(reqBody); } const requestHeaders = this.getAuthHeaders(accesses); for (const e of Object.entries(headers ?? {})) { if (typeof e[1] === 'undefined') { continue; } requestHeaders[e[0]] = e[1]; } if (noCache) { requestHeaders['x-vy-bypass-cache'] = '1'; } const response = await request({ method, url, headers: requestHeaders, body: reqBody as RequestBody | undefined }); let respBody = response.body as ArrayBuffer | JSONValue | undefined; if (response.status >= 300) { return [undefined, response]; } if (typeof respBody !== 'undefined' && options.response === 'json') { respBody = JSON.parse(this._decoder.decode(respBody as ArrayBuffer)); } return [respBody as T, response]; } private handleError(resp: Response): SystemError { if (!resp.body) { return new SystemError(0, `unknown error, received status code ${resp.status}`); } const text = this._decoder.decode(resp.body); try { const err = JSON.parse(text); return new SystemError(err['status']['code'], err['status']['message']); } catch (e) { return new SystemError(0, `unknown error, received status code ${resp.status} and message ${text}`); } } private getAuthHeaders(accessNames: string[]): { [key: string]: string } { const headers: { [key: string]: string } = {}; let spaceTokens = ''; for (const accessName of accessNames) { const tokenAccess = this.layerProfile.getAccessGroup(accessName); if (!tokenAccess) { return headers; } for (const token of tokenAccess.tokens) { if (spaceTokens.length > 0) { spaceTokens += ','; } spaceTokens += token.token; } } if (spaceTokens) { headers['x-vy-layers'] = spaceTokens; } return headers; } } export function newSystemClient(url = 'https://api.vyze.io/system'): SystemClient { return new SystemClient(url); }