Merge pull request #6552 from ToolJet/merge-back/2.6.1

Merge back/2.6.1
This commit is contained in:
Sherfin Shamsudeen 2023-05-26 14:01:48 +05:30 committed by GitHub
commit d24744e0cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 166 additions and 58 deletions

View file

@ -1 +1 @@
2.6.0
2.6.1

View file

@ -7,6 +7,7 @@ PG_HOST=__required__
PG_PASS=__required__
PG_DB=tooljet_prod
ORM_LOGGING=true
NODE_ENV=production
DEPLOYMENT_PLATFORM=ec2
# ToolJet Database

View file

@ -89,7 +89,9 @@ COPY --from=builder /app/server/dist ./app/server/dist
# Define non-sudo user
RUN useradd --create-home appuser \
&& chown -R appuser:appuser /app
&& chown -R appuser:0 /app \
&& chmod u+x /app \
&& chmod -R g=u /app
USER appuser
WORKDIR /app

View file

@ -4,6 +4,7 @@ FROM tooljet/tooljet-ce:latest
COPY --from=postgrest/postgrest:v10.1.1.20221215 /bin/postgrest /bin
# Install Postgres
USER root
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
RUN echo "deb http://deb.debian.org/debian"
@ -33,6 +34,7 @@ RUN echo "[supervisord] \n" \
# ENV defaults
ENV TOOLJET_HOST=http://localhost \
PORT=80 \
NODE_ENV=production \
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \
SECRET_KEY_BASE=replace_with_secret_key_base \
PG_DB=tooljet_production \
@ -49,6 +51,7 @@ ENV TOOLJET_HOST=http://localhost \
PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \
ORM_LOGGING=true \
DEPLOYMENT_PLATFORM=docker:local \
HOME=/home/appuser \
TERM=xterm
# Prepare DB and start application

View file

@ -175,7 +175,7 @@ export function CodeHinter({
const getPreview = () => {
if (!enablePreview) return;
const customResolvables = getCustomResolvables();
const [preview, error] = resolveReferences(currentValue, realState, null, customResolvables, true);
const [preview, error] = resolveReferences(currentValue, realState, null, customResolvables, true, true);
const themeCls = darkMode ? 'bg-dark py-1' : 'bg-light py-1';
if (error) {

View file

@ -4,6 +4,7 @@ import { CodeHinter } from '../../../CodeBuilder/CodeHinter';
export const ProgramaticallyHandleToggleSwitch = ({
currentState,
darkMode,
// eslint-disable-next-line no-unused-vars
label,
index,
callbackFunction,
@ -18,28 +19,29 @@ export const ProgramaticallyHandleToggleSwitch = ({
const param = { name: property };
const definition = { value, fxActive: props.fxActive };
const initialValue = definition?.value ?? `{{false}}`;
const options = {};
return (
<CodeHinter
enablePreview={true}
currentState={currentState}
initialValue={initialValue}
mode={options.mode}
theme={darkMode ? 'monokai' : options.theme}
lineWrapping={true}
onChange={(value) => callbackFunction(index, property, value)}
componentName={`widget/${component.name}::${label}`}
type={paramMeta.type}
paramName={param.name}
paramLabel={paramMeta.displayName}
fieldMeta={paramMeta}
onFxPress={(active) => {
callbackFunction(index, 'fxActive', active);
}}
fxActive={props?.fxActive ?? false}
component={component}
className="codehinter-default-input"
/>
<div className={`mb-2 field ${options.className}`} onClick={(e) => e.stopPropagation()}>
<CodeHinter
enablePreview={true}
currentState={currentState}
initialValue={initialValue}
mode={options.mode}
theme={darkMode ? 'monokai' : options.theme}
lineWrapping={true}
onChange={(value) => callbackFunction(index, property, value)}
componentName={`widget/${component?.component?.name}::${param.name}`}
type={paramMeta.type}
paramName={param.name}
paramLabel={paramMeta.displayName}
fieldMeta={paramMeta}
onFxPress={(active) => {
callbackFunction(index, 'fxActive', active);
}}
fxActive={props?.fxActive ?? false}
component={component.component}
className={options.className}
/>
</div>
);
};

View file

@ -777,10 +777,19 @@ export function getQueryVariables(options, state) {
switch (optionsType) {
case 'string': {
options = options.replace(/\n/g, ' ');
const dynamicVariables = getDynamicVariables(options) || [];
dynamicVariables.forEach((variable) => {
queryVariables[variable] = resolveReferences(variable, state);
});
// check if {{var}} and %%var%% are present in the string
if (options.includes('{{') && options.includes('%%')) {
const vars = resolveReferences(options, state);
console.log('queryVariables', { options, vars });
queryVariables[options] = vars;
} else {
const dynamicVariables = getDynamicVariables(options) || [];
dynamicVariables.forEach((variable) => {
queryVariables[variable] = resolveReferences(variable, state);
});
}
break;
}
@ -800,6 +809,7 @@ export function getQueryVariables(options, state) {
default:
break;
}
return queryVariables;
}

View file

@ -91,8 +91,62 @@ function resolveCode(code, state, customObjects = {}, withError = false, reserve
if (withError) return [result, error];
return result;
}
export function resolveString(str, state, customObjects, reservedKeyword, withError, forPreviewBox) {
let resolvedStr = str;
export function resolveReferences(object, state, defaultValue, customObjects = {}, withError = false) {
// Resolve {{object}}
const codeRegex = /(\{\{.+?\}\})/g;
const codeMatches = resolvedStr.match(codeRegex);
if (codeMatches) {
codeMatches.forEach((codeMatch) => {
const code = codeMatch.replace('{{', '').replace('}}', '');
if (reservedKeyword.includes(code)) {
resolvedStr = resolvedStr.replace(codeMatch, '');
} else {
const resolvedCode = resolveCode(code, state, customObjects, withError, reservedKeyword, true);
if (forPreviewBox) {
resolvedStr = resolvedStr.replace(codeMatch, resolvedCode[0]);
} else {
resolvedStr = resolvedStr.replace(codeMatch, resolvedCode);
}
}
});
}
// Resolve %%object%%
const serverRegex = /(%%.+?%%)/g;
const serverMatches = resolvedStr.match(serverRegex);
if (serverMatches) {
serverMatches.forEach((serverMatch) => {
const code = serverMatch.replace(/%%/g, '');
if (code.includes('server.') && !/^server\.[A-Za-z0-9]+$/.test(code)) {
resolvedStr = resolvedStr.replace(serverMatch, '');
} else {
const resolvedCode = resolveCode(code, state, customObjects, withError, reservedKeyword, false);
if (forPreviewBox) {
resolvedStr = resolvedStr.replace(serverMatch, resolvedCode[0]);
} else {
resolvedStr = resolvedStr.replace(serverMatch, resolvedCode);
}
}
});
}
return resolvedStr;
}
export function resolveReferences(
object,
state,
defaultValue,
customObjects = {},
withError = false,
forPreviewBox = false
) {
if (object === '{{{}}}') return '';
const reservedKeyword = ['app']; //Keywords that slows down the app
object = _.clone(object);
@ -100,6 +154,10 @@ export function resolveReferences(object, state, defaultValue, customObjects = {
let error;
switch (objectType) {
case 'string': {
if (object.includes('{{') && object.includes('}}') && object.includes('%%') && object.includes('%%')) {
object = resolveString(object, state, customObjects, reservedKeyword, withError, forPreviewBox);
}
if (object.startsWith('{{') && object.endsWith('}}')) {
const code = object.replace('{{', '').replace('}}', '');

View file

@ -1 +1 @@
2.6.0
2.6.1

View file

@ -39,8 +39,6 @@ import { GroupPermissionsModule } from './modules/group_permissions/group_permis
import { TooljetDbModule } from './modules/tooljet_db/tooljet_db.module';
import { PluginsModule } from './modules/plugins/plugins.module';
import { CopilotModule } from './modules/copilot/copilot.module';
import * as path from 'path';
import * as fs from 'fs';
import { AppEnvironmentsModule } from './modules/app_environments/app_environments.module';
import { RequestContextModule } from './modules/request_context/request-context.module';
import { ScheduleModule } from '@nestjs/schedule';
@ -101,31 +99,7 @@ const imports = [
CopilotModule,
];
if (process.env.SERVE_CLIENT !== 'false') {
const filesToReplaceAssetPath = ['index.html', 'runtime.js', 'main.js'];
for (const fileName of filesToReplaceAssetPath) {
const file = join(__dirname, '../../../', 'frontend/build', fileName);
let newValue = process.env.SUB_PATH;
if (process.env.SUB_PATH === undefined) {
newValue = fileName === 'index.html' ? '/' : '';
}
fs.readFile(file, 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
const result = data
.replace(/__REPLACE_SUB_PATH__\/api/g, path.join(newValue, '/api'))
.replace(/__REPLACE_SUB_PATH__/g, newValue);
fs.writeFile(file, result, 'utf8', function (err) {
if (err) return console.log(err);
});
});
}
if (process.env.SERVE_CLIENT !== 'false' && process.env.NODE_ENV === 'production') {
imports.unshift(
ServeStaticModule.forRoot({
// Have to remove trailing slash of SUB_PATH.

View file

@ -17,6 +17,28 @@ const fs = require('fs');
globalThis.TOOLJET_VERSION = fs.readFileSync('./.version', 'utf8').trim();
function replaceSubpathPlaceHoldersInStaticAssets() {
const filesToReplaceAssetPath = ['index.html', 'runtime.js', 'main.js'];
for (const fileName of filesToReplaceAssetPath) {
const file = join(__dirname, '../../../', 'frontend/build', fileName);
let newValue = process.env.SUB_PATH;
if (process.env.SUB_PATH === undefined) {
newValue = fileName === 'index.html' ? '/' : '';
}
const data = fs.readFileSync(file, { encoding: 'utf8' });
const result = data
.replace(/__REPLACE_SUB_PATH__\/api/g, join(newValue, '/api'))
.replace(/__REPLACE_SUB_PATH__/g, newValue);
fs.writeFileSync(file, result, { encoding: 'utf8' });
}
}
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
bufferLogs: true,
@ -97,9 +119,13 @@ async function bootstrap() {
defaultVersion: VERSION_NEUTRAL,
});
const listen_addr = process.env.LISTEN_ADDR || "::";
const listen_addr = process.env.LISTEN_ADDR || '::';
const port = parseInt(process.env.PORT) || 3000;
if (process.env.SERVE_CLIENT !== 'false' && process.env.NODE_ENV === 'production') {
replaceSubpathPlaceHoldersInStaticAssets();
}
await app.listen(port, listen_addr, function () {
const tooljetHost = configService.get<string>('TOOLJET_HOST');
console.log(`Ready to use at ${tooljetHost} 🚀`);

View file

@ -409,6 +409,38 @@ export class DataQueriesService {
return object;
} else if (typeof object === 'string') {
object = object.replace(/\n/g, ' ');
//if object has {{}} and %%%% then resolve %% in a single string
if (object.includes('{{') && object.includes('}}') && object.includes('%%') && object.includes('%%')) {
let resolvedvar = options[object];
if (object.includes(`server.`)) {
// find all server variables in the string
const serverVariables = object.match(/server.(.*?)%%/g);
serverVariables?.map((variable) => {
return variable
.match(/server.(.*?)%%/g)[0]
.replace('%%', '')
.replace('server.', '');
});
const resolvedOrgVar = [];
for (const variable of serverVariables) {
const resolvedVariable = await this.resolveVariable(variable, organization_id);
resolvedOrgVar.push(resolvedVariable);
}
//replace the HiddenEnvironmentVariable with the resolved value
for (let i = 0; i < serverVariables.length; i++) {
resolvedvar = resolvedvar.replace('HiddenEnvironmentVariable', resolvedOrgVar[i]);
}
}
return resolvedvar;
}
if (object.startsWith('{{') && object.endsWith('}}') && (object.match(/{{/g) || []).length === 1) {
object = options[object];
return object;