diff --git a/.github/workflows/ci-test-custom-script.yml b/.github/workflows/ci-test-custom-script.yml index 2062ecb29ce9..575110b1b825 100644 --- a/.github/workflows/ci-test-custom-script.yml +++ b/.github/workflows/ci-test-custom-script.yml @@ -204,7 +204,6 @@ jobs: db_url=$(grep -oP 'APPSMITH_DB_URL=\K[^ ]+' cicontainerlocal/stacks/configuration/docker.env || echo "") if [[ -z "$db_url" ]]; then echo "::error::APPSMITH_DB_URL not found in the environment file" - exit 1 fi if [[ $db_url == "postgresql"* ]]; then echo "Database type: Postgres. Ensure PostgreSQL-specific configurations are in place." diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/Date_column_types_validation_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/Date_column_types_validation_spec.ts index 0ce8bc321a35..312ebb7c946e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/Date_column_types_validation_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/Date_column_types_validation_spec.ts @@ -43,17 +43,16 @@ describe( // Click unix cell edit table.ClickOnEditIcon(row, column); - // Click on specific date within + // Click on a specific date within the view port of the date picker + // Date picker opens in september 2024 due to the Table data set agHelper.GetNClick( - `${table._dateInputPopover} [aria-label='${table.getFormattedTomorrowDates().verboseFormat}']`, + `${table._dateInputPopover} [aria-label='Thu Sep 26 2024']`, ); - // Check that date is set in column + // Check that the date is set in column table .ReadTableRowColumnData(row, column, "v2") - .then((val) => - expect(val).to.equal(table.getFormattedTomorrowDates().isoFormat), - ); + .then((val) => expect(val).to.equal("2024-09-26")); }; it("1. should allow inline editing of Unix Timestamp in seconds (unix/s)", () => { diff --git a/app/client/cypress/support/Pages/Table.ts b/app/client/cypress/support/Pages/Table.ts index 45dd2132dedd..d1439a589271 100644 --- a/app/client/cypress/support/Pages/Table.ts +++ b/app/client/cypress/support/Pages/Table.ts @@ -854,38 +854,4 @@ export class Table { this.agHelper.GetHoverNClick(selector, 1, true); verify && cy.get(selector).eq(1).should("be.disabled"); } - - /** - * Helper function to get formatted date strings for tomorrow's date. - * - * @returns {Object} An object containing: - * - verbose format (e.g., "Sat Sep 21 2024") - * - ISO date format (e.g., "2024-09-21") - */ - public getFormattedTomorrowDates() { - // Create a new Date object for today - const tomorrow = new Date(); - - // Set the date to tomorrow by adding 1 to today's date - tomorrow.setDate(tomorrow.getDate() + 1); - - // Format tomorrow's date in verbose form (e.g., "Sat Sep 21 2024") - const verboseFormat = tomorrow - .toLocaleDateString("en-US", { - weekday: "short", - year: "numeric", - month: "short", - day: "2-digit", - }) - .replace(/,/g, ""); // Remove commas from the formatted string - - // Format tomorrow's date in ISO form (e.g., "2024-09-21") - const isoFormat = tomorrow.toISOString().split("T")[0]; // Extract the date part only - - // Return both formatted date strings as an object - return { - verboseFormat, - isoFormat, - }; - } } diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx b/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx index d6975d9750a2..40a50d43754f 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AIChat.tsx @@ -14,6 +14,7 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef) => { // assistantName, chatTitle, isWaitingForResponse = false, + onApplyAssistantSuggestion, onPromptChange, onSubmit, prompt, @@ -56,7 +57,12 @@ const _AIChat = (props: AIChatProps, ref: ForwardedRef) => { diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/AssistantSuggestionButton.tsx b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/AssistantSuggestionButton.tsx new file mode 100644 index 000000000000..e28f98c1228e --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/AssistantSuggestionButton.tsx @@ -0,0 +1,18 @@ +import { Text } from "@appsmith/wds"; +import { clsx } from "clsx"; +import React from "react"; +import { Button as HeadlessButton } from "react-aria-components"; +import styles from "./styles.module.css"; +import type { AssistantSuggestionButtonProps } from "./types"; + +export const AssistantSuggestionButton = ({ + children, + className, + ...rest +}: AssistantSuggestionButtonProps) => { + return ( + + {children} + + ); +}; diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/index.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/index.ts new file mode 100644 index 000000000000..c9075961d613 --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/index.ts @@ -0,0 +1,2 @@ +export * from "./AssistantSuggestionButton"; +export * from "./types"; diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/styles.module.css b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/styles.module.css new file mode 100644 index 000000000000..6aa30119ff76 --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/styles.module.css @@ -0,0 +1,20 @@ +.root { + height: 30px; + padding: 0 var(--inner-spacing-4); + background-color: var(--bg-neutral-subtle-alt, #e7e8e8); + border-radius: var(--radius-inner-button, 1.8px); + + &:hover { + background-color: var(--bg-neutral-subtle-alt-hover, #f0f1f1); + } + + &:focus-visible { + box-shadow: + 0 0 0 2px var(--color-bg), + 0 0 0 4px var(--color-bd-focus); + } + + &:active { + background-color: var(--bg-neutral-subtle-alt-active, #e1e2e2); + } +} diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/types.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/types.ts new file mode 100644 index 000000000000..20186f75d246 --- /dev/null +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/AssistantSuggestionButton/types.ts @@ -0,0 +1,5 @@ +import type { PropsWithChildren } from "react"; +import type { ButtonProps as HeadlessButtonProps } from "react-aria-components"; + +export interface AssistantSuggestionButtonProps + extends PropsWithChildren {} diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx index 37ea1f1c7923..7b827d3f576d 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/ThreadMessage.tsx @@ -1,9 +1,10 @@ -import { Text } from "@appsmith/wds"; +import { Flex, Text } from "@appsmith/wds"; import { clsx } from "clsx"; import React from "react"; import Markdown from "react-markdown"; import SyntaxHighlighter from "react-syntax-highlighter"; import { monokai } from "react-syntax-highlighter/dist/cjs/styles/hljs"; +import { AssistantSuggestionButton } from "../AssistantSuggestionButton"; import { UserAvatar } from "../UserAvatar"; import styles from "./styles.module.css"; import type { ThreadMessageProps } from "./types"; @@ -12,6 +13,8 @@ export const ThreadMessage = ({ className, content, isAssistant, + onApplyAssistantSuggestion, + promptSuggestions = [], username, ...rest }: ThreadMessageProps) => { @@ -50,6 +53,25 @@ export const ThreadMessage = ({ {content} + + {promptSuggestions.length > 0 && ( + + {promptSuggestions.map((suggestion) => ( + onApplyAssistantSuggestion?.(suggestion)} + > + {suggestion} + + ))} + + )} ) : ( <> diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts index 8935dfe1e5c4..4459c37a5a94 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/ThreadMessage/types.ts @@ -4,4 +4,6 @@ export interface ThreadMessageProps extends HTMLProps { content: string; isAssistant: boolean; username: string; + promptSuggestions?: string[]; + onApplyAssistantSuggestion?: (suggestion: string) => void; } diff --git a/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts b/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts index 69572c5d6f83..e27a804b91cf 100644 --- a/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts +++ b/app/client/packages/design-system/widgets/src/components/AIChat/src/types.ts @@ -2,6 +2,7 @@ export interface ChatMessage { id: string; content: string; isAssistant: boolean; + promptSuggestions?: string[]; } export interface AIChatProps { @@ -15,4 +16,5 @@ export interface AIChatProps { isWaitingForResponse?: boolean; onPromptChange: (prompt: string) => void; onSubmit?: () => void; + onApplyAssistantSuggestion?: (suggestion: string) => void; } diff --git a/app/client/packages/rts/package.json b/app/client/packages/rts/package.json index e75769c5e5e4..53c429b9e2da 100644 --- a/app/client/packages/rts/package.json +++ b/app/client/packages/rts/package.json @@ -15,6 +15,9 @@ "start": "./start-server.sh" }, "dependencies": { + "@opentelemetry/instrumentation-http": "^0.53.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/semantic-conventions": "^1.27.0", "@shared/ast": "workspace:^", "axios": "^1.7.4", "express": "^4.20.0", diff --git a/app/client/packages/rts/src/instrumentation.ts b/app/client/packages/rts/src/instrumentation.ts new file mode 100644 index 000000000000..083c5ca6663d --- /dev/null +++ b/app/client/packages/rts/src/instrumentation.ts @@ -0,0 +1,50 @@ +import { + BatchSpanProcessor, + NodeTracerProvider, +} from "@opentelemetry/sdk-trace-node"; +import { Resource } from "@opentelemetry/resources"; +import { + ATTR_DEPLOYMENT_NAME, + ATTR_SERVICE_INSTANCE_ID, +} from "@opentelemetry/semantic-conventions/incubating"; +import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions"; +import { registerInstrumentations } from "@opentelemetry/instrumentation"; +import { HttpInstrumentation } from "@opentelemetry/instrumentation-http"; +import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto"; + +const provider = new NodeTracerProvider({ + resource: new Resource({ + [ATTR_DEPLOYMENT_NAME]: `${process.env.APPSMITH_DEPLOYMENT_NAME || "self-hosted"}`, + [ATTR_SERVICE_INSTANCE_ID]: `${process.env.HOSTNAME || "appsmith-0"}`, + [ATTR_SERVICE_NAME]: "rts", + }), +}); + +const nrTracesExporter = new OTLPTraceExporter({ + url: `${process.env.APPSMITH_NEW_RELIC_OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces`, + headers: { + "api-key": `${process.env.APPSMITH_NEW_RELIC_OTLP_LICENSE_KEY}`, + }, +}); + +registerInstrumentations({ + instrumentations: [new HttpInstrumentation()], +}); + +const batchSpanProcessor = new BatchSpanProcessor( + nrTracesExporter, + //Optional BatchSpanProcessor Configurations + { + // The maximum queue size. After the size is reached spans are dropped. + maxQueueSize: 100, + // The maximum batch size of every export. It must be smaller or equal to maxQueueSize. + maxExportBatchSize: 50, + // The interval between two consecutive exports + scheduledDelayMillis: 500, + // How long the export can run before it is cancelled + exportTimeoutMillis: 30000, + }, +); + +provider.addSpanProcessor(batchSpanProcessor); +provider.register(); diff --git a/app/client/packages/rts/src/server.ts b/app/client/packages/rts/src/server.ts index e0b0c3765dfd..b4d77949d4fa 100644 --- a/app/client/packages/rts/src/server.ts +++ b/app/client/packages/rts/src/server.ts @@ -1,3 +1,4 @@ +import "./instrumentation"; import http from "http"; import express from "express"; import { Server } from "socket.io"; diff --git a/app/client/src/PluginActionEditor/PluginActionContext.tsx b/app/client/src/PluginActionEditor/PluginActionContext.tsx index 42c0d4e67458..e37882c7d294 100644 --- a/app/client/src/PluginActionEditor/PluginActionContext.tsx +++ b/app/client/src/PluginActionEditor/PluginActionContext.tsx @@ -28,19 +28,27 @@ interface ChildrenProps { export const PluginActionContextProvider = ( props: ChildrenProps & PluginActionContextType, ) => { - const { action, children, datasource, editorConfig, plugin, settingsConfig } = - props; + const { + action, + actionResponse, + children, + datasource, + editorConfig, + plugin, + settingsConfig, + } = props; // using useMemo to avoid unnecessary renders const contextValue = useMemo( () => ({ action, + actionResponse, datasource, editorConfig, plugin, settingsConfig, }), - [action, datasource, editorConfig, plugin, settingsConfig], + [action, actionResponse, datasource, editorConfig, plugin, settingsConfig], ); return ( diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/APIEditorForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/APIEditorForm.tsx index 9c046e9119ce..a3065ba2e5e0 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/APIEditorForm.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/APIEditorForm.tsx @@ -10,13 +10,17 @@ import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; import Pagination from "pages/Editor/APIEditor/Pagination"; import { reduxForm } from "redux-form"; -import { useHandleRunClick } from "PluginActionEditor/hooks"; +import { + useHandleRunClick, + useAnalyticsOnRunClick, +} from "PluginActionEditor/hooks"; const FORM_NAME = API_EDITOR_FORM_NAME; const APIEditorForm = () => { const { action } = usePluginActionContext(); const { handleRunClick } = useHandleRunClick(); + const { callRunActionAnalytics } = useAnalyticsOnRunClick(); const theme = EditorTheme.LIGHT; const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); @@ -25,6 +29,11 @@ const APIEditorForm = () => { action.userPermissions, ); + const onTestClick = () => { + callRunActionAnalytics(); + handleRunClick(); + }; + return ( { paginationUiComponent={ diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx index f897d38246e9..211042452700 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx @@ -1,107 +1,87 @@ -import React, { useCallback, useRef } from "react"; +import React from "react"; import styled from "styled-components"; -import QueryEditor from "pages/Editor/APIEditor/GraphQL/QueryEditor"; -import VariableEditor from "pages/Editor/APIEditor/GraphQL/VariableEditor"; -import useHorizontalResize from "utils/hooks/useHorizontalResize"; -import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import classNames from "classnames"; -import { tailwindLayers } from "constants/Layers"; - -const ResizableDiv = styled.div` - display: flex; - height: 100%; - flex-shrink: 0; -`; +import { + CodeEditorBorder, + EditorModes, + EditorSize, + EditorTheme, + TabBehaviour, +} from "components/editorComponents/CodeEditor/EditorConfig"; +import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField"; +import { Section, Zone } from "pages/Editor/ActionForm"; +import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; +import FormLabel from "components/editorComponents/FormLabel"; const PostBodyContainer = styled.div` - display: flex; - height: 100%; - overflow: hidden; &&&& .CodeMirror { - height: 100%; - border-top: 1px solid var(--ads-v2-color-border); - border-bottom: 1px solid var(--ads-v2-color-border); - border-radius: 0; - padding: 0; - } - & .CodeMirror-scroll { - margin: 0px; - padding: 0px; - overflow: auto !important; + height: auto; + min-height: 250px; } `; -const ResizerHandler = styled.div<{ resizing: boolean }>` - width: 6px; - height: 100%; - margin-left: 2px; - border-right: 1px solid var(--ads-v2-color-border); - background: ${(props) => - props.resizing ? "var(--ads-v2-color-border)" : "transparent"}; - &:hover { - background: var(--ads-v2-color-border); - border-color: transparent; +const StyledFormLabel = styled(FormLabel)` + && { + margin-bottom: var(--ads-v2-spaces-2); + padding: 0; } `; -const DEFAULT_GRAPHQL_VARIABLE_WIDTH = 300; - interface Props { actionName: string; } +const EXPECTED_VARIABLE = { + type: "object", + example: + '{\n "name":"{{ inputName.property }}",\n "preference":"{{ dropdownName.property }}"\n}', + autocompleteDataType: AutocompleteDataType.OBJECT, +}; + function PostBodyData(props: Props) { const { actionName } = props; const theme = EditorTheme.LIGHT; - const resizeableRef = useRef(null); - const [variableEditorWidth, setVariableEditorWidth] = React.useState( - DEFAULT_GRAPHQL_VARIABLE_WIDTH, - ); - /** - * Variable Editor's resizeable handler for the changing of width - */ - const onVariableEditorWidthChange = useCallback((newWidth) => { - setVariableEditorWidth(newWidth); - }, []); - - const { onMouseDown, onMouseUp, onTouchStart, resizing } = - useHorizontalResize( - resizeableRef, - onVariableEditorWidthChange, - undefined, - true, - ); return ( - -
- -
- - - +
+ +
+ Query + +
+
+ +
+ Query variables + +
+
+
); } diff --git a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx index cfad25a5ee4c..5c0362be3190 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx @@ -1,10 +1,16 @@ -import React from "react"; +import React, { useCallback } from "react"; import { IDEToolbar } from "IDE"; import { Button, Menu, MenuContent, MenuTrigger, Tooltip } from "@appsmith/ads"; import { modText } from "utils/helpers"; import { usePluginActionContext } from "../PluginActionContext"; -import { useHandleRunClick } from "PluginActionEditor/hooks"; +import { + useBlockExecution, + useHandleRunClick, + useAnalyticsOnRunClick, +} from "PluginActionEditor/hooks"; import { useToggle } from "@mantine/hooks"; +import { useSelector } from "react-redux"; +import { isActionRunning } from "PluginActionEditor/store"; interface PluginActionToolbarProps { runOptions?: React.ReactNode; @@ -15,7 +21,15 @@ interface PluginActionToolbarProps { const PluginActionToolbar = (props: PluginActionToolbarProps) => { const { action } = usePluginActionContext(); const { handleRunClick } = useHandleRunClick(); + const { callRunActionAnalytics } = useAnalyticsOnRunClick(); const [isMenuOpen, toggleMenuOpen] = useToggle([false, true]); + const blockExecution = useBlockExecution(); + const isRunning = useSelector(isActionRunning(action.id)); + + const onRunClick = useCallback(() => { + callRunActionAnalytics(); + handleRunClick(); + }, [callRunActionAnalytics, handleRunClick]); return ( @@ -27,7 +41,13 @@ const PluginActionToolbar = (props: PluginActionToolbarProps) => { placement="topRight" showArrow={false} > - diff --git a/app/client/src/PluginActionEditor/hooks/index.ts b/app/client/src/PluginActionEditor/hooks/index.ts index 00460d54c4c0..c61530a08d8c 100644 --- a/app/client/src/PluginActionEditor/hooks/index.ts +++ b/app/client/src/PluginActionEditor/hooks/index.ts @@ -1,3 +1,5 @@ export { useActionSettingsConfig } from "ee/PluginActionEditor/hooks/useActionSettingsConfig"; export { useHandleDeleteClick } from "ee/PluginActionEditor/hooks/useHandleDeleteClick"; export { useHandleRunClick } from "ee/PluginActionEditor/hooks/useHandleRunClick"; +export { useBlockExecution } from "ee/PluginActionEditor/hooks/useBlockExecution"; +export { useAnalyticsOnRunClick } from "ee/PluginActionEditor/hooks/useAnalyticsOnRunClick"; diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 51a10762d293..22cfe873e309 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -68,25 +68,23 @@ export const fetchPageAction = ( export interface FetchPublishedPageActionPayload { pageId: string; bustCache?: boolean; - firstLoad?: boolean; pageWithMigratedDsl?: FetchPageResponse; } export interface FetchPublishedPageResourcesPayload { pageId: string; + basePageId: string; } export const fetchPublishedPageAction = ( pageId: string, bustCache = false, - firstLoad = false, pageWithMigratedDsl?: FetchPageResponse, ): ReduxAction => ({ type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT, payload: { pageId, bustCache, - firstLoad, pageWithMigratedDsl, }, }); @@ -299,12 +297,14 @@ export const clonePageSuccess = ({ // Fetches resources required for published page, currently only used for fetching actions // In future we can reuse this for fetching other page level resources in published mode -export const fetchPublishedPageResourcesAction = ( - pageId: string, -): ReduxAction => ({ +export const fetchPublishedPageResources = ({ + basePageId, + pageId, +}: FetchPublishedPageResourcesPayload): ReduxAction => ({ type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_INIT, payload: { pageId, + basePageId, }, }); @@ -675,21 +675,18 @@ export const setupPageAction = ( export interface SetupPublishedPageActionPayload { pageId: string; bustCache: boolean; - firstLoad: boolean; pageWithMigratedDsl?: FetchPageResponse; } export const setupPublishedPage = ( pageId: string, bustCache = false, - firstLoad = false, pageWithMigratedDsl?: FetchPageResponse, ): ReduxAction => ({ type: ReduxActionTypes.SETUP_PUBLISHED_PAGE_INIT, payload: { pageId, bustCache, - firstLoad, pageWithMigratedDsl, }, }); diff --git a/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx b/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx index 18c863ccbc78..4b8f04d18a50 100644 --- a/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx +++ b/app/client/src/ce/PluginActionEditor/components/PluginActionResponse/hooks/usePluginActionResponseTabs.tsx @@ -17,22 +17,30 @@ import DebuggerLogs from "components/editorComponents/Debugger/DebuggerLogs"; import { PluginType } from "entities/Action"; import { ApiResponse } from "PluginActionEditor/components/PluginActionResponse/components/ApiResponse"; import { ApiResponseHeaders } from "PluginActionEditor/components/PluginActionResponse/components/ApiResponseHeaders"; -import { noop } from "lodash"; import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import { getErrorCount } from "selectors/debuggerSelectors"; -import { getPluginActionDebuggerState } from "PluginActionEditor/store"; +import { + getPluginActionDebuggerState, + isActionRunning, +} from "PluginActionEditor/store"; import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers"; import useShowSchema from "components/editorComponents/ActionRightPane/useShowSchema"; import Schema from "components/editorComponents/Debugger/Schema"; import QueryResponseTab from "pages/Editor/QueryEditor/QueryResponseTab"; import type { SourceEntity } from "entities/AppsmithConsole"; import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; -import { useHandleRunClick } from "PluginActionEditor/hooks"; +import { + useBlockExecution, + useHandleRunClick, + useAnalyticsOnRunClick, +} from "PluginActionEditor/hooks"; +import useDebuggerTriggerClick from "components/editorComponents/Debugger/hooks/useDebuggerTriggerClick"; function usePluginActionResponseTabs() { const { action, actionResponse, datasource, plugin } = usePluginActionContext(); const { handleRunClick } = useHandleRunClick(); + const { callRunActionAnalytics } = useAnalyticsOnRunClick(); const IDEViewMode = useSelector(getIDEViewMode); const errorCount = useSelector(getErrorCount); @@ -42,8 +50,17 @@ function usePluginActionResponseTabs() { const { responseTabHeight } = useSelector(getPluginActionDebuggerState); + const onDebugClick = useDebuggerTriggerClick(); + const isRunning = useSelector(isActionRunning(action.id)); + const blockExecution = useBlockExecution(); + const tabs: BottomTab[] = []; + const onRunClick = () => { + callRunActionAnalytics(); + handleRunClick(); + }; + if (IDEViewMode === EditorViewMode.FullScreen) { tabs.push( { @@ -69,9 +86,9 @@ function usePluginActionResponseTabs() { @@ -83,10 +100,10 @@ function usePluginActionResponseTabs() { panelComponent: ( ), }, @@ -132,8 +149,8 @@ function usePluginActionResponseTabs() { actionName={action.name} actionSource={actionSource} currentActionConfig={action} - isRunning={false} - onRunClick={handleRunClick} + isRunning={isRunning} + onRunClick={onRunClick} runErrorMessage={""} // TODO /> ), diff --git a/app/client/src/ce/PluginActionEditor/hooks/useAnalyticsOnRunClick.ts b/app/client/src/ce/PluginActionEditor/hooks/useAnalyticsOnRunClick.ts new file mode 100644 index 000000000000..421b9ebcc63c --- /dev/null +++ b/app/client/src/ce/PluginActionEditor/hooks/useAnalyticsOnRunClick.ts @@ -0,0 +1,33 @@ +import { useCallback } from "react"; +import { useSelector } from "react-redux"; +import { usePluginActionContext } from "PluginActionEditor/PluginActionContext"; +import { getPageNameByPageId } from "ee/selectors/entitiesSelector"; +import AnalyticsUtil from "ee/utils/AnalyticsUtil"; + +function useAnalyticsOnRunClick() { + const { action, datasource, plugin } = usePluginActionContext(); + const pageName = useSelector((state) => + getPageNameByPageId(state, action.pageId), + ); + + const actionId = action.id; + const actionName = action.name; + const datasourceId = datasource?.id; + const pluginName = plugin.name; + const isMock = !!datasource?.isMock || false; // as mock db exists only for postgres and mongo plugins + + const callRunActionAnalytics = useCallback(() => { + AnalyticsUtil.logEvent("RUN_ACTION_CLICK", { + actionId, + actionName, + datasourceId, + pageName, + pluginName, + isMock, + }); + }, [actionId, actionName, datasourceId, pageName, pluginName, isMock]); + + return { callRunActionAnalytics }; +} + +export { useAnalyticsOnRunClick }; diff --git a/app/client/src/ce/PluginActionEditor/hooks/useBlockExecution.ts b/app/client/src/ce/PluginActionEditor/hooks/useBlockExecution.ts new file mode 100644 index 000000000000..e02bcc7231d0 --- /dev/null +++ b/app/client/src/ce/PluginActionEditor/hooks/useBlockExecution.ts @@ -0,0 +1,63 @@ +import { getHasExecuteActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; +import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; +import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; +import { DEFAULT_DATASOURCE_NAME } from "constants/ApiEditorConstants/ApiEditorConstants"; +import { UIComponentTypes } from "api/PluginApi"; +import { SQL_DATASOURCES } from "constants/QueryEditorConstants"; +import { usePluginActionContext } from "PluginActionEditor/PluginActionContext"; + +const useBlockExecution = () => { + const { action, plugin } = usePluginActionContext(); + const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); + const isExecutePermitted = getHasExecuteActionPermission( + isFeatureEnabled, + action?.userPermissions, + ); + + let actionBody = ""; + let blockExecution = false; + + // API Editor Constants + // this gets the url of the current action's datasource + const actionDatasourceUrl = + action.datasource.datasourceConfiguration?.url || ""; + const actionDatasourceUrlPath = action.actionConfiguration.path || ""; + // this gets the name of the current action's datasource + const actionDatasourceName = action.datasource.name || ""; + + // Query Editor Constants + if (!!action.actionConfiguration) { + if ("formData" in action.actionConfiguration) { + // if the action has a formData (the action is postUQI e.g. Oracle) + actionBody = action.actionConfiguration.formData?.body?.data || ""; + } else { + // if the action is pre UQI, the path is different e.g. mySQL + actionBody = action.actionConfiguration?.body || ""; + } + } + + if ( + [ + UIComponentTypes.ApiEditorForm, + UIComponentTypes.GraphQLEditorForm, + ].includes(plugin.uiComponent) + ) { + // if the url is empty and the action's datasource name is the default datasource name (this means the api does not have a datasource attached) + // or the user does not have permission, + // we block action execution. + blockExecution = + (!actionDatasourceUrl && + !actionDatasourceUrlPath && + actionDatasourceName === DEFAULT_DATASOURCE_NAME) || + !isExecutePermitted; + } else { + // if (the body is empty and the action is an sql datasource) or the user does not have permission, block action execution. + blockExecution = + (!actionBody && SQL_DATASOURCES.includes(plugin.name)) || + !isExecutePermitted; + } + + return blockExecution; +}; + +export { useBlockExecution }; diff --git a/app/client/src/ce/PluginActionEditor/hooks/useHandleRunClick.ts b/app/client/src/ce/PluginActionEditor/hooks/useHandleRunClick.ts index b44c80f5c618..62d8075bcbda 100644 --- a/app/client/src/ce/PluginActionEditor/hooks/useHandleRunClick.ts +++ b/app/client/src/ce/PluginActionEditor/hooks/useHandleRunClick.ts @@ -1,8 +1,8 @@ +import { useCallback } from "react"; +import { useDispatch } from "react-redux"; import { runAction } from "actions/pluginActionActions"; import type { PaginationField } from "api/ActionAPI"; import { usePluginActionContext } from "PluginActionEditor/PluginActionContext"; -import { useCallback } from "react"; -import { useDispatch } from "react-redux"; function useHandleRunClick() { const { action } = usePluginActionContext(); diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 5d9a68d41c5f..3d344202b8a4 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -985,6 +985,8 @@ const AppViewActionTypes = { SET_APP_VIEWER_HEADER_HEIGHT: "SET_APP_VIEWER_HEADER_HEIGHT", SET_APP_SIDEBAR_PINNED: "SET_APP_SIDEBAR_PINNED", FETCH_PUBLISHED_PAGE_RESOURCES_INIT: "FETCH_PUBLISHED_PAGE_RESOURCES_INIT", + FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS: + "FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS", }; const AppViewActionErrorTypes = { diff --git a/app/client/src/ce/sagas/PageSagas.tsx b/app/client/src/ce/sagas/PageSagas.tsx index 12e52ba930c5..aeee2a7b60c4 100644 --- a/app/client/src/ce/sagas/PageSagas.tsx +++ b/app/client/src/ce/sagas/PageSagas.tsx @@ -325,12 +325,47 @@ export function* fetchPageSaga(action: ReduxAction) { } } +export function* updateCanvasLayout(response: FetchPageResponse) { + // Wait for widget config to load before we can get the canvas payload + yield call(waitForWidgetConfigBuild); + // Get Canvas payload + const canvasWidgetsPayload = getCanvasWidgetsPayload(response); + + // resize main canvas + resizePublishedMainCanvasToLowestWidget(canvasWidgetsPayload.widgets); + // Update the canvas + yield put(initCanvasLayout(canvasWidgetsPayload)); + + // Since new page has new layout, we need to generate a data structure + // to compute dynamic height based on the new layout. + yield put(generateAutoHeightLayoutTreeAction(true, true)); +} + +export function* postFetchedPublishedPage( + response: FetchPageResponse, + pageId: string, +) { + // set current page + yield put( + updateCurrentPage( + pageId, + response.data.slug, + response.data.userPermissions, + ), + ); + // Clear any existing caches + yield call(clearEvalCache); + // Set url params + yield call(setDataUrl); + + yield call(updateCanvasLayout, response); +} + export function* fetchPublishedPageSaga( action: ReduxAction, ) { try { - const { bustCache, firstLoad, pageId, pageWithMigratedDsl } = - action.payload; + const { bustCache, pageId, pageWithMigratedDsl } = action.payload; const params = { pageId, bustCache }; const response: FetchPageResponse = yield call( @@ -342,41 +377,9 @@ export function* fetchPublishedPageSaga( const isValidResponse: boolean = yield validateResponse(response); if (isValidResponse) { - // Clear any existing caches - yield call(clearEvalCache); - // Set url params - yield call(setDataUrl); - // Wait for widget config to load before we can get the canvas payload - yield call(waitForWidgetConfigBuild); - // Get Canvas payload - const canvasWidgetsPayload = getCanvasWidgetsPayload(response); - - // resize main canvas - resizePublishedMainCanvasToLowestWidget(canvasWidgetsPayload.widgets); - // Update the canvas - yield put(initCanvasLayout(canvasWidgetsPayload)); - // set current page - yield put( - updateCurrentPage( - pageId, - response.data.slug, - response.data.userPermissions, - ), - ); + yield call(postFetchedPublishedPage, response, pageId); - // dispatch fetch page success yield put(fetchPublishedPageSuccess()); - - // Since new page has new layout, we need to generate a data structure - // to compute dynamic height based on the new layout. - yield put(generateAutoHeightLayoutTreeAction(true, true)); - - /* Currently, All Actions are fetched in initSagas and on pageSwitch we only fetch page - */ - // Hence, if is not isFirstLoad then trigger evaluation with execute pageLoad action - if (!firstLoad) { - yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); - } } } catch (error) { yield put({ @@ -392,9 +395,9 @@ export function* fetchPublishedPageResourcesSaga( action: ReduxAction, ) { try { - const { pageId } = action.payload; + const { basePageId, pageId } = action.payload; - const params = { defaultPageId: pageId }; + const params = { defaultPageId: basePageId }; const initConsolidatedApiResponse: ApiResponse = yield ConsolidatedPageLoadApi.getConsolidatedPageLoadDataView(params); @@ -410,10 +413,18 @@ export function* fetchPublishedPageResourcesSaga( // In future, we can reuse this saga to fetch other resources of the page like actionCollections etc const { publishedActions } = response; - // Sending applicationId as empty as we have publishedActions present, - // it won't call the actions view api with applicationId + yield call( + postFetchedPublishedPage, + response.pageWithMigratedDsl, + pageId, + ); + + // NOTE: fetchActionsForView is used here to update publishedActions in redux store and not to fetch actions again yield put(fetchActionsForView({ applicationId: "", publishedActions })); yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); + yield put({ + type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS, + }); } } catch (error) { yield put({ @@ -1425,21 +1436,13 @@ export function* setupPublishedPageSaga( action: ReduxAction, ) { try { - const { bustCache, firstLoad, pageId, pageWithMigratedDsl } = - action.payload; + const { bustCache, pageId, pageWithMigratedDsl } = action.payload; /* Added the first line for isPageSwitching redux state to be true when page is being fetched to fix scroll position issue. Added the second line for sync call instead of async (due to first line) as it was leading to issue with on page load actions trigger. */ - yield put( - fetchPublishedPageAction( - pageId, - bustCache, - firstLoad, - pageWithMigratedDsl, - ), - ); + yield put(fetchPublishedPageAction(pageId, bustCache, pageWithMigratedDsl)); yield take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS); yield put({ diff --git a/app/client/src/ce/sagas/__tests__/PageSaga.test.ts b/app/client/src/ce/sagas/__tests__/PageSaga.test.ts index 96dc534cc274..4fb55f74d31b 100644 --- a/app/client/src/ce/sagas/__tests__/PageSaga.test.ts +++ b/app/client/src/ce/sagas/__tests__/PageSaga.test.ts @@ -47,7 +47,6 @@ describe("ce/PageSaga", () => { pageWithMigratedDsl: mockResponse.data .pageWithMigratedDsl as FetchPageResponse, bustCache: false, - firstLoad: true, }, }; @@ -57,7 +56,6 @@ describe("ce/PageSaga", () => { fetchPublishedPageAction( action.payload.pageId, action.payload.bustCache, - action.payload.firstLoad, action.payload.pageWithMigratedDsl, ), ) diff --git a/app/client/src/ce/utils/analyticsUtilTypes.ts b/app/client/src/ce/utils/analyticsUtilTypes.ts index ec84c03efdf9..9bb8ea464f57 100644 --- a/app/client/src/ce/utils/analyticsUtilTypes.ts +++ b/app/client/src/ce/utils/analyticsUtilTypes.ts @@ -48,9 +48,7 @@ export type EventName = | "DELETE_SAAS" | "RUN_SAAS_API" | "SAVE_API_CLICK" - | "RUN_API" | "RUN_API_CLICK" - | "RUN_API_SHORTCUT" | "DELETE_API" | "IMPORT_API" | "EXPAND_API" @@ -59,9 +57,8 @@ export type EventName = | "ADD_API_PAGE" | "DUPLICATE_ACTION" | "DUPLICATE_ACTION_CLICK" - | "RUN_QUERY" | "RUN_QUERY_CLICK" - | "RUN_QUERY_SHORTCUT" + | "RUN_ACTION_CLICK" | "DELETE_QUERY" | "MOVE_API" | "3P_PROVIDER_CLICK" diff --git a/app/client/src/components/editorComponents/Debugger/LogItem.tsx b/app/client/src/components/editorComponents/Debugger/LogItem.tsx index 821002cb03de..b80d94c4fec2 100644 --- a/app/client/src/components/editorComponents/Debugger/LogItem.tsx +++ b/app/client/src/components/editorComponents/Debugger/LogItem.tsx @@ -395,4 +395,4 @@ function LogItem(props: LogItemProps) { ); } -export default LogItem; +export default React.memo(LogItem); diff --git a/app/client/src/components/propertyControls/ArrayComponent.tsx b/app/client/src/components/propertyControls/ArrayComponent.tsx new file mode 100644 index 000000000000..ec98932273b9 --- /dev/null +++ b/app/client/src/components/propertyControls/ArrayComponent.tsx @@ -0,0 +1,152 @@ +import { Button } from "@appsmith/ads"; +import { debounce } from "lodash"; +import React, { useCallback, useEffect, useState } from "react"; +import styled from "styled-components"; +import { ControlWrapper, InputGroup } from "./StyledControls"; + +function updateOptionLabel( + items: Array, + index: number, + updatedLabel: string, +) { + return items.map((option: T, optionIndex) => { + if (index !== optionIndex) { + return option; + } + + return updatedLabel; + }); +} + +const StyledBox = styled.div` + width: 10px; +`; + +type UpdateItemsFunction = ( + items: string[], + isUpdatedViaKeyboard?: boolean, +) => void; + +interface ArrayComponentProps { + items: string[]; + updateItems: UpdateItemsFunction; + addLabel?: string; +} + +const StyledInputGroup = styled(InputGroup)` + > .ads-v2-input__input-section > div { + flex: 1; + min-width: 0px; + } +`; + +export function ArrayComponent(props: ArrayComponentProps) { + const [renderItems, setRenderItems] = useState([]); + const [typing, setTyping] = useState(false); + const { items } = props; + + useEffect(() => { + let { items } = props; + + items = Array.isArray(items) ? items.slice() : []; + + items.length !== 0 && !typing && setRenderItems(items); + }, [props, items.length, renderItems.length, typing]); + + const debouncedUpdateItems = useCallback( + debounce((updatedItems: string[]) => { + props.updateItems(updatedItems, true); + }, 200), + [props.updateItems], + ); + + function updateKey(index: number, updatedKey: string) { + let { items } = props; + + items = Array.isArray(items) ? items : []; + const updatedItems = updateOptionLabel(items, index, updatedKey); + const updatedRenderItems = updateOptionLabel( + renderItems, + index, + updatedKey, + ); + + setRenderItems(updatedRenderItems); + debouncedUpdateItems(updatedItems); + } + + function deleteItem(index: number, isUpdatedViaKeyboard = false) { + let { items } = props; + + items = Array.isArray(items) ? items : []; + + const newItems = items.filter((o, i) => i !== index); + const newRenderItems = renderItems.filter((o, i) => i !== index); + + setRenderItems(newRenderItems); + props.updateItems(newItems, isUpdatedViaKeyboard); + } + + function addItem(e: React.MouseEvent) { + let { items } = props; + + items = Array.isArray(items) ? items.slice() : []; + + items.push(""); + + const updatedRenderItems = renderItems.slice(); + + updatedRenderItems.push(""); + + setRenderItems(updatedRenderItems); + props.updateItems(items, e.detail === 0); + } + + function onInputFocus() { + setTyping(true); + } + + function onInputBlur() { + setTyping(false); + } + + return ( + <> + {renderItems.map((item: string, index) => { + return ( + + updateKey(index, value)} + onFocus={onInputFocus} + value={item} + /> + + + + + ); +} diff --git a/app/client/src/components/propertyControls/ArrayControl.tsx b/app/client/src/components/propertyControls/ArrayControl.tsx new file mode 100644 index 000000000000..8dab080e041a --- /dev/null +++ b/app/client/src/components/propertyControls/ArrayControl.tsx @@ -0,0 +1,48 @@ +import { objectKeys } from "@appsmith/utils"; +import type { DropdownOption } from "components/constants"; +import React from "react"; +import { isDynamicValue } from "utils/DynamicBindingUtils"; +import { ArrayComponent } from "./ArrayComponent"; +import type { ControlData, ControlProps } from "./BaseControl"; +import BaseControl from "./BaseControl"; + +class ArrayControl extends BaseControl { + render() { + return ( + + ); + } + + updateItems = (items: string[], isUpdatedViaKeyboard = false) => { + this.updateProperty(this.props.propertyName, items, isUpdatedViaKeyboard); + }; + + static getControlType() { + return "ARRAY_INPUT"; + } + + static canDisplayValueInUI(_config: ControlData, value: string): boolean { + if (isDynamicValue(value)) return false; + + try { + const items: DropdownOption[] = JSON.parse(value); + + for (const x of items) { + const keys = objectKeys(x); + + if (!keys.includes("label") || !keys.includes("value")) { + return false; + } + } + } catch { + return false; + } + + return true; + } +} + +export default ArrayControl; diff --git a/app/client/src/components/propertyControls/index.ts b/app/client/src/components/propertyControls/index.ts index 6683cd30246b..cf85e900e04b 100644 --- a/app/client/src/components/propertyControls/index.ts +++ b/app/client/src/components/propertyControls/index.ts @@ -76,12 +76,14 @@ import type { IconSelectControlV2Props } from "./IconSelectControlV2"; import IconSelectControlV2 from "./IconSelectControlV2"; import PrimaryColumnsControlWDS from "./PrimaryColumnsControlWDS"; import ToolbarButtonListControl from "./ToolbarButtonListControl"; +import ArrayControl from "./ArrayControl"; export const PropertyControls = { InputTextControl, DropDownControl, SwitchControl, OptionControl, + ArrayControl, CodeEditorControl, DatePickerControl, ActionSelectorControl, diff --git a/app/client/src/ee/PluginActionEditor/hooks/useAnalyticsOnRunClick.ts b/app/client/src/ee/PluginActionEditor/hooks/useAnalyticsOnRunClick.ts new file mode 100644 index 000000000000..c77f70fc9d9e --- /dev/null +++ b/app/client/src/ee/PluginActionEditor/hooks/useAnalyticsOnRunClick.ts @@ -0,0 +1 @@ +export * from "ce/PluginActionEditor/hooks/useAnalyticsOnRunClick"; diff --git a/app/client/src/ee/PluginActionEditor/hooks/useBlockExecution.ts b/app/client/src/ee/PluginActionEditor/hooks/useBlockExecution.ts new file mode 100644 index 000000000000..d95708182c37 --- /dev/null +++ b/app/client/src/ee/PluginActionEditor/hooks/useBlockExecution.ts @@ -0,0 +1 @@ +export * from "ce/PluginActionEditor/hooks/useBlockExecution"; diff --git a/app/client/src/entities/Engine/AppViewerEngine.ts b/app/client/src/entities/Engine/AppViewerEngine.ts index d93d7b6cb1a5..e4cca1b7db6e 100644 --- a/app/client/src/entities/Engine/AppViewerEngine.ts +++ b/app/client/src/entities/Engine/AppViewerEngine.ts @@ -105,7 +105,7 @@ export default class AppViewerEngine extends AppEngine { }), fetchSelectedAppThemeAction(applicationId, currentTheme), fetchAppThemesAction(applicationId, themes), - setupPublishedPage(toLoadPageId, true, true, pageWithMigratedDsl), + setupPublishedPage(toLoadPageId, true, pageWithMigratedDsl), ]; const successActionEffects = [ diff --git a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/defaultConfig.ts b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/defaultConfig.ts index 6d618658a411..77e0374f4092 100644 --- a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/defaultConfig.ts +++ b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/defaultConfig.ts @@ -7,4 +7,6 @@ export const defaultsConfig = { widgetType: "AI_CHAT", version: 1, responsiveBehavior: ResponsiveBehavior.Fill, + initialAssistantMessage: "", + initialAssistantSuggestions: [], } as unknown as WidgetDefaultProps; diff --git a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/propertyPaneContent.ts b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/propertyPaneContent.ts index ac4abc2c8b17..8fff6583b8d7 100644 --- a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/propertyPaneContent.ts +++ b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/config/propertyPaneContent.ts @@ -79,16 +79,27 @@ export const propertyPaneContent = [ defaultValue: "", }, { - helpText: "Configures a prompt for the assistant", - propertyName: "systemPrompt", - label: "Prompt", + helpText: "Configures an initial assistant message", + propertyName: "initialAssistantMessage", + label: "Initial Assistant Message", controlType: "INPUT_TEXT", - isJSConvertible: false, - isBindProperty: false, + isJSConvertible: true, + isBindProperty: true, isTriggerProperty: false, validation: { type: ValidationTypes.TEXT }, defaultValue: "", }, + { + helpText: "Configures initial assistant suggestions", + propertyName: "initialAssistantSuggestions", + label: "Initial Assistant Suggestions", + controlType: "ARRAY_INPUT", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.ARRAY }, + defaultValue: [], + }, { helpText: "Controls the visibility of the widget", propertyName: "isVisible", diff --git a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx index f94fb5935f71..0c2d773abae5 100644 --- a/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx +++ b/app/client/src/modules/ui-builder/ui/wds/WDSAIChatWidget/widget/index.tsx @@ -27,10 +27,12 @@ import { export interface WDSAIChatWidgetProps extends ContainerWidgetProps {} + export interface Message { id: string; content: string; role: "assistant" | "user" | "system"; + promptSuggestions?: string[]; } interface State extends WidgetState { @@ -43,24 +45,7 @@ class WDSAIChatWidget extends BaseWidget { static type = "WDS_AI_CHAT_WIDGET"; state = { - messages: [ - { - id: "1", - content: "Hello! How can I help you?", - role: "assistant" as const, - }, - { - id: "2", - content: "Find stuck support requests", - role: "user" as const, - }, - { - id: "3", - content: - "I'm finding these customer support requests that have been waiting for a response for over a day:", - role: "assistant" as const, - }, - ], + messages: [] as Message[], prompt: "", isWaitingForResponse: false, }; @@ -123,13 +108,85 @@ class WDSAIChatWidget extends BaseWidget { return {}; } - adaptMessages(messages: Message[]): ChatMessage[] { - return messages.map((message) => ({ - ...message, - isAssistant: message.role === "assistant", - })); + componentDidMount() { + // Add initial assistant message with suggestions if they were configured + if (this.props.initialAssistantMessage.length > 0) { + this.setState((state) => ({ + ...state, + messages: [ + { + id: Math.random().toString(), + content: this.props.initialAssistantMessage, + role: "assistant", + promptSuggestions: this.props.initialAssistantSuggestions || [], + }, + ], + })); + } + } + + componentDidUpdate(prevProps: WDSAIChatWidgetProps): void { + // Track changes in the widget's properties and update the local state accordingly + + // Update the initial assistant message + if ( + prevProps.initialAssistantMessage !== + this.props.initialAssistantMessage || + prevProps.initialAssistantSuggestions !== + this.props.initialAssistantSuggestions + ) { + let updatedMessage: Message | null; + + // + if (this.props.initialAssistantMessage.length > 0) { + const currentMessage = this.state.messages[0]; + + updatedMessage = { + // If the initial assistant message is set, update it + // Otherwise, create a new one + ...(currentMessage || { + id: Math.random().toString(), + role: "assistant", + }), + content: this.props.initialAssistantMessage, + promptSuggestions: this.props.initialAssistantSuggestions, + }; + } else { + updatedMessage = null; + } + + this.setState((state) => ({ + ...state, + messages: updatedMessage ? [updatedMessage] : [], + })); + } } + updatePrompt = (prompt: string) => { + this.setState({ prompt }); + }; + + adaptMessages = (messages: Message[]): ChatMessage[] => { + const chatMessages: ChatMessage[] = messages.map((message) => { + if (message.role === "assistant") { + return { + id: message.id, + content: message.content, + isAssistant: true, + promptSuggestions: message.promptSuggestions || [], + }; + } + + return { + id: message.id, + content: message.content, + isAssistant: false, + }; + }); + + return chatMessages; + }; + handleMessageSubmit = (event?: FormEvent) => { event?.preventDefault(); @@ -148,18 +205,7 @@ class WDSAIChatWidget extends BaseWidget { }), () => { const messages: Message[] = [...this.state.messages]; - - if (this.props.systemPrompt) { - messages.unshift({ - id: String(Date.now()), - content: this.props.systemPrompt, - role: "system", - }); - } - - const params = { - messages, - }; + const params = { messages }; this.executeAction({ triggerPropertyName: "onClick", @@ -182,6 +228,8 @@ class WDSAIChatWidget extends BaseWidget { id: Math.random().toString(), content: this.props.queryData.choices[0].message.content, role: "assistant", + // TODO: Add prompt suggestions from the query data, if any + promptSuggestions: [], }, ], isWaitingForResponse: false, @@ -190,7 +238,11 @@ class WDSAIChatWidget extends BaseWidget { }; handlePromptChange = (prompt: string) => { - this.setState({ prompt }); + this.updatePrompt(prompt); + }; + + handleApplyAssistantSuggestion = (suggestion: string) => { + this.updatePrompt(suggestion); }; getWidgetView(): ReactNode { @@ -199,6 +251,7 @@ class WDSAIChatWidget extends BaseWidget { assistantName={this.props.assistantName} chatTitle={this.props.chatTitle} isWaitingForResponse={this.state.isWaitingForResponse} + onApplyAssistantSuggestion={this.handleApplyAssistantSuggestion} onPromptChange={this.handlePromptChange} onSubmit={this.handleMessageSubmit} prompt={this.state.prompt} diff --git a/app/client/src/pages/AppViewer/index.tsx b/app/client/src/pages/AppViewer/index.tsx index bde4699342c4..abe1ebbf3884 100644 --- a/app/client/src/pages/AppViewer/index.tsx +++ b/app/client/src/pages/AppViewer/index.tsx @@ -28,10 +28,7 @@ import { useSelector } from "react-redux"; import BrandingBadge from "./BrandingBadge"; import { setAppViewHeaderHeight } from "actions/appViewActions"; import { CANVAS_SELECTOR } from "constants/WidgetConstants"; -import { - setupPublishedPage, - fetchPublishedPageResourcesAction, -} from "actions/pageActions"; +import { fetchPublishedPageResources } from "actions/pageActions"; import usePrevious from "utils/hooks/usePrevious"; import { getIsBranchUpdated } from "../utils"; import { APP_MODE } from "entities/App"; @@ -165,10 +162,12 @@ function AppViewer(props: Props) { )?.pageId; if (pageId) { - dispatch(setupPublishedPage(pageId, true)); - - // Used for fetching page resources - dispatch(fetchPublishedPageResourcesAction(basePageId)); + dispatch( + fetchPublishedPageResources({ + basePageId, + pageId, + }), + ); } } } diff --git a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx index 71baf3d547d4..80a1748ad0c7 100644 --- a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx @@ -111,6 +111,7 @@ const Wrapper = styled.div` flex-direction: row; height: 100%; position: relative; + overflow: hidden; `; const MainContainer = styled.div` diff --git a/app/client/src/pages/Editor/APIEditor/Editor.tsx b/app/client/src/pages/Editor/APIEditor/Editor.tsx index 5ce681b23e05..1e6fab13010d 100644 --- a/app/client/src/pages/Editor/APIEditor/Editor.tsx +++ b/app/client/src/pages/Editor/APIEditor/Editor.tsx @@ -229,9 +229,10 @@ class ApiEditor extends React.Component { const formStyles: CSSProperties = { position: "relative", - height: "100%", display: "flex", flexDirection: "column", + flexGrow: "1", + overflow: "auto", }; // TODO: Fix this the next time the file is edited diff --git a/app/client/src/pages/Editor/APIEditor/GraphQL/QueryEditor.tsx b/app/client/src/pages/Editor/APIEditor/GraphQL/QueryEditor.tsx deleted file mode 100644 index 53948e1520b4..000000000000 --- a/app/client/src/pages/Editor/APIEditor/GraphQL/QueryEditor.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from "react"; -import { Field } from "redux-form"; -// // Codemirror GraphQL plugins -import "codemirror-graphql/hint"; -import "codemirror-graphql/info"; -import "codemirror-graphql/jump"; -import "codemirror-graphql/mode"; - -import QueryWrapper from "./QueryWrapperWithCSS"; -import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import { - CodeEditorBorder, - EditorModes, - EditorSize, - TabBehaviour, -} from "components/editorComponents/CodeEditor/EditorConfig"; -import styled from "styled-components"; -import { Text, TextType } from "@appsmith/ads-old"; -import LazyCodeEditor from "components/editorComponents/LazyCodeEditor"; - -const QueryHeader = styled(Text)` - background: var(--ads-v2-color-bg-subtle); - padding: 8px 16px; -`; - -interface QueryProps { - // Path to store the value in the actual data object - dataTreePath: string; - // Height for the editor - height: string; - // Name of the field of the form - name: string; - // Theme to be used in CodeEditor - theme: EditorTheme; -} - -/** - * Query Editor is for writing Graphql query using the Codemirror Editor which we use - * @param props Props that are required by the CodeEditor to render the query editor - * @returns Component with Editor - */ -function QueryEditor(props: QueryProps) { - const editorProps = { - mode: EditorModes.GRAPHQL_WITH_BINDING, - tabBehaviour: TabBehaviour.INDENT, - size: EditorSize.EXTENDED, - showLineNumbers: true, - }; - - return ( - - - Query - - - - ); -} - -export default QueryEditor; diff --git a/app/client/src/pages/Editor/APIEditor/GraphQL/VariableEditor.tsx b/app/client/src/pages/Editor/APIEditor/GraphQL/VariableEditor.tsx deleted file mode 100644 index 89a3d3b96d34..000000000000 --- a/app/client/src/pages/Editor/APIEditor/GraphQL/VariableEditor.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from "react"; -import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import { - CodeEditorBorder, - EditorModes, - EditorSize, - TabBehaviour, -} from "components/editorComponents/CodeEditor/EditorConfig"; -import styled from "styled-components"; -import { Text, TextType } from "@appsmith/ads-old"; -import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField"; -import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; - -const VariableWrapper = styled.div` - display: flex; - flex-direction: column; - width: 100%; - flex-shrink: 0; - &&&&& .CodeMirror { - border: 0px; - } - &&& .CodeMirror-gutters { - background: var(--ads-v2-color-bg-subtle); - } -`; - -const VariableHeader = styled(Text)` - background: var(--ads-v2-color-bg-subtle); - padding: 8px 16px; -`; - -const EXPECTED_VARIABLE = { - type: "object", - example: - '{\n "name":"{{ inputName.property }}",\n "preference":"{{ dropdownName.property }}"\n}', - autocompleteDataType: AutocompleteDataType.OBJECT, -}; - -interface VariableProps { - // Name of the action to define the path to the config property - actionName: string; - // Theme to be used in CodeEditor - theme: EditorTheme; -} - -/** - * Variable Editor is for writing Graphql variables using the Codemirror Editor which we use for JSON - * @param props Props that are required by the CodeEditor to render the variable editor - * @returns Component with Editor - */ -function VariableEditor(props: VariableProps) { - return ( - - - Query variables - - - - ); -} - -export default VariableEditor; diff --git a/app/client/src/pages/Editor/ActionForm/Section/index.tsx b/app/client/src/pages/Editor/ActionForm/Section/index.tsx index 937ad0b631d5..7b664e119607 100644 --- a/app/client/src/pages/Editor/ActionForm/Section/index.tsx +++ b/app/client/src/pages/Editor/ActionForm/Section/index.tsx @@ -5,11 +5,13 @@ import styles from "./styles.module.css"; interface SectionProps extends React.HTMLAttributes { children: React.ReactNode; isStandalone?: boolean; + isFullWidth?: boolean; } const Section: React.FC = ({ children, className, + isFullWidth = false, isStandalone = false, ...props }) => { @@ -18,6 +20,7 @@ const Section: React.FC = ({ return (
diff --git a/app/client/src/pages/Editor/ActionForm/Section/styles.module.css b/app/client/src/pages/Editor/ActionForm/Section/styles.module.css index e3d2a2d2f309..fb03da7ffbd3 100644 --- a/app/client/src/pages/Editor/ActionForm/Section/styles.module.css +++ b/app/client/src/pages/Editor/ActionForm/Section/styles.module.css @@ -13,4 +13,8 @@ &[data-standalone="false"]:not(:last-child) { border-bottom: 1px solid var(--ads-v2-color-border); } + + &[data-fullwidth="true"] { + max-width: none; + } } diff --git a/app/client/src/reducers/uiReducers/appViewReducer.tsx b/app/client/src/reducers/uiReducers/appViewReducer.tsx index 1d3671392f24..05ffcdbfcad3 100644 --- a/app/client/src/reducers/uiReducers/appViewReducer.tsx +++ b/app/client/src/reducers/uiReducers/appViewReducer.tsx @@ -26,6 +26,11 @@ const appViewReducer = createReducer(initialState, { [ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT]: (state: AppViewReduxState) => { return { ...state, isFetchingPage: true }; }, + [ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_INIT]: ( + state: AppViewReduxState, + ) => { + return { ...state, isFetchingPage: true }; + }, [ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR]: ( state: AppViewReduxState, ) => { @@ -44,6 +49,14 @@ const appViewReducer = createReducer(initialState, { isFetchingPage: false, }; }, + [ReduxActionTypes.FETCH_PUBLISHED_PAGE_RESOURCES_SUCCESS]: ( + state: AppViewReduxState, + ) => { + return { + ...state, + isFetchingPage: false, + }; + }, [ReduxActionTypes.SET_APP_VIEWER_HEADER_HEIGHT]: ( state: AppViewReduxState, action: ReduxAction, diff --git a/app/client/src/workers/Evaluation/evalTreeWithChanges.test.ts b/app/client/src/workers/Evaluation/evalTreeWithChanges.test.ts index 3227e290ecce..929aa0a36382 100644 --- a/app/client/src/workers/Evaluation/evalTreeWithChanges.test.ts +++ b/app/client/src/workers/Evaluation/evalTreeWithChanges.test.ts @@ -277,6 +277,11 @@ describe("evaluateAndGenerateResponse", () => { [], [], ); + + expect(webworkerResponse.workerResponse.dependencies).toEqual({ + "Text1.text": ["Text2.text", "Text1"], + "Text2.text": ["Text2"], + }); const parsedUpdates = getParsedUpdatesFromWebWorkerResp(webworkerResponse); diff --git a/app/client/src/workers/Evaluation/evalTreeWithChanges.ts b/app/client/src/workers/Evaluation/evalTreeWithChanges.ts index 8b75815c5ba6..bc561de0df56 100644 --- a/app/client/src/workers/Evaluation/evalTreeWithChanges.ts +++ b/app/client/src/workers/Evaluation/evalTreeWithChanges.ts @@ -139,6 +139,7 @@ export const evaluateAndGenerateResponse = ( ); defaultResponse.staleMetaIds = updateResponse.staleMetaIds; + defaultResponse.dependencies = dataTreeEvaluator.inverseDependencies; // when additional paths are required to be added as updates, we extract the updates from the data tree using these paths. const additionalUpdates = getNewDataTreeUpdates( diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 65539a33a99d..c97f664a5bc3 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -4769,6 +4769,15 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/api-logs@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/api-logs@npm:0.53.0" + dependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 3383ff75f94a77402370a655f8edf049f9864ad60140f70821a1b775ce43bdb9ca6fade533a1faf46dbca19f3189bcbf1f8805062f5a68bfe2a00281b1712d1f + languageName: node + linkType: hard + "@opentelemetry/api@npm:^1.0.0, @opentelemetry/api@npm:^1.9.0": version: 1.9.0 resolution: "@opentelemetry/api@npm:1.9.0" @@ -4776,6 +4785,15 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/context-async-hooks@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/context-async-hooks@npm:1.26.0" + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: f0fe5bfa3aeed99fbe7d6f6157e3bcc2e4450850a62ef60e551812f3e5aa72cb81e38de8c4e1b6934c93e18579a503664597f78e7e7d9904e271f59c939a3e02 + languageName: node + linkType: hard + "@opentelemetry/context-zone-peer-dep@npm:1.25.1": version: 1.25.1 resolution: "@opentelemetry/context-zone-peer-dep@npm:1.25.1" @@ -4807,7 +4825,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:^1.26.0": +"@opentelemetry/core@npm:1.26.0, @opentelemetry/core@npm:^1.26.0": version: 1.26.0 resolution: "@opentelemetry/core@npm:1.26.0" dependencies: @@ -4848,6 +4866,20 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/instrumentation-http@npm:^0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/instrumentation-http@npm:0.53.0" + dependencies: + "@opentelemetry/core": 1.26.0 + "@opentelemetry/instrumentation": 0.53.0 + "@opentelemetry/semantic-conventions": 1.27.0 + semver: ^7.5.2 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 4ee569f7fc8c7ce50fabaff016d33577f36e63272b0634ac45806d70bffdf38fcf09db3cd9dd27c3150f6c4547fec673c356c419a6ed2399ff2849b9487a6e89 + languageName: node + linkType: hard + "@opentelemetry/instrumentation@npm:0.52.1": version: 0.52.1 resolution: "@opentelemetry/instrumentation@npm:0.52.1" @@ -4864,6 +4896,22 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/instrumentation@npm:0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/instrumentation@npm:0.53.0" + dependencies: + "@opentelemetry/api-logs": 0.53.0 + "@types/shimmer": ^1.2.0 + import-in-the-middle: ^1.8.1 + require-in-the-middle: ^7.1.1 + semver: ^7.5.2 + shimmer: ^1.2.1 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: a386fe066eab71129a6edbc883ab407b1022850e8acc4750029a12e8730588a8b81442d0b008aaddb46f7614af40d19d331e7348790ca2d08ba8eed6d23ffdae + languageName: node + linkType: hard + "@opentelemetry/otlp-exporter-base@npm:0.52.1": version: 0.52.1 resolution: "@opentelemetry/otlp-exporter-base@npm:0.52.1" @@ -4893,6 +4941,28 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/propagator-b3@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/propagator-b3@npm:1.26.0" + dependencies: + "@opentelemetry/core": 1.26.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: c2e99a8ed2814cf5b8e6e2a79411f2f6d668b7d5fc8351e5302ea4149601a96ec655422cf59470c66d8a408850f8a6b5156bf7deac7afb07d3f7a935c51fff04 + languageName: node + linkType: hard + +"@opentelemetry/propagator-jaeger@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/propagator-jaeger@npm:1.26.0" + dependencies: + "@opentelemetry/core": 1.26.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: a0ac3888c86f1b4671c7ca520396b89b4c47fa9e9d976bd014472d2b7786e7c5bdf4823a6e2a900fed5ea5dfe23eda0bdf6740e77c1352f2c0f82b13a71c03df + languageName: node + linkType: hard + "@opentelemetry/resources@npm:1.25.1": version: 1.25.1 resolution: "@opentelemetry/resources@npm:1.25.1" @@ -4905,6 +4975,18 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/resources@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/resources@npm:1.26.0" + dependencies: + "@opentelemetry/core": 1.26.0 + "@opentelemetry/semantic-conventions": 1.27.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: f70b0fdf4fb00c950bc30084818c92a5339f1be5d709bd681ab14453e877d6bb9f700324b8e65a0eabfeea618d01ed071abf9088e00fa0bf7f3305b1abad22cb + languageName: node + linkType: hard + "@opentelemetry/sdk-logs@npm:0.52.1": version: 0.52.1 resolution: "@opentelemetry/sdk-logs@npm:0.52.1" @@ -4944,6 +5026,35 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/sdk-trace-base@npm:1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/sdk-trace-base@npm:1.26.0" + dependencies: + "@opentelemetry/core": 1.26.0 + "@opentelemetry/resources": 1.26.0 + "@opentelemetry/semantic-conventions": 1.27.0 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: a4f4ddf644fd0d79b2bd49e4377143688d2aa657643a470d8bed6696f26817598fb4e9f16ba2d8c237292af56f06eec56594a7b4cc417d4ea7e490a45a22113b + languageName: node + linkType: hard + +"@opentelemetry/sdk-trace-node@npm:^1.26.0": + version: 1.26.0 + resolution: "@opentelemetry/sdk-trace-node@npm:1.26.0" + dependencies: + "@opentelemetry/context-async-hooks": 1.26.0 + "@opentelemetry/core": 1.26.0 + "@opentelemetry/propagator-b3": 1.26.0 + "@opentelemetry/propagator-jaeger": 1.26.0 + "@opentelemetry/sdk-trace-base": 1.26.0 + semver: ^7.5.2 + peerDependencies: + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 1d63bed8fc36496698919ccd25be3b7b0e0d0bf9478f413a26bdbfe0bf0d4166bf58bbbee2415fb2fe42d3008b5c32ec7e4e42f2cb6d18b665b349eb025c15eb + languageName: node + linkType: hard + "@opentelemetry/sdk-trace-web@npm:1.25.1": version: 1.25.1 resolution: "@opentelemetry/sdk-trace-web@npm:1.25.1" @@ -4964,7 +5075,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.27.0": +"@opentelemetry/semantic-conventions@npm:1.27.0, @opentelemetry/semantic-conventions@npm:^1.27.0": version: 1.27.0 resolution: "@opentelemetry/semantic-conventions@npm:1.27.0" checksum: 26d85f8d13c8c64024f7a84528cff41d56afc9829e7ff8a654576404f8b2c1a9c264adcc6fa5a9551bacdd938a4a464041fa9493e0a722e5605f2c2ae6752398 @@ -11023,10 +11134,10 @@ __metadata: languageName: node linkType: hard -"@types/shimmer@npm:^1.0.2": - version: 1.0.4 - resolution: "@types/shimmer@npm:1.0.4" - checksum: f1e7f8b773c34ea21b69686cb100117bd94cc0d1f043e3fc50683453b9936d1295c4f48e1872766556234a9ec48ea37fc7e6b5e56212f66ec65d5b2b5d73092b +"@types/shimmer@npm:^1.0.2, @types/shimmer@npm:^1.2.0": + version: 1.2.0 + resolution: "@types/shimmer@npm:1.2.0" + checksum: f081a31d826ce7bfe8cc7ba8129d2b1dffae44fd580eba4fcf741237646c4c2494ae6de2cada4b7713d138f35f4bc512dbf01311d813dee82020f97d7d8c491c languageName: node linkType: hard @@ -12570,6 +12681,9 @@ __metadata: version: 0.0.0-use.local resolution: "appsmith-rts@workspace:packages/rts" dependencies: + "@opentelemetry/instrumentation-http": ^0.53.0 + "@opentelemetry/sdk-trace-node": ^1.26.0 + "@opentelemetry/semantic-conventions": ^1.27.0 "@shared/ast": "workspace:^" "@types/express": ^4.17.14 "@types/jest": ^29.2.3