From 516f874a564bc7b55bc9cd87ee4d66f9d8543846 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 6 Mar 2023 09:43:47 -0600 Subject: [PATCH] Checkpoint refactoring script out of just associated-record, for more general purpose --- src/qqq/components/scripts/ScriptDocsForm.tsx | 2 +- src/qqq/components/scripts/ScriptLogsView.tsx | 27 +- .../components/widgets/misc/ScriptViewer.tsx | 131 +++++-- .../records/view/RecordDeveloperView.tsx | 336 ++---------------- src/qqq/styles/qqq-override-styles.css | 6 + 5 files changed, 156 insertions(+), 346 deletions(-) diff --git a/src/qqq/components/scripts/ScriptDocsForm.tsx b/src/qqq/components/scripts/ScriptDocsForm.tsx index 0efd843..ab08fa7 100644 --- a/src/qqq/components/scripts/ScriptDocsForm.tsx +++ b/src/qqq/components/scripts/ScriptDocsForm.tsx @@ -73,7 +73,7 @@ function ScriptDocsForm({helpText, exampleCode, aceEditorHeight}: Props): JSX.El return ( {oneBlock("helpText", "text", "Documentation", helpText)} - {oneBlock("exampleCode", "javascript", "ExampleCode", exampleCode)} + {oneBlock("exampleCode", "javascript", "Example Code", exampleCode)} ); } diff --git a/src/qqq/components/scripts/ScriptLogsView.tsx b/src/qqq/components/scripts/ScriptLogsView.tsx index 714aca4..43d9bc0 100644 --- a/src/qqq/components/scripts/ScriptLogsView.tsx +++ b/src/qqq/components/scripts/ScriptLogsView.tsx @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import Box from "@mui/material/Box"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; @@ -31,7 +32,7 @@ import ValueUtils from "qqq/utils/qqq/ValueUtils"; interface Props { - logs: any; + logs: QRecord[]; } ScriptLogsView.defaultProps = { @@ -41,7 +42,7 @@ ScriptLogsView.defaultProps = { function ScriptLogsView({logs}: Props): JSX.Element { return ( - + @@ -55,29 +56,29 @@ function ScriptLogsView({logs}: Props): JSX.Element { - logs.map((logRecord: any) => + logs?.map((logRecord: QRecord) => { let logs = ""; - if (logRecord.values.scriptLogLine) + if (logRecord.values.get("scriptLogLine")) { - for (let i = 0; i < logRecord.values.scriptLogLine.length; i++) + for (let i = 0; i < logRecord.values.get("scriptLogLine").length; i++) { console.log(" += " + i); - logs += (logRecord.values.scriptLogLine[i].values.text + "\n"); + logs += (logRecord.values.get("scriptLogLine")[i].values.text + "\n"); } } return ( - - {ValueUtils.formatDateTime(logRecord.values.startTimestamp)} - {logRecord.values.runTimeMillis?.toLocaleString()} + + {ValueUtils.formatDateTime(logRecord.values.get("startTimestamp"))} + {logRecord.values.get("runTimeMillis")?.toLocaleString()} -
{ValueUtils.formatBoolean(logRecord.values.hadError)}
+
{ValueUtils.formatBoolean(logRecord.values.get("hadError"))}
- {logRecord.values.input} + {logRecord.values.get("input")} - {logRecord.values.output} - {logRecord.values.error} + {logRecord.values.get("output")} + {logRecord.values.get("error")} {logs}
diff --git a/src/qqq/components/widgets/misc/ScriptViewer.tsx b/src/qqq/components/widgets/misc/ScriptViewer.tsx index b4a6668..1179602 100644 --- a/src/qqq/components/widgets/misc/ScriptViewer.tsx +++ b/src/qqq/components/widgets/misc/ScriptViewer.tsx @@ -20,6 +20,8 @@ */ import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException"; +import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; +import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator"; import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria"; @@ -32,7 +34,6 @@ import Button from "@mui/material/Button"; import Chip from "@mui/material/Chip"; import Divider from "@mui/material/Divider"; import Grid from "@mui/material/Grid"; -import Icon from "@mui/material/Icon"; import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import ListItemAvatar from "@mui/material/ListItemAvatar"; @@ -44,8 +45,12 @@ import Tabs from "@mui/material/Tabs"; import Typography from "@mui/material/Typography"; import React, {useReducer, useState} from "react"; import AceEditor from "react-ace"; +import {Link} from "react-router-dom"; import TabPanel from "qqq/components/misc/TabPanel"; +import ScriptDocsForm from "qqq/components/scripts/ScriptDocsForm"; import ScriptEditor, {ScriptEditorProps} from "qqq/components/scripts/ScriptEditor"; +import ScriptLogsView from "qqq/components/scripts/ScriptLogsView"; +import ScriptTestForm from "qqq/components/scripts/ScriptTestForm"; import CustomWidthTooltip from "qqq/components/tooltips/CustomWidthTooltip"; import {LoadingState} from "qqq/models/LoadingState"; import DeveloperModeUtils from "qqq/utils/DeveloperModeUtils"; @@ -63,19 +68,33 @@ const qController = Client.getInstance(); // Declaring props types for ViewForm interface Props { - scriptId: number + scriptId: number, + associatedScriptTableName?: string, + associatedScriptFieldName?: string, + associatedScriptRecordId?: any, + testInputFields?: QFieldMetaData[], + testOutputFields?: QFieldMetaData[], } ScriptViewer.defaultProps = { + associatedScriptTableName: null, + associatedScriptFieldName: null, + associatedScriptRecordId: null, + testInputFields: null, + testOutputFields: null, }; -export default function ScriptViewer({scriptId}: Props): JSX.Element +export default function ScriptViewer({scriptId, associatedScriptTableName, associatedScriptFieldName, associatedScriptRecordId, testInputFields, testOutputFields}: Props): JSX.Element { + const [metaData, setMetaData] = useState(null as QInstance); const [scriptRecord, setScriptRecord] = useState(null as QRecord); const [asyncLoadInited, setAsyncLoadInited] = useState(false); const [versionRecordList, setVersionRecordList] = useState(null as QRecord[]); const [selectedVersionRecord, setSelectedVersionRecord] = useState(null as QRecord); + const [scriptLogs, setScriptLogs] = useState({} as any); + const [scriptTypeRecord, setScriptTypeRecord] = useState(null as QRecord) + const [testScriptDefinitionObject, setTestScriptDefinitionObject] = useState({} as any) const [currentVersionId , setCurrentVersionId] = useState(null as number); const [notFoundMessage, setNotFoundMessage] = useState(null); const [selectedTab, setSelectedTab] = useState(0); @@ -94,9 +113,24 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element { try { + setMetaData(await qController.loadMetaData()); + const scriptRecord = await qController.get("script", scriptId); setScriptRecord(scriptRecord); + setScriptTypeRecord(await qController.get("scriptType", scriptRecord.values.get("scriptTypeId"))); + + if(testInputFields !== null || testOutputFields !== null) + { + setTestScriptDefinitionObject({testInputFields: testInputFields, testOutputFields: testOutputFields}); + } + else + { + setTestScriptDefinitionObject({testInputFields: [ + new QFieldMetaData({name: "recordPrimaryKeyList", label: "Record Primary Key List"}) + ], testOutputFields: []}) + } + const criteria = [new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, [scriptId])]; const orderBys = [new QFilterOrderBy("sequenceNo", false)]; const filter = new QQueryFilter(criteria, orderBys); @@ -181,11 +215,11 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element { (async () => { - // fetch the full version - setSelectedVersionRecord(version); + setCurrentVersionId(version.values.get("id")); loadingSelectedVersion.setLoading(); - const selectedVersion = await qController.get("scriptVersion", version.values.get("id")); + // fetch the full version + const selectedVersion = await qController.get("scriptRevision", version.values.get("id")); console.log("Fetched selectedVersion:"); console.log(selectedVersion); setSelectedVersionRecord(selectedVersion); @@ -236,11 +270,38 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element ; } + const getScriptLogs = (scriptRevisionId: number) => + { + if(!scriptLogs[scriptRevisionId]) + { + (async () => + { + scriptLogs[scriptRevisionId] = await qController.query("scriptLog", new QQueryFilter([new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, [scriptRevisionId])]), 100, 0); + setScriptLogs(scriptLogs); + forceUpdate(); + })(); + return Loading...; + } + + const logs = scriptLogs[scriptRevisionId] as any[]; + if (logs === null || logs === undefined) + { + return Loading...; + } + + if (logs.length === 0) + { + return No logs available for this version.; + } + + return (); + } + let editButtonTooltip = ""; let editButtonText = "Create New Version"; if (currentVersionId) { - if (currentVersionId === selectedVersionRecord?.values?.get("id")) + if (currentVersionId === scriptRecord?.values?.get("currentScriptRevisionId")) { editButtonTooltip = "If you make any changes to this script, a new version will be created when you hit Save."; editButtonText = "Edit"; @@ -253,6 +314,17 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element } } + function buildScriptLogFilter(scriptRevisionId: any) + { + return JSON.stringify(new QQueryFilter([new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, [scriptRevisionId])])); + } + + /* + position: relative; + left: -356px; + width: calc(100% + 380px); + */ + return ( @@ -288,13 +360,15 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element changeTab(newValue)} variant="standard" > - - + + + + @@ -314,7 +388,7 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element Version {selectedVersionRecord.values.get("sequenceNo")} { - currentVersionId === selectedVersionRecord.values.get("id") + currentVersionId === scriptRecord.values.get("currentScriptRevisionId") ? (<> (Current)) : <> } @@ -350,7 +424,7 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element - {/* + @@ -360,17 +434,34 @@ export default function ScriptViewer({scriptId}: Props): JSX.Element {getVersionsList(versionRecordList, selectedVersionRecord)} - - Data Preview (Version {selectedVersionRecord?.values?.get("sequenceNo")}) - - - {loadingSelectedVersion.isNotLoading() && selectedTab == 1 && selectedVersionRecord?.values?.get("data") && } - {loadingSelectedVersion.isLoadingSlow() && Loading...} - + { + selectedVersionRecord ? ( + <> + + Script Logs (Version {selectedVersionRecord?.values.get("sequenceNo")}) + View All + + + {getScriptLogs(selectedVersionRecord.values.get("id"))} + + + ) : Select a version to view logs + } - */} + + + + + + + + + + + + diff --git a/src/qqq/pages/records/view/RecordDeveloperView.tsx b/src/qqq/pages/records/view/RecordDeveloperView.tsx index ec6fe9f..7f32f7b 100644 --- a/src/qqq/pages/records/view/RecordDeveloperView.tsx +++ b/src/qqq/pages/records/view/RecordDeveloperView.tsx @@ -22,33 +22,18 @@ import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; -import {Alert, Chip, Icon, ListItem, ListItemAvatar, Typography} from "@mui/material"; -import Avatar from "@mui/material/Avatar"; +import {Alert, Typography} from "@mui/material"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Card from "@mui/material/Card"; -import Divider from "@mui/material/Divider"; import Grid from "@mui/material/Grid"; -import List from "@mui/material/List"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import ListItemText from "@mui/material/ListItemText"; -import Modal from "@mui/material/Modal"; import Snackbar from "@mui/material/Snackbar"; -import Tab from "@mui/material/Tab"; -import Tabs from "@mui/material/Tabs"; import React, {useContext, useReducer, useState} from "react"; import AceEditor from "react-ace"; import {useParams} from "react-router-dom"; import QContext from "QContext"; -import TabPanel from "qqq/components/misc/TabPanel"; -import AssociatedScriptEditor from "qqq/components/scripts/AssociatedScriptEditor"; -import ScriptDocsForm from "qqq/components/scripts/ScriptDocsForm"; -import ScriptLogsView from "qqq/components/scripts/ScriptLogsView"; -import ScriptTestForm from "qqq/components/scripts/ScriptTestForm"; -import CustomWidthTooltip from "qqq/components/tooltips/CustomWidthTooltip"; import ScriptViewer from "qqq/components/widgets/misc/ScriptViewer"; import BaseLayout from "qqq/layouts/BaseLayout"; -import DeveloperModeUtils from "qqq/utils/DeveloperModeUtils"; import Client from "qqq/utils/qqq/Client"; import ValueUtils from "qqq/utils/qqq/ValueUtils"; @@ -60,7 +45,6 @@ import "ace-builds/src-noconflict/ext-language_tools"; const qController = Client.getInstance(); -// Declaring props types for ViewForm interface Props { table?: QTableMetaData; @@ -89,7 +73,6 @@ function RecordDeveloperView({table}: Props): JSX.Element const [viewingRevisions, setViewingRevisions] = useState({} as any); const [scriptLogs, setScriptLogs] = useState({} as any); - const [editingScript, setEditingScript] = useState(null as any); const [alertText, setAlertText] = useState(null as string); const {setPageHeader} = useContext(QContext); @@ -152,135 +135,13 @@ function RecordDeveloperView({table}: Props): JSX.Element })(); } - const editScript = (fieldName: string, code: string, object: any) => + const createScript = async (fieldName: string) => { - const editingScript = {} as any; - editingScript.fieldName = fieldName; - editingScript.titlePrefix = code ? "Editing Script" : "Creating New Script"; - editingScript.code = code; - editingScript.scriptDefinitionObject = object; - setEditingScript(editingScript); - }; - - const closeEditingScript = (event: object, reason: string, alert: string = null) => - { - if (reason === "backdropClick" || reason === "escapeKeyDown") - { - return; - } - - if (reason === "saved") - { - setAsyncLoadInited(false); - setAssociatedScripts([]); - viewingRevisions[editingScript.fieldName] = null; - setViewingRevisions(viewingRevisions); - forceUpdate(); - } - - if (alert) - { - setAlertText(alert); - } - - setEditingScript(null); - }; - - const changeTab = (newValue: number, fieldName: string) => - { - selectedTabs[fieldName] = newValue; - setSelectedTabs(selectedTabs); + const rs = await qController.storeRecordAssociatedScript(tableName, id, fieldName, "// Edit this new script to define its code.", "Initial version"); + record.values.set(fieldName, rs.scriptId); forceUpdate(); }; - const selectRevision = (fieldName: string, revisionId: number) => - { - viewingRevisions[fieldName] = revisionId; - setViewingRevisions(viewingRevisions); - - scriptLogs[revisionId] = null; - setScriptLogs(scriptLogs); - - loadRevisionLogs(fieldName, revisionId); - - forceUpdate(); - }; - - const loadRevisionLogs = (fieldName: string, revisionId: number) => - { - (async () => - { - const rs = await qController.getRecordAssociatedScriptLogs(tableName, id, fieldName, revisionId); - scriptLogs[revisionId] = []; - if (rs["scriptLogRecords"]) - { - scriptLogs[revisionId] = rs["scriptLogRecords"]; - } - console.log("Script logs:"); - console.log(scriptLogs[revisionId]); - setScriptLogs(scriptLogs); - forceUpdate(); - })(); - }; - - function getRevisionsList(scriptRevisions: any, fieldName: any, currentScriptRevisionId: any) - { - return - { - scriptRevisions ? <> : - - There are not any versions of this script. - - } - { - scriptRevisions?.map((revision: any) => ( - - selectRevision(fieldName, revision.values.id)}> - - {`${revision.values.sequenceNo}`} - - - {revision.values.id == currentScriptRevisionId && } - {revision.values.commitMessage} - - } - secondary={ - <> - {ValueUtils.formatDateTime(revision.values.createDate)} -
- {revision.values.author} - - } - /> - settings -
- -
- )) - } -
; - } - - function getScriptLogs(revisionId: number) - { - const logs = scriptLogs[revisionId] as any[]; - if (logs === null || logs === undefined) - { - return Loading...; - } - - if (logs.length === 0) - { - return No logs available for this version.; - } - - return (); - } - return ( @@ -289,8 +150,7 @@ function RecordDeveloperView({table}: Props): JSX.Element { notFoundMessage - ? - {notFoundMessage} + ? {notFoundMessage} : { @@ -323,18 +183,33 @@ function RecordDeveloperView({table}: Props): JSX.Element { associatedScripts && associatedScripts.map((object) => { + console.log(object); let fieldName = object.associatedScript?.fieldName; let field = tableMetaData.fields.get(fieldName); - let scriptId = recordJSONString ? recordJSONObject[fieldName] : null; + let scriptId = record?.values.get(fieldName); return (
- {field?.label} + {field?.label} {scriptId ? - - : <>No script here yet. {/* todo! */} + + : <> + + No script has been created in this field for this record at this time. + + + + + } @@ -343,171 +218,8 @@ function RecordDeveloperView({table}: Props): JSX.Element }) } - {/* - associatedScripts && associatedScripts.map((object) => - { - let fieldName = object.associatedScript?.fieldName; - let field = tableMetaData.fields.get(fieldName); - - let currentScriptRevisionId = object.script?.values?.currentScriptRevisionId; - - if (!selectedTabs[fieldName]) - { - selectedTabs[fieldName] = 0; - } - - if (!viewingRevisions[fieldName] || viewingRevisions[fieldName] === -1) - { - console.log(`Defaulting revision for ${fieldName} to ${currentScriptRevisionId}`); - viewingRevisions[fieldName] = currentScriptRevisionId; - - if (!scriptLogs[currentScriptRevisionId]) - { - loadRevisionLogs(fieldName, currentScriptRevisionId); - } - } - - const viewingRevisionArray = object.scriptRevisions?.filter((rev: any) => rev?.values?.id === viewingRevisions[fieldName]); - const code = viewingRevisionArray?.length > 0 ? viewingRevisionArray[0].values.contents : ""; - const viewingSequenceNo = viewingRevisionArray?.length > 0 ? viewingRevisionArray[0].values.sequenceNo : ""; - - let editButtonTooltip = ""; - let editButtonText = "Create New Script"; - if (currentScriptRevisionId) - { - if (currentScriptRevisionId === viewingRevisions[fieldName]) - { - editButtonTooltip = "If you make any changes to this script, a new version will be created when you hit Save."; - editButtonText = "Edit"; - } - else - { - editButtonTooltip = "If you want to make this previous Version active, bring up the Edit window, make any changes " + - "to the old Version if they are needed, then click Save. A new Version will be created, and set as Current."; - editButtonText = "Edit and Activate"; - } - } - - - return ( - - - - {field?.label} - changeTab(newValue, fieldName)} - variant="standard" - > - - - - - - - - - - - - Versions - - {getRevisionsList(object.scriptRevisions, fieldName, currentScriptRevisionId)} - - - - - { - currentScriptRevisionId && - - { - currentScriptRevisionId === viewingRevisions[fieldName] - ? (<>Current Version ({viewingSequenceNo})) - : (<>Version {viewingSequenceNo}) - } - - } - - - - - { - code ? ( - <> - - - ) : null - } - - - - - - - - Versions - - {getRevisionsList(object.scriptRevisions, fieldName, currentScriptRevisionId)} - - - - Script Logs (Version {viewingSequenceNo}) - - - {getScriptLogs(viewingRevisions[fieldName])} - - - - - - - - - - - - - - - - - ); - }) - */} - - - { - editingScript && - closeEditingScript(event, reason)}> - - - } - } diff --git a/src/qqq/styles/qqq-override-styles.css b/src/qqq/styles/qqq-override-styles.css index f484b9b..ea147c5 100644 --- a/src/qqq/styles/qqq-override-styles.css +++ b/src/qqq/styles/qqq-override-styles.css @@ -339,6 +339,12 @@ input[type="search"]::-webkit-search-results-decoration { display: none; } border-right: 0.0625rem solid rgb(222, 226, 230); } +.scriptLogsView TD, +.scriptLogsView TH +{ + white-space: nowrap; +} + .fieldLabel { color: rgb(52, 71, 103);