/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { ApiInParameterSpec, FieldSpec, TimeframeSpec } from '../schema/WebApiSchema.js'; import { DataTransform, PluginContext } from '../types/PluginContext.js'; import { SqupContext } from '../types/SqupContext.js'; import { Timeframe } from '../types/Timeframe.js'; import { referenceData } from './Render.js'; export type FormattedTimeFrame = Timeframe & { formattedStart: string | number; formattedEnd: string | number; }; export function FormatTimeframe( timeframe: Timeframe, timeframeSpec?: TimeframeSpec ): FormattedTimeFrame { const format = timeframeSpec?.format || 'ISO'; switch (format) { case 'ISO': return { ...timeframe, formattedStart: timeframe.start, formattedEnd: timeframe.end }; case 'Unix': { return { ...timeframe, formattedStart: timeframe.unixStart, formattedEnd: timeframe.unixEnd }; } default: { throw new Error(`Invalid timeframe format: ${format}`); } } } export function GetPageSize( pluginContext: PluginContext, squpContext: SqupContext, pageSizeParam?: ApiInParameterSpec ): number { const pageSize = !Number.isNaN(Number(pageSizeParam?.suggestedValue)) && pageSizeParam?.suggestedValue ? Number(pageSizeParam.suggestedValue) : 100; return pageSize; } export function NormalizeNextUrl(nextUrl: string, baseUrl: string) { if (nextUrl.match(/^https?:/u)) { return nextUrl; } else if (nextUrl.startsWith('/')){ return baseUrl + nextUrl; }else{ throw new Error('Unsupported next url'); } } export function handleSimpleField( pluginContext: PluginContext, field: FieldSpec, output: Record, replacements: Record = {} ): void { const val = referenceData(field.value, pluginContext, replacements); if (val === undefined) { return; } output[field.name] = val; if (field.dataTransform) { output[field.name] = ApplyDataTransform(pluginContext, field.dataTransform, output[field.name]); } if (field.convertToString) { output[field.name] = output[field.name].toString(); } else if (field.convertToDate) { switch (field.dateFormat) { case 'ISO': { const date = new Date(output[field.name]); output[field.name] = Math.floor(date.getTime() / 1000); break; } default: { // Do nothing for Unix time stamps break; } } } if (field.idForSourceId && replacements.targetNodeIdsBySourceId) { const nid = replacements.targetNodeIdsBySourceId.get(output[field.name]); if (nid) { output[field.idForSourceId] = nid; } } } export function ApplyDataTransform( pluginContext: PluginContext, dataTransformName: string, fromValue: any): any { if (!pluginContext.dataTransformsByName) { pluginContext.dataTransformsByName = pluginContext.dataTransforms.reduce( (acc, val) => acc.set(val.name, { dataTransformSpec: val }), new Map() ); } const dataTransform = pluginContext.dataTransformsByName!.get(dataTransformName); if (!dataTransform) { throw new Error(`Unknown data transform name: "${dataTransformName}"`); } if (dataTransform.dataTransformSpec.flags) { let remaining: number | undefined = undefined; if (typeof fromValue === 'number') { remaining = fromValue; } else if (typeof fromValue === 'string') { try { remaining = parseInt(fromValue); } catch {/* ignore */} } if (remaining !== undefined && Number.isSafeInteger(remaining)) { // Break down flags let result = ''; for (const { from, to } of dataTransform.dataTransformSpec.map) { if (remaining & from) { const sep = result ? ',' : ''; result = `${result}${sep}${to}`; remaining = remaining & ~from; } } if (remaining) { const sep = result ? ',' : ''; result = `${result}${sep}0x${remaining.toString(16)}`; } return result; } else { return fromValue; } } else { if (!dataTransform.map) { dataTransform.map = dataTransform.dataTransformSpec.map.reduce( (acc: Map, val: any) => acc.set(val.from, val.to), new Map() ); } return dataTransform.map!.has(fromValue) ? dataTransform.map!.get(fromValue) : fromValue; } } export async function timeArrived(when: Date) { const ms = Math.max(0, when.getTime() - Date.now()); return new Promise(resolve => setTimeout(resolve, ms)); } export function mergeBodies(body1: Record, body2: Record) { const result = { ...body1 }; if (body1 == null || typeof body1 !== 'object') { throw new Error('mergeBodies: body1 is not a valid object'); } if (body2 == null || typeof body2 !== 'object') { throw new Error('mergeBodies: body2 is not a valid object'); } for (const [key, value] of Object.entries(body2)) { if (Object.hasOwnProperty.call(result, key)) { // key is present in the result object created from body 1 and in body2 if (typeof value === 'object') { if (value !== null) { // The value in body2 is a non-null object if (typeof result[key] === 'object') { if (result[key] !== null) { // The value in body1 is a non-null object - merge the two result[key] = mergeBodies(result[key] as Record, value as Record); } else { // The value in body1 is null - overwrite the value in result result[key] = value; } } else { // The value in body1 is not an object - overwrite the value in result result[key] = value; } } else { // The value in body2 is null - overwrite the value in result result[key] = value; } } else { // The value in body2 is a primitive, overwrite the value in result if (value === undefined) { delete result[key]; } else { result[key] = value; } } } else { if (value !== undefined) { // key is not present in the result object created from body 1, create it from the value now result[key] = value; } } } return result; }