import { Logs } from '@aimee-blue/ab-contracts';
import { time } from '../time';
import * as PubSub from '../pubsub';
import { appVersion, appName } from '../app';
import { Observable, merge, empty, defer } from 'rxjs';
import {
ignoreElements,
catchError,
filter,
publish,
mergeMap,
map,
} from 'rxjs/operators';
import { IAction } from '../action';
import { timesRegistered } from './streams';
import { BasicLogger, defaultBasicLogger } from '../logging';
interface ISendParams {
event: string;
traceKey?: string;
data?: {
[key: string]: unknown;
};
}
export const sendOne = async (params: ISendParams) => {
const timestamp = await time();
const version = await appVersion();
const name = await appName();
const data: Logs.ILogParams = {
event: params.event,
data: {
version,
appName: name,
...params.data,
},
source: 'server',
timestamp,
...(params.traceKey && {
traceKey: params.traceKey,
}),
};
return PubSub.publish('profiler', data);
};
function optionalFilter(
predicate?: (action: A) => action is AOut
) {
return (stream: Observable) => {
if (predicate) {
return stream.pipe(filter(predicate));
} else {
return stream;
}
};
}
export interface ISendActionsParams {
event?: string;
traceKey?: string;
filter?: (action: A) => action is AOut;
transform?: (action: A, params?: ISendActionsParams) => ISendParams;
logger?: BasicLogger;
}
function defaultTransform(
action: IAction,
params?: ISendActionsParams
): ISendParams {
const { filter: _, transform: __, ...logParams } = params || {};
return {
event: action.type,
data: (action as unknown) as { [key: string]: unknown },
...logParams,
};
}
export const sendActions = (
params?: ISendActionsParams,
deps = {
sendOne,
}
) => (input: Observable) =>
input.pipe(
publish(shared =>
merge(
shared,
shared.pipe(
optionalFilter(params && params.filter),
mergeMap(action =>
defer(() =>
deps.sendOne(
((params && params.transform) || defaultTransform)(
action,
params
)
)
).pipe(
catchError(err => {
(params?.logger ?? defaultBasicLogger()).error(
'💥 An error when profiling actions',
err
);
return empty();
})
)
),
ignoreElements()
)
)
)
);
export function sendAllTimings() {
return timesRegistered().pipe(
map(timing => ({
type: `PROFILER/${timing.name.toUpperCase()}`,
timeTook: timing.time,
...timing.details,
})),
sendActions(),
ignoreElements()
);
}