diff --git a/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx b/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx
index f95baaa328..ef5a5dbd7d 100644
--- a/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx
+++ b/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx
@@ -7,6 +7,7 @@ import { keymap } from '@codemirror/view';
import { completionKeymap, acceptCompletion, autocompletion, completionStatus } from '@codemirror/autocomplete';
import { python } from '@codemirror/lang-python';
import { sql } from '@codemirror/lang-sql';
+import _ from 'lodash';
import { sass, sassCompletionSource } from '@codemirror/lang-sass';
import { okaidia } from '@uiw/codemirror-theme-okaidia';
import { githubLight } from '@uiw/codemirror-theme-github';
@@ -21,6 +22,7 @@ import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow';
import { search, searchKeymap, searchPanelOpen } from '@codemirror/search';
import { handleSearchPanel, SearchBtn } from './SearchBox';
+import { isInsideParent } from './utils';
const langSupport = Object.freeze({
javascript: javascript(),
@@ -51,8 +53,15 @@ const MultiLineCodeEditor = (props) => {
renderCopilot,
} = props;
const replaceIdsWithName = useStore((state) => state.replaceIdsWithName, shallow);
+ const wrapperRef = useRef(null);
const getSuggestions = useStore((state) => state.getSuggestions, shallow);
+ const getServerSideGlobalSuggestions = useStore((state) => state.getServerSideGlobalSuggestions, shallow);
+
const isInsideQueryPane = !!document.querySelector('.code-hinter-wrapper')?.closest('.query-details');
+ const isInsideQueryManager = useMemo(
+ () => isInsideParent(wrapperRef?.current, 'query-manager'),
+ [wrapperRef.current]
+ );
const context = useContext(CodeHinterContext);
@@ -100,9 +109,16 @@ const MultiLineCodeEditor = (props) => {
const hints = getSuggestions();
+ const serverHints = getServerSideGlobalSuggestions(isInsideQueryManager);
+
+ const allHints = {
+ ...hints,
+ appHints: [...hints.appHints, ...serverHints],
+ };
+
let JSLangHints = [];
if (lang === 'javascript') {
- JSLangHints = Object.keys(hints['jsHints'])
+ JSLangHints = Object.keys(allHints['jsHints'])
.map((key) => {
return hints['jsHints'][key]['methods'].map((hint) => ({
hint: hint,
@@ -120,7 +136,7 @@ const MultiLineCodeEditor = (props) => {
});
}
- const appHints = hints['appHints'];
+ const appHints = allHints['appHints'];
let autoSuggestionList = appHints.filter((suggestion) => {
return suggestion.hint.includes(nearestSubstring);
@@ -229,6 +245,7 @@ const MultiLineCodeEditor = (props) => {
diff --git a/frontend/src/AppBuilder/CodeEditor/PreviewBox.jsx b/frontend/src/AppBuilder/CodeEditor/PreviewBox.jsx
index 6c28bdbb21..c123cb2b9c 100644
--- a/frontend/src/AppBuilder/CodeEditor/PreviewBox.jsx
+++ b/frontend/src/AppBuilder/CodeEditor/PreviewBox.jsx
@@ -96,6 +96,7 @@ export const PreviewBox = ({
const [largeDataset, setLargeDataset] = useState(false);
const globals = useStore((state) => state.getAllExposedValues().constants || {}, shallow);
const secrets = useStore((state) => state.getSecrets(), shallow);
+ const globalServerConstantsRegex = /^\{\{.*globals\.server.*\}\}$/;
const getPreviewContent = (content, type) => {
if (content === undefined || content === null) return currentValue;
@@ -118,11 +119,11 @@ export const PreviewBox = ({
let previewContent = resolvedValue;
let isGlobalConstant = currentValue && currentValue.includes('{{constants.');
let isSecretConstant = currentValue && currentValue.includes('{{secrets.');
+ const isServerConstant = currentValue && currentValue.match(globalServerConstantsRegex);
let invalidConstants = null;
let undefinedError = null;
if (isGlobalConstant || isSecretConstant) {
invalidConstants = verifyConstant(currentValue, globals, secrets);
- console.log('invalidConstants', invalidConstants);
}
if (invalidConstants?.length) {
undefinedError = { type: 'Invalid constants' };
@@ -197,7 +198,11 @@ export const PreviewBox = ({
const errValue = ifCoersionErrorHasCircularDependency(_resolveValue);
setError({
- message: isSecretError ? 'secrets cannot be used in apps' : _error,
+ message: isServerConstant
+ ? 'Server variables cannot be used in apps'
+ : isSecretError
+ ? 'secrets cannot be used in apps'
+ : _error,
value: isSecretError
? 'Undefined'
: jsErrorType === 'Invalid'
@@ -222,6 +227,7 @@ export const PreviewBox = ({
isWorkspaceVariable={isWorkspaceVariable}
isSecretConstant={isSecretConstant || false}
isLargeDataset={largeDataset}
+ isServerConstant={isServerConstant}
/>
copyToClipboard(error ? error?.value : content)}
@@ -240,8 +246,11 @@ const RenderResolvedValue = ({
withValidation,
isWorkspaceVariable,
isSecretConstant = false,
+ isServerConstant = false,
isLargeDataset,
}) => {
+ const isServerSideGlobalEnabled = useStore((state) => !!state?.license?.featureAccess?.serverSideGlobal, shallow);
+
const computeCoersionPreview = (resolvedValue, coersionData) => {
if (coersionData?.typeBeforeCoercion === coersionData?.typeAfterCoercion) return resolvedValue;
@@ -264,7 +273,11 @@ const RenderResolvedValue = ({
}`
: previewType;
- const previewContent = isSecretConstant
+ const previewContent = isServerConstant
+ ? isServerSideGlobalEnabled
+ ? 'Server variables would be resolved at runtime'
+ : 'Server variables are only available in paid plans'
+ : isSecretConstant
? 'Values of secret constants are hidden'
: !withValidation
? resolvedValue
diff --git a/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx b/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx
index 7f8765e287..4c35f6d0ed 100644
--- a/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx
+++ b/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx
@@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from 'react';
import { PreviewBox } from './PreviewBox';
import { ToolTip } from '@/Editor/Inspector/Elements/Components/ToolTip';
import { useTranslation } from 'react-i18next';
-import { camelCase, isEmpty, noop } from 'lodash';
+import { camelCase, isEmpty, noop, get } from 'lodash';
import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { autocompletion, completionKeymap, completionStatus, acceptCompletion } from '@codemirror/autocomplete';
@@ -12,7 +12,7 @@ import { keymap } from '@codemirror/view';
import FxButton from '../CodeBuilder/Elements/FxButton';
import cx from 'classnames';
import { DynamicFxTypeRenderer } from './DynamicFxTypeRenderer';
-import { resolveReferences } from './utils';
+import { isInsideParent, resolveReferences } from './utils';
import { okaidia } from '@uiw/codemirror-theme-okaidia';
import { githubLight } from '@uiw/codemirror-theme-github';
import { getAutocompletion } from './autocompleteExtensionConfig';
@@ -161,6 +161,7 @@ const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...r
componentName={componentName}
setShowPreview={setShowPreview}
showPreview={showPreview}
+ wrapperRef={wrapperRef}
showSuggestions={showSuggestions}
{...restProps}
/>
@@ -194,11 +195,25 @@ const EditorInput = ({
previewRef,
setShowPreview,
onInputChange,
+ wrapperRef,
showSuggestions,
}) => {
+ const getServerSideGlobalSuggestions = useStore((state) => state.getServerSideGlobalSuggestions, shallow);
+
const getSuggestions = useStore((state) => state.getSuggestions, shallow);
+ const isInsideQueryManager = useMemo(
+ () => isInsideParent(wrapperRef?.current, 'query-manager'),
+ [wrapperRef.current]
+ );
function autoCompleteExtensionConfig(context) {
const hints = getSuggestions();
+ const serverHints = getServerSideGlobalSuggestions(isInsideQueryManager);
+
+ const allHints = {
+ ...hints,
+ appHints: [...hints.appHints, ...serverHints],
+ };
+
let word = context.matchBefore(/\w*/);
const totalReferences = (context.state.doc.toString().match(/{{/g) || []).length;
@@ -229,7 +244,7 @@ const EditorInput = ({
queryInput = '{{' + currentWord + '}}';
}
- let completions = getAutocompletion(queryInput, validationType, hints, totalReferences, originalQueryInput);
+ let completions = getAutocompletion(queryInput, validationType, allHints, totalReferences, originalQueryInput);
return {
from: word.from,
@@ -239,7 +254,7 @@ const EditorInput = ({
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- const overRideFunction = React.useCallback((context) => autoCompleteExtensionConfig(context), []);
+ const overRideFunction = React.useCallback((context) => autoCompleteExtensionConfig(context), [isInsideQueryManager]);
const autoCompleteConfig = autocompletion({
override: [overRideFunction],
@@ -409,11 +424,11 @@ const EditorInput = ({
extensions={
showSuggestions
? [
- javascript({ jsx: lang === 'jsx' }),
- autoCompleteConfig,
- keymap.of([...customKeyMaps]),
- customTabKeymap,
- ]
+ javascript({ jsx: lang === 'jsx' }),
+ autoCompleteConfig,
+ keymap.of([...customKeyMaps]),
+ customTabKeymap,
+ ]
: [javascript({ jsx: lang === 'jsx' })]
}
onChange={(val) => {
@@ -485,9 +500,8 @@ const DynamicEditorBridge = (props) => {
)}
@@ -495,9 +509,8 @@ const DynamicEditorBridge = (props) => {
{paramLabel !== 'Type' && isFxNotRequired === undefined && (
{
+ while (element) {
+ if (element.classList?.contains(className)) {
+ console.log('element.classList', element.classList);
+ return true;
+ }
+ element = element.parentElement;
+ }
+ return false;
+};
+
function getMethods(type) {
const arrayMethods = Object.getOwnPropertyNames(Array.prototype).filter(
(p) => typeof Array.prototype[p] === 'function'
diff --git a/frontend/src/AppBuilder/_stores/slices/codeHinterSlice.js b/frontend/src/AppBuilder/_stores/slices/codeHinterSlice.js
index 953d253709..854cba49da 100644
--- a/frontend/src/AppBuilder/_stores/slices/codeHinterSlice.js
+++ b/frontend/src/AppBuilder/_stores/slices/codeHinterSlice.js
@@ -36,4 +36,27 @@ export const createCodeHinterSlice = (set, get) => ({
setSuggestions({ appHints: suggestionList, jsHints: jsHints });
},
getSuggestions: () => get().suggestions,
+ getServerSideGlobalSuggestions: (isInsideQueryManager) => {
+ const isServerSideGlobalEnabled = !!get()?.license?.featureAccess?.serverSideGlobal;
+ const serverHints = [];
+ const hints = get().getSuggestions();
+ if (isInsideQueryManager && isServerSideGlobalEnabled) {
+ serverHints.push({ hint: 'globals.server', type: 'Object' });
+ hints?.appHints?.forEach((appHint) => {
+ if (appHint?.hint?.startsWith('globals.currentUser')) {
+ const key = appHint?.hint?.replace('globals.currentUser', 'globals.server.currentUser');
+ console.log({
+ hint: key,
+ type: appHint?.type,
+ });
+ serverHints.push({
+ hint: key,
+ type: appHint?.type,
+ });
+ }
+ });
+ }
+
+ return serverHints;
+ },
});
diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js
index 92712693b3..986c7011b1 100644
--- a/frontend/webpack.config.js
+++ b/frontend/webpack.config.js
@@ -122,7 +122,7 @@ module.exports = {
'@cloud/modules': emptyModulePath,
},
},
- devtool: environment === 'development' ? 'eval-source-map' : 'hidden-source-map',
+ devtool: environment === 'development' ? 'source-map' : 'hidden-source-map',
module: {
rules: [
{
diff --git a/server/ee b/server/ee
index 683647f83d..381d9771d1 160000
--- a/server/ee
+++ b/server/ee
@@ -1 +1 @@
-Subproject commit 683647f83d3efeeadbe69c40b8e8dd5ba4e8ea06
+Subproject commit 381d9771d1e237285a362f0c75bffe68f6de707f
diff --git a/server/src/modules/data-queries/interfaces/IUtilService.ts b/server/src/modules/data-queries/interfaces/IUtilService.ts
index 738108d75d..f070380b54 100644
--- a/server/src/modules/data-queries/interfaces/IUtilService.ts
+++ b/server/src/modules/data-queries/interfaces/IUtilService.ts
@@ -22,7 +22,8 @@ export interface IDataQueriesUtilService {
dataQuery: any,
queryOptions: object,
organization_id: string,
- environmentId?: string
+ environmentId?: string,
+ user?: User
): Promise<{
service: any;
sourceOptions: object;
@@ -31,5 +32,11 @@ export interface IDataQueriesUtilService {
setCookiesBackToClient(response: Response, responseHeaders: any): void;
- parseQueryOptions(object: any, options: object, organization_id: string, environmentId?: string): Promise