chore: Log JS Function execution (#24163)

This commit is contained in:
Favour Ohanekwu 2023-06-09 06:41:07 +01:00 committed by GitHub
parent 1c701ba1f7
commit 5db37b871e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 22 deletions

View File

@ -5,6 +5,7 @@ import type {
ExecuteTriggerPayload,
TriggerSource,
} from "constants/AppsmithActionConstants/ActionConstants";
import { TriggerKind } from "constants/AppsmithActionConstants/ActionConstants";
import * as log from "loglevel";
import { all, call, put, takeEvery, takeLatest } from "redux-saga/effects";
import {
@ -37,6 +38,7 @@ import type { ActionDescription } from "@appsmith/workers/Evaluation/fns";
export type TriggerMeta = {
source?: TriggerSource;
triggerPropertyName?: string;
triggerKind?: TriggerKind;
};
/**
@ -123,7 +125,11 @@ export function* executeAppAction(payload: ExecuteTriggerPayload): any {
evaluateAndExecuteDynamicTrigger,
dynamicString,
type,
{ source, triggerPropertyName },
{
source,
triggerPropertyName,
triggerKind: TriggerKind.EVENT_EXECUTION,
},
callbackData,
globalContext,
);

View File

@ -26,6 +26,10 @@ export type TriggerSource = {
isJSAction?: boolean;
actionId?: string;
};
export enum TriggerKind {
EVENT_EXECUTION = "EVENT_EXECUTION", // Eg. Button onClick
JS_FUNCTION_EXECUTION = "JS_FUNCTION_EXECUTION", // Executing js function from jsObject page
}
export type ExecuteTriggerPayload = {
dynamicString: string;

View File

@ -30,6 +30,7 @@ export type UpdateDataTreeMessageData = {
import { sortJSExecutionDataByCollectionId } from "workers/Evaluation/JSObject/utils";
import type { LintTreeSagaRequestData } from "workers/Linting/types";
import AnalyticsUtil from "utils/AnalyticsUtil";
export function* handleEvalWorkerRequestSaga(listenerChannel: Channel<any>) {
while (true) {
@ -110,6 +111,19 @@ export function* processTriggerHandler(message: any) {
if (messageType === MessageType.REQUEST)
yield call(evalWorker.respond, message.messageId, result);
}
export function* handleJSExecutionLog(data: TMessage<{ data: string[] }>) {
const {
body: { data: executedFns },
} = data;
for (const executedFn of executedFns) {
AnalyticsUtil.logEvent("EXECUTE_ACTION", {
type: "JS",
name: executedFn,
});
}
yield call(logJSFunctionExecution, data);
}
export function* handleEvalWorkerMessage(message: TMessage<any>) {
const { body } = message;
@ -136,7 +150,7 @@ export function* handleEvalWorkerMessage(message: TMessage<any>) {
break;
}
case MAIN_THREAD_ACTION.LOG_JS_FUNCTION_EXECUTION: {
yield call(logJSFunctionExecution, message);
yield call(handleJSExecutionLog, message);
break;
}
case MAIN_THREAD_ACTION.PROCESS_BATCHED_TRIGGERS: {

View File

@ -65,7 +65,10 @@ import {
} from "actions/globalSearchActions";
import type { TriggerMeta } from "@appsmith/sagas/ActionExecution/ActionExecutionSagas";
import { executeActionTriggers } from "@appsmith/sagas/ActionExecution/ActionExecutionSagas";
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
import {
EventType,
TriggerKind,
} from "constants/AppsmithActionConstants/ActionConstants";
import {
createMessage,
SNIPPET_EXECUTION_FAILED,
@ -401,6 +404,7 @@ function* executeAsyncJSFunction(
type: ENTITY_TYPE.JSACTION,
},
triggerPropertyName: `${collectionName}.${action.name}`,
triggerKind: TriggerKind.JS_FUNCTION_EXECUTION,
};
const eventType = EventType.ON_JS_FUNCTION_EXECUTE;
const response: JSFunctionExecutionResponse = yield call(

View File

@ -23,11 +23,13 @@ export default class ExecutionMetaData {
}
}
static getExecutionMetaData() {
const { source, triggerPropertyName } = ExecutionMetaData.triggerMeta || {};
const { source, triggerKind, triggerPropertyName } =
ExecutionMetaData.triggerMeta || {};
return {
triggerMeta: {
source: { ...source } as TriggerSource,
triggerPropertyName,
triggerKind,
},
eventType: ExecutionMetaData.eventType,
enableJSVarUpdateTracking: ExecutionMetaData.enableJSVarUpdateTracking,

View File

@ -147,13 +147,15 @@ TriggerEmitter.on(
jsVariableUpdatesHandlerWrapper,
);
export const fnInvokeLogHandler = priorityBatchedActionHandler((data) => {
const set = new Set([...data]);
WorkerMessenger.ping({
method: MAIN_THREAD_ACTION.LOG_JS_FUNCTION_EXECUTION,
data: [...set],
});
});
export const fnInvokeLogHandler = priorityBatchedActionHandler<string>(
(data) => {
const set = new Set([...data]);
WorkerMessenger.ping({
method: MAIN_THREAD_ACTION.LOG_JS_FUNCTION_EXECUTION,
data: [...set],
});
},
);
TriggerEmitter.on(BatchKey.process_batched_fn_invoke_log, fnInvokeLogHandler);

View File

@ -2,6 +2,7 @@ import { isPromise } from "workers/Evaluation/JSObject/utils";
import { postJSFunctionExecutionLog } from "@appsmith/workers/Evaluation/JSObject/postJSFunctionExecution";
import TriggerEmitter, { BatchKey } from "./TriggerEmitter";
import ExecutionMetaData from "./ExecutionMetaData";
import { TriggerKind } from "constants/AppsmithActionConstants/ActionConstants";
declare global {
interface Window {
@ -11,48 +12,91 @@ declare global {
) => any;
}
}
export type PostProcessorArg = {
executionMetaData: ReturnType<typeof ExecutionMetaData.getExecutionMetaData>;
jsFnFullName: string;
executionResponse: unknown;
};
export type PostProcessor = (args: PostProcessorArg) => void;
export interface JSExecutionData {
data: unknown;
funcName: string;
}
function saveExecutionData(name: string, data: unknown) {
function saveExecutionData({
executionResponse,
jsFnFullName,
}: PostProcessorArg) {
TriggerEmitter.emit(BatchKey.process_batched_fn_execution, {
name,
data,
name: jsFnFullName,
data: executionResponse,
});
}
function logJSExecution({ executionMetaData, jsFnFullName }: PostProcessorArg) {
switch (executionMetaData.triggerMeta.triggerKind) {
case TriggerKind.EVENT_EXECUTION: {
TriggerEmitter.emit(BatchKey.process_batched_fn_invoke_log, jsFnFullName);
break;
}
default: {
break;
}
}
postJSFunctionExecutionLog(jsFnFullName);
}
export function jsObjectFunctionFactory<P extends ReadonlyArray<unknown>>(
fn: (...args: P) => unknown,
name: string,
postProcessors: Array<(name: string, res: unknown) => void> = [
saveExecutionData,
postJSFunctionExecutionLog,
],
postProcessors: PostProcessor[] = [saveExecutionData, logJSExecution],
) {
return function (this: unknown, ...args: P) {
if (!ExecutionMetaData.getExecutionMetaData().enableJSFnPostProcessors) {
return fn.call(this, ...args);
}
const executionMetaData = ExecutionMetaData.getExecutionMetaData();
try {
const result = fn.call(this, ...args);
if (isPromise(result)) {
result.then((res) => {
postProcessors.forEach((p) => p(name, res));
postProcessors.forEach((p) =>
p({
executionMetaData,
jsFnFullName: name,
executionResponse: res,
}),
);
return res;
});
result.catch((e) => {
postProcessors.forEach((p) => p(name, undefined));
postProcessors.forEach((p) =>
p({
executionMetaData,
jsFnFullName: name,
executionResponse: undefined,
}),
);
throw e;
});
} else {
postProcessors.forEach((p) => p(name, result));
postProcessors.forEach((p) =>
p({
executionMetaData,
jsFnFullName: name,
executionResponse: result,
}),
);
}
return result;
} catch (e) {
postProcessors.forEach((postProcessor) => {
postProcessor(name, undefined);
postProcessor({
executionMetaData,
jsFnFullName: name,
executionResponse: undefined,
});
});
throw e;
}