diff --git a/server/controllers/helperFunctions/universal.helpers.ts b/server/controllers/helperFunctions/universal.helpers.ts index 52e5eba5..0c2d4760 100644 --- a/server/controllers/helperFunctions/universal.helpers.ts +++ b/server/controllers/helperFunctions/universal.helpers.ts @@ -12,6 +12,7 @@ interface NewColumn { } //---------------CONNECT TO THE DATABASE----------------------------------------------------------------------------------------- +let dbDataSource: DataSource | null = null; export const dbConnect = async (req: Request) => { const { db_type, hostname, password, port, username, database_name, service_name } = diff --git a/server/controllers/postgresData.controller.ts b/server/controllers/postgresData.controller.ts index 4923cb01..79767827 100644 --- a/server/controllers/postgresData.controller.ts +++ b/server/controllers/postgresData.controller.ts @@ -22,7 +22,9 @@ const postgresController = { //----------Function to collect all schema and data from database----------------------------------------------------------------- postgresQuery: async (req: Request, res: Response, next: NextFunction) => { const PostgresDataSource = await dbConnect(req); + console.log('postgresQuery REQ: ', req); console.log('MADE IT TO postgresQuery MIDDLEWARE'); + /* * Used for storing Primary Key table and column names that are * part of Foreign Keys to adjust IsDestination to be true. @@ -146,6 +148,8 @@ const postgresController = { // Storage of queried results into res.locals res.locals.schema = schema; res.locals.data = tableData; + // res.locals.database_link = req.query.database_link; + // res.locals.db_connection = PostgresDataSource; // Disconnecting after data has been received PostgresDataSource.destroy(); @@ -159,6 +163,52 @@ const postgresController = { } }, + //----------Function to gather query metrics from database----------------------------------------------------------------- + postgresGetMetrics: async (req: Request, res: Response, next: NextFunction) => { + // if we pass database_link from FE then we might not need to initialize dbConnect again + // database_link and query can be passed in from FE + // console.log('FULL REQUEST: ', req); + const PostgresGetMetrics = await dbConnect(req); + // console.log('dblink check: ', res.locals.database_link); + console.log('REACHED postgresGetMetrics MIDDLEWARE'); + console.log('REQ QUERY: ', req.query); + const { queryString } = req.query; + console.log('❓ QUERY FROM FE IS: ', queryString); + + // Query string (EXPLAIN) to access performance data + const testQuery = `EXPLAIN (FORMAT JSON, ANALYZE, VERBOSE, BUFFERS) ${queryString};`; + // view result of Explain query + const result = await PostgresGetMetrics.query(testQuery); + // console.log('🌟 result of testing: ', result); + console.log('⭐️QUERY PLAN RESULT: ', result[0]['QUERY PLAN']); + + // Pull Execution time only + const executionTime = `Execution Time: ${result[0]['QUERY PLAN'][0]['Execution Time']}`; + // console.log('⏰ EXECUTION TIME METRIC', executionTime); + + // Create date metric to add to response + const now = new Date(); + const options: any = { + year: 'numeric', + month: 'long', + day: 'numeric', + // hour: 'numeric', + // minute: 'numeric', + // second: 'numeric', + // hour12: true, // Use 12-hour time format + timeZone: 'America/New_York', // Set to US Eastern Time + }; + const formattedDate = `Date Run: ${now.toLocaleString('en-US', options)}`; + // console.log(`πŸ—“οΈ TODAY'S DATE`, formattedDate); + + // console.log('done w getMetrics controller'); + + // Send date and execution time on response + res.locals.metrics = [formattedDate, executionTime] ; + + return next(); + }, + //-------------------------------------DATA TABLE ROWS------------------------------------------------- //-------------------ADD NEW ROW----------------------------------------------------------- postgresAddNewRow: async (req: Request, res: Response, next: NextFunction) => { diff --git a/server/routes/postgres.router.ts b/server/routes/postgres.router.ts index 6f4b79f9..7d8c6311 100644 --- a/server/routes/postgres.router.ts +++ b/server/routes/postgres.router.ts @@ -12,6 +12,14 @@ postgresRouter.get( } ); +postgresRouter.get( + '/run-query', + postgresController.postgresGetMetrics, + (_req: Request, res: Response) => { + return res.status(200).json(res.locals.metrics); + } +); + //-------------------------------------DATA TABLE ROWS---------------------------------------------------------------------------------------- //-------------------ADD NEW ROW----------------------------------------------------------------------------------------- postgresRouter.post( diff --git a/src/components/DBDisplay/Sidebar.tsx b/src/components/DBDisplay/Sidebar.tsx index 6085f8fe..fb9de3f8 100644 --- a/src/components/DBDisplay/Sidebar.tsx +++ b/src/components/DBDisplay/Sidebar.tsx @@ -92,6 +92,7 @@ const Sidebar = (props: any) => { values.password = internalLinkArray_Postgres[0]; values.port = postgresPort ? postgresPort : '5432'; values.database_name = postgresName; + // values.database_link = fullLink; values.db_type = 'postgres'; break; } diff --git a/src/pages/TestNewQuery.tsx b/src/pages/TestNewQuery.tsx index cfc88413..8babc0df 100644 --- a/src/pages/TestNewQuery.tsx +++ b/src/pages/TestNewQuery.tsx @@ -1,8 +1,11 @@ //* this will be coming from the sidebar (FeatureTab) import React from 'react'; +import axios from 'axios'; import { useEffect, useState } from 'react'; import FeatureTab from '../components/DBDisplay/FeatureTab'; import { NavLink } from 'react-router-dom'; +import useCredentialsStore from '../store/credentialsStore'; +import { QueryRunnerAlreadyReleasedError } from 'typeorm'; // db selecting from prev connected by user type Database = { @@ -11,18 +14,41 @@ type Database = { }; // defining type of query result -type QueryResult = any; +type QueryResult = []; const TestNewQuery: React.FC = () => { // holds the list of dbs user can select from const [dbInput, setDbInput] = useState(null); // holds the user's query input - const [textInput, setTextInput] = useState(''); + const [queryInput, setQueryInput] = useState(''); // holds the selected db which will be from an arr of all saved dbs from user const [selectedDb, setSelectedDb] = useState(null); // holds the result of the query after post req const [queryResult, setQueryResult] = useState(null); + // holds database link to test query on + const [databaseLink, setDatabaseLink] = useState(''); + // sets state of db credentials to allow connection on BE + // TODO: Review the use of Store in this case + const setDbCredentials = useCredentialsStore((state) => state.setDbCredentials); + + // State variables / types for DB link elements which sets values object + // TODO: Review this state declaration: should we be setting a state here? + const [dbValues, setDbValues] = useState<{ + db_type: string; + database_link?: string; + hostname?: string; + port?: string; + username?: string; + password?: string; + database_name?: string; + service_name?: string; + file_path?: string; + queryString?: string; + }>({ db_type: 'postgres' }); + //END: STATE DECLARATION + + // ! Not sure if this useEffect is needed. we aren't loading any data on render // getting req to query / select db user is using useEffect(() => { const fetchUserDatabase = async () => { @@ -44,46 +70,53 @@ const TestNewQuery: React.FC = () => { console.error('Failed to fetch user database', error); } }; - fetchUserDatabase(); + // fetchUserDatabase(); }, []); // leave empty dependency array to run once on mount - // post req to send query & db to the back for execution + // Send DB link and query string to BE for testing const sendQuery = async () => { try { - // conditional to prevent missing input - if (!textInput || !selectedDb) { - alert('Please enter a query and make sure a database is connected'); - return; + // Define params to be sent to BE + const values: any = dbValues; + // Break down DB link into relevant elements for BE to read + if (databaseLink) { + const fullLink = databaseLink; + const splitURI = fullLink.split('/'); + const postgresName = splitURI[3]; + const postgresPort = splitURI[2].split(':')[2]; + const internalLinkArray_Postgres = splitURI[2].split(':')[1].split('@'); + values.hostname = internalLinkArray_Postgres[1]; + values.username = splitURI[2].split(':')[0]; + values.password = internalLinkArray_Postgres[0]; + values.port = postgresPort ? postgresPort : '5432'; + values.database_name = postgresName; + values.db_type = 'postgres'; + values.queryString = queryInput; // include query string on params } - // post req - //TODO where is it getting sent to?? backend route?? - const response = await fetch('', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: textInput, - databaseId: selectedDb.id, - }), - }); - - // conditional for failed resp - if (!response.ok) { - throw new Error('HTTP error! status: ${response.status}'); - } + // View values array + // console.log('VALUES FROM testNewQuery FE: ', values); - // parsing and saving the resp (query result) - const result = await response.json(); - // updates state - setQueryResult(result); + // Update DB credential store with values from passed in link + setDbCredentials(values); + const dataFromBackend = await axios + .get(`/api/sql/${values.db_type}/run-query`, { params: values }) + .then((res) => { + console.log('response from BE: ', res.data); + return res.data; // data is an array + }) + .catch((err: ErrorEvent) => console.error('getSchema error', err)); + // set query result state with data from response (array) + setQueryResult(dataFromBackend); } catch (error) { - console.error('Failed to run query', error); - setQueryResult({ error: 'Something went wrong with running the query' }); + console.error('sendQuery Error: Failed to test query', error); } }; + // pull metrics from data array before display + const metrics = queryResult?.map((metric) =>
{metric}
); + + // ! Is saveQuery needed? // post req to save query const saveQuery = async () => { try { @@ -95,27 +128,27 @@ const TestNewQuery: React.FC = () => { // post req //TODO where is it getting sent to?? backend route?? - const response = await fetch('', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: textInput, - databaseId: selectedDb.id, - result: queryResult, - }), - }); - - // conditional for failed resp - if (!response.ok) { - throw new Error('HTTP error! status: ${response.status}'); - } + // const response = await fetch('', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // query: textInput, + // databaseId: selectedDb.id, + // result: queryResult, + // }), + // }); + // // conditional for failed resp + // if (!response.ok) { + // throw new Error('HTTP error! status: ${response.status}'); + // } } catch (error) { - console.error('Failed to save query results', error); + // console.error('Failed to save query results', error); } }; + // TODO Implement AI // post req to Improve w/ AI const improveWithAi = async () => { try { @@ -127,112 +160,130 @@ const TestNewQuery: React.FC = () => { // post req //TODO where is it getting sent to?? backend route?? - const response = await fetch('', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: textInput, - databaseId: selectedDb.id, - result: queryResult, - }), - }); - - // conditional for failed resp - if (!response.ok) { - throw new Error('HTTP error! status: ${response.status}'); - } + // const response = await fetch('', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // query: textInput, + // databaseId: selectedDb.id, + // result: queryResult, + // }), + // }); + + // // conditional for failed resp + // if (!response.ok) { + // throw new Error('HTTP error! status: ${response.status}'); + // } } catch (error) { - console.error('Coming Soon!', error); + // console.error('Coming Soon!', error); } }; + //TODO get the FeatureTab to not sit on top of content in the page return ( -
- {/* */} -
-

- Test New Query Page
-

-
+ <> +
+ {/* */} +
+

+ Test New Query Page
+

+
- {/* πŸ’™πŸ’™ Improve w/ AI Button -------------- */} -
- -
- {/* πŸ’™πŸ’™ Select db dropdown ------------------- */} - {dbInput && ( -
- - + Improve with AI +
- )} - {/* turnery to serve as placeholder for the time between db being fetech and db being rendered */} - {selectedDb ? ( - Connected to: {selectedDb.name} - ) : ( -

Loading database...

- )} - {/* πŸ’™πŸ’™ Query Input ------------- */} -
+ + {/* TEMPORARY BE - test db + query */} + {/* revas link: + postgresql://postgres.gcfszuopjvbjtllgmenw:store2025@aws-0-us-east-1.pooler.supabase.com:6543/postgres + */}