## Description - Remove new relic browser agent - Add faro sdk to capture frontend perf metrics and traces. Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/12490844984> > Commit: c9d4264027467bf33e1de519eb69c7762b6e7f75 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12490844984&attempt=2" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Wed, 25 Dec 2024 09:33:26 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Introduced new environment variable `APPSMITH_HOSTNAME` for dynamic hostname configuration in HTML files. - Enhanced telemetry capabilities with new imports and updated types for better observability. - Added `tracingUrl` under the observability section in configuration files for improved telemetry tracking. - **Bug Fixes** - Adjusted telemetry data handling to utilize new `Attributes` type for improved consistency. - **Documentation** - Updated import paths for various telemetry-related components to reflect new module organization. - **Chores** - Removed deprecated telemetry configurations and streamlined build processes. - Updated Nginx configuration to reflect new telemetry parameters. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
483 lines
12 KiB
TypeScript
483 lines
12 KiB
TypeScript
import type { ApiResponse } from "api/ApiResponses";
|
|
import LibraryApi from "api/LibraryAPI";
|
|
import { createMessage, customJSLibraryMessages } from "ee/constants/messages";
|
|
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
|
|
import {
|
|
ReduxActionErrorTypes,
|
|
ReduxActionTypes,
|
|
} from "ee/constants/ReduxActionConstants";
|
|
import type { ActionPattern } from "redux-saga/effects";
|
|
import {
|
|
actionChannel,
|
|
all,
|
|
call,
|
|
put,
|
|
select,
|
|
take,
|
|
takeEvery,
|
|
takeLatest,
|
|
} from "redux-saga/effects";
|
|
import { getCurrentApplicationId } from "selectors/editorSelectors";
|
|
import CodemirrorTernService from "utils/autocomplete/CodemirrorTernService";
|
|
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
|
|
import { validateResponse } from "./ErrorSagas";
|
|
import { evalWorker as EvalWorker } from "utils/workerInstances";
|
|
import log from "loglevel";
|
|
import { APP_MODE } from "entities/App";
|
|
import { getAppMode } from "ee/selectors/applicationSelectors";
|
|
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
|
|
import type { JSLibrary } from "workers/common/JSLibrary";
|
|
import { getUsedActionNames } from "selectors/actionSelectors";
|
|
import AppsmithConsole from "utils/AppsmithConsole";
|
|
import { selectInstalledLibraries } from "ee/selectors/entitiesSelector";
|
|
import { toast } from "@appsmith/ads";
|
|
import { endSpan, startRootSpan } from "instrumentation/generateTraces";
|
|
import { getFromServerWhenNoPrefetchedResult } from "./helper";
|
|
|
|
export function parseErrorMessage(text: string) {
|
|
return text.split(": ").slice(1).join("");
|
|
}
|
|
|
|
function* handleInstallationFailure(
|
|
url: string,
|
|
message: string,
|
|
accessor?: string[],
|
|
) {
|
|
if (accessor) {
|
|
yield call(
|
|
EvalWorker.request,
|
|
EVAL_WORKER_ACTIONS.UNINSTALL_LIBRARY,
|
|
accessor,
|
|
);
|
|
}
|
|
|
|
AppsmithConsole.error({
|
|
text: `Failed to install library script at ${url}`,
|
|
});
|
|
|
|
const applicationid: ReturnType<typeof getCurrentApplicationId> =
|
|
yield select(getCurrentApplicationId);
|
|
|
|
yield put({
|
|
type: ReduxActionErrorTypes.INSTALL_LIBRARY_FAILED,
|
|
payload: {
|
|
url,
|
|
show: true,
|
|
message: message || `Failed to install library script at ${url}`,
|
|
},
|
|
});
|
|
AnalyticsUtil.logEvent("INSTALL_LIBRARY", {
|
|
url,
|
|
success: false,
|
|
applicationid,
|
|
});
|
|
log.error(message);
|
|
}
|
|
|
|
export function* installLibrarySaga(lib: Partial<JSLibrary>) {
|
|
const { url } = lib;
|
|
|
|
const takenNamesMap: Record<string, true> = yield select(
|
|
getUsedActionNames,
|
|
"",
|
|
);
|
|
|
|
const installedLibraries: JSLibrary[] = yield select(
|
|
selectInstalledLibraries,
|
|
);
|
|
|
|
const alreadyInstalledLibrary = installedLibraries.find(
|
|
(library) => library.url === url,
|
|
);
|
|
|
|
if (alreadyInstalledLibrary) {
|
|
toast.show(
|
|
createMessage(
|
|
customJSLibraryMessages.INSTALLED_ALREADY,
|
|
alreadyInstalledLibrary.accessor,
|
|
),
|
|
{
|
|
kind: "info",
|
|
},
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
const takenAccessors = ([] as string[]).concat(
|
|
...installedLibraries.map((lib) => lib.accessor),
|
|
);
|
|
|
|
const { accessor, defs, error, success } = yield call(
|
|
EvalWorker.request,
|
|
EVAL_WORKER_ACTIONS.INSTALL_LIBRARY,
|
|
{
|
|
url,
|
|
takenNamesMap,
|
|
takenAccessors,
|
|
},
|
|
);
|
|
|
|
if (!success) {
|
|
log.debug("Failed to install locally");
|
|
yield call(handleInstallationFailure, url as string, error?.message);
|
|
|
|
return;
|
|
}
|
|
|
|
const name: string = lib.name || accessor[accessor.length - 1];
|
|
const applicationId: string = yield select(getCurrentApplicationId);
|
|
|
|
const versionMatch = (url as string).match(/(?:@)(\d+\.)(\d+\.)(\d+)/);
|
|
let [version = ""] = versionMatch ? versionMatch : [];
|
|
|
|
version = version.startsWith("@") ? version.slice(1) : version;
|
|
version = version || lib?.version || "";
|
|
|
|
let stringifiedDefs = "";
|
|
|
|
try {
|
|
stringifiedDefs = JSON.stringify(defs);
|
|
} catch (e) {
|
|
stringifiedDefs = JSON.stringify({
|
|
"!name": `LIB/${accessor[accessor.length - 1]}`,
|
|
});
|
|
}
|
|
|
|
const response: ApiResponse<boolean> = yield call(
|
|
LibraryApi.addLibrary,
|
|
applicationId,
|
|
{
|
|
name,
|
|
version,
|
|
accessor,
|
|
defs: stringifiedDefs,
|
|
url,
|
|
},
|
|
);
|
|
|
|
try {
|
|
const isValidResponse: boolean = yield validateResponse(response, false);
|
|
|
|
if (!isValidResponse || !response.data) {
|
|
log.debug("Install API failed");
|
|
yield call(handleInstallationFailure, url as string, "", accessor);
|
|
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
yield call(
|
|
handleInstallationFailure,
|
|
url as string,
|
|
(e as Error).message,
|
|
accessor,
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
CodemirrorTernService.updateDef(defs["!name"], defs);
|
|
AnalyticsUtil.logEvent("DEFINITIONS_GENERATION", { url, success: true });
|
|
} catch (e) {
|
|
toast.show(
|
|
createMessage(customJSLibraryMessages.AUTOCOMPLETE_FAILED, name),
|
|
{
|
|
kind: "warning",
|
|
},
|
|
);
|
|
AppsmithConsole.warning({
|
|
text: `Failed to generate code definitions for ${name}`,
|
|
});
|
|
AnalyticsUtil.logEvent("DEFINITIONS_GENERATION", { url, success: false });
|
|
log.debug("Failed to update Tern defs", e);
|
|
}
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.UPDATE_LINT_GLOBALS,
|
|
payload: {
|
|
libs: [
|
|
{
|
|
name,
|
|
version,
|
|
url,
|
|
accessor,
|
|
},
|
|
],
|
|
add: true,
|
|
},
|
|
});
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.INSTALL_LIBRARY_SUCCESS,
|
|
payload: {
|
|
url,
|
|
accessor,
|
|
version,
|
|
name,
|
|
},
|
|
});
|
|
|
|
toast.show(
|
|
createMessage(
|
|
customJSLibraryMessages.INSTALLATION_SUCCESSFUL,
|
|
accessor[accessor.length - 1],
|
|
),
|
|
{
|
|
kind: "success",
|
|
},
|
|
);
|
|
AnalyticsUtil.logEvent("INSTALL_LIBRARY", {
|
|
url,
|
|
namespace: accessor.join("."),
|
|
success: true,
|
|
applicationId,
|
|
});
|
|
|
|
AppsmithConsole.info({
|
|
text: `${name} installed successfully`,
|
|
});
|
|
}
|
|
|
|
function* uninstallLibrarySaga(action: ReduxAction<JSLibrary>) {
|
|
const { accessor, name } = action.payload;
|
|
const applicationId: string = yield select(getCurrentApplicationId);
|
|
|
|
try {
|
|
const response: ApiResponse = yield call(
|
|
LibraryApi.removeLibrary,
|
|
applicationId,
|
|
action.payload,
|
|
);
|
|
|
|
const isValidResponse: boolean = yield validateResponse(response);
|
|
|
|
if (!isValidResponse) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.UNINSTALL_LIBRARY_FAILED,
|
|
payload: {
|
|
show: true,
|
|
accessor,
|
|
error: {
|
|
message: createMessage(
|
|
customJSLibraryMessages.UNINSTALL_FAILED,
|
|
name,
|
|
),
|
|
},
|
|
},
|
|
});
|
|
AnalyticsUtil.logEvent("UNINSTALL_LIBRARY", {
|
|
url: action.payload.url,
|
|
success: false,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.UPDATE_LINT_GLOBALS,
|
|
payload: {
|
|
libs: [action.payload],
|
|
add: false,
|
|
},
|
|
});
|
|
|
|
const { success }: { success: boolean } = yield call(
|
|
EvalWorker.request,
|
|
EVAL_WORKER_ACTIONS.UNINSTALL_LIBRARY,
|
|
accessor,
|
|
);
|
|
|
|
if (!success) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.UNINSTALL_LIBRARY_FAILED,
|
|
payload: {
|
|
accessor,
|
|
show: true,
|
|
error: {
|
|
message: createMessage(
|
|
customJSLibraryMessages.UNINSTALL_FAILED,
|
|
name,
|
|
),
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
try {
|
|
CodemirrorTernService.removeDef(`LIB/${accessor[accessor.length - 1]}`);
|
|
} catch (e) {
|
|
log.debug(`Failed to remove definitions for ${name}`, e);
|
|
}
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.UNINSTALL_LIBRARY_SUCCESS,
|
|
payload: action.payload,
|
|
});
|
|
|
|
toast.show(createMessage(customJSLibraryMessages.UNINSTALL_SUCCESS, name), {
|
|
kind: "success",
|
|
});
|
|
AnalyticsUtil.logEvent("UNINSTALL_LIBRARY", {
|
|
url: action.payload.url,
|
|
success: true,
|
|
});
|
|
} catch (e) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.UNINSTALL_LIBRARY_FAILED,
|
|
payload: {
|
|
accessor,
|
|
show: true,
|
|
error: {
|
|
message: createMessage(
|
|
customJSLibraryMessages.UNINSTALL_FAILED,
|
|
name,
|
|
),
|
|
},
|
|
},
|
|
});
|
|
AnalyticsUtil.logEvent("UNINSTALL_LIBRARY", {
|
|
url: action.payload.url,
|
|
success: false,
|
|
});
|
|
}
|
|
}
|
|
|
|
function* fetchJSLibraries(
|
|
action: ReduxAction<{
|
|
applicationId: string;
|
|
customJSLibraries: ApiResponse;
|
|
}>,
|
|
) {
|
|
const span = startRootSpan("fetchJSLibraries");
|
|
const { applicationId, customJSLibraries } = action.payload;
|
|
const mode: APP_MODE = yield select(getAppMode);
|
|
|
|
try {
|
|
const response: ApiResponse = yield call(
|
|
getFromServerWhenNoPrefetchedResult,
|
|
customJSLibraries,
|
|
() => call(LibraryApi.getLibraries, applicationId, mode),
|
|
);
|
|
|
|
const isValidResponse: boolean = yield validateResponse(response);
|
|
|
|
if (!isValidResponse) {
|
|
endSpan(span);
|
|
|
|
return;
|
|
}
|
|
|
|
const libraries = response.data as Array<JSLibrary & { defs: string }>;
|
|
|
|
const { message, success }: { success: boolean; message: string } =
|
|
yield call(
|
|
EvalWorker.request,
|
|
EVAL_WORKER_ACTIONS.LOAD_LIBRARIES,
|
|
libraries.map((lib) => ({
|
|
name: lib.name,
|
|
version: lib.version,
|
|
url: lib.url,
|
|
accessor: lib.accessor,
|
|
})),
|
|
);
|
|
|
|
if (!success) {
|
|
if (mode === APP_MODE.EDIT) {
|
|
yield put({
|
|
type: ReduxActionTypes.FETCH_JS_LIBRARIES_SUCCESS,
|
|
payload: libraries.map((lib) => ({
|
|
name: lib.name,
|
|
accessor: lib.accessor,
|
|
version: lib.version,
|
|
url: lib.url,
|
|
docsURL: lib.docsURL,
|
|
})),
|
|
});
|
|
toast.show(parseErrorMessage(message), {
|
|
kind: "warning",
|
|
});
|
|
} else {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.FETCH_JS_LIBRARIES_FAILED,
|
|
});
|
|
}
|
|
|
|
endSpan(span);
|
|
|
|
return;
|
|
}
|
|
|
|
if (mode === APP_MODE.EDIT) {
|
|
for (const lib of libraries) {
|
|
try {
|
|
const defs = JSON.parse(lib.defs);
|
|
|
|
CodemirrorTernService.updateDef(defs["!name"], defs);
|
|
} catch (e) {
|
|
toast.show(
|
|
createMessage(
|
|
customJSLibraryMessages.AUTOCOMPLETE_FAILED,
|
|
lib.name,
|
|
),
|
|
{
|
|
kind: "info",
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.UPDATE_LINT_GLOBALS,
|
|
payload: {
|
|
libs: libraries,
|
|
},
|
|
});
|
|
}
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.FETCH_JS_LIBRARIES_SUCCESS,
|
|
payload: libraries.map((lib) => ({
|
|
name: lib.name,
|
|
accessor: lib.accessor,
|
|
version: lib.version,
|
|
url: lib.url,
|
|
docsURL: lib.docsURL,
|
|
id: lib.id,
|
|
})),
|
|
});
|
|
} catch (e) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.FETCH_JS_LIBRARIES_FAILED,
|
|
});
|
|
}
|
|
endSpan(span);
|
|
}
|
|
|
|
function* startInstallationRequestChannel() {
|
|
// TODO: Fix this the next time the file is edited
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const queueInstallChannel: ActionPattern<any> = yield actionChannel([
|
|
ReduxActionTypes.INSTALL_LIBRARY_INIT,
|
|
]);
|
|
|
|
while (true) {
|
|
const action: ReduxAction<Partial<JSLibrary>> =
|
|
yield take(queueInstallChannel);
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.INSTALL_LIBRARY_START,
|
|
payload: action.payload.url,
|
|
});
|
|
yield call(installLibrarySaga, action.payload);
|
|
}
|
|
}
|
|
|
|
export default function* () {
|
|
yield all([
|
|
takeEvery(ReduxActionTypes.UNINSTALL_LIBRARY_INIT, uninstallLibrarySaga),
|
|
takeLatest(ReduxActionTypes.FETCH_JS_LIBRARIES_INIT, fetchJSLibraries),
|
|
call(startInstallationRequestChannel),
|
|
]);
|
|
}
|