diff --git a/ft_build/compiler/index.ts b/ft_build/compiler/index.ts
index ea9dce16..2a016f24 100644
--- a/ft_build/compiler/index.ts
+++ b/ft_build/compiler/index.ts
@@ -16,16 +16,18 @@ export default async function generateConfig(
streamLogger
).then(async (success) => {
if (!success) {
- await streamLogger(`generateConfigFromTableSchema failed to complete`);
+ await streamLogger.info(
+ `generateConfigFromTableSchema failed to complete`
+ );
return false;
}
- await streamLogger(`generateConfigFromTableSchema done`);
+ await streamLogger.info(`generateConfigFromTableSchema done`);
const configFile = fs.readFileSync(
path.resolve(__dirname, "../functions/src/functionConfig.ts"),
"utf-8"
);
- await streamLogger(`configFile: ${JSON.stringify(configFile)}`);
+ await streamLogger.info(`configFile: ${JSON.stringify(configFile)}`);
const requiredDependencies = configFile.match(
/(?<=(require\(("|'))).*?(?=("|')\))/g
);
@@ -39,7 +41,7 @@ export default async function generateConfig(
return false;
}
}
- await streamLogger(
+ await streamLogger.info(
`requiredDependencies: ${JSON.stringify(requiredDependencies)}`
);
@@ -54,7 +56,7 @@ export default async function generateConfig(
streamLogger
)
);
- await streamLogger(
+ await streamLogger.info(
`isFunctionConfigValid: ${JSON.stringify(isFunctionConfigValid)}`
);
if (!isFunctionConfigValid) {
@@ -63,7 +65,9 @@ export default async function generateConfig(
const { sparksConfig } = require("../functions/src/functionConfig.js");
const requiredSparks = sparksConfig.map((s: any) => s.type);
- await streamLogger(`requiredSparks: ${JSON.stringify(requiredSparks)}`);
+ await streamLogger.info(
+ `requiredSparks: ${JSON.stringify(requiredSparks)}`
+ );
for (const lib of requiredSparks) {
const success = await addSparkLib(lib, user, streamLogger);
diff --git a/ft_build/compiler/loader.ts b/ft_build/compiler/loader.ts
index 7bc6293d..530f6242 100644
--- a/ft_build/compiler/loader.ts
+++ b/ft_build/compiler/loader.ts
@@ -9,15 +9,17 @@ export const generateConfigFromTableSchema = async (
user: admin.auth.UserRecord,
streamLogger
) => {
- await streamLogger("getting schema...");
+ await streamLogger.info("getting schema...");
const schemaDoc = await db.doc(schemaDocPath).get();
const schemaData = schemaDoc.data();
if (!schemaData) throw new Error("no schema found");
- await streamLogger(`schemaData: ${JSON.stringify(schemaData)}`);
+ await streamLogger.info(`schemaData: ${JSON.stringify(schemaData)}`);
const derivativeColumns = Object.values(schemaData.columns).filter(
(col: any) => col.type === "DERIVATIVE"
);
- await streamLogger(`derivativeColumns: ${JSON.stringify(derivativeColumns)}`);
+ await streamLogger.info(
+ `derivativeColumns: ${JSON.stringify(derivativeColumns)}`
+ );
const derivativesConfig = `[${derivativeColumns.reduce(
(acc, currColumn: any) => {
if (
@@ -41,12 +43,14 @@ export const generateConfigFromTableSchema = async (
},
""
)}]`;
- await streamLogger(`derivativesConfig: ${JSON.stringify(derivativesConfig)}`);
+ await streamLogger.info(
+ `derivativesConfig: ${JSON.stringify(derivativesConfig)}`
+ );
const initializableColumns = Object.values(
schemaData.columns
).filter((col: any) => Boolean(col.config?.defaultValue));
- await streamLogger(
+ await streamLogger.info(
`initializableColumns: ${JSON.stringify(initializableColumns)}`
);
const initializeConfig = `[${initializableColumns.reduce(
@@ -73,7 +77,9 @@ export const generateConfigFromTableSchema = async (
},
""
)}]`;
- await streamLogger(`initializeConfig: ${JSON.stringify(initializeConfig)}`);
+ await streamLogger.info(
+ `initializeConfig: ${JSON.stringify(initializeConfig)}`
+ );
const documentSelectColumns = Object.values(schemaData.columns).filter(
(col: any) => col.type === "DOCUMENT_SELECT" && col.config?.trackedFields
);
@@ -87,12 +93,12 @@ export const generateConfigFromTableSchema = async (
},
""
)}]`;
- await streamLogger(
+ await streamLogger.info(
`documentSelectColumns: ${JSON.stringify(documentSelectColumns)}`
);
const sparksConfig = parseSparksConfig(schemaData.sparks, user);
- await streamLogger(`sparksConfig: ${JSON.stringify(sparksConfig)}`);
+ await streamLogger.info(`sparksConfig: ${JSON.stringify(sparksConfig)}`);
const collectionType = schemaDocPath.includes("subTables")
? "subCollection"
@@ -144,7 +150,7 @@ export const generateConfigFromTableSchema = async (
default:
break;
}
- await streamLogger(`collectionType: ${JSON.stringify(collectionType)}`);
+ await streamLogger.info(`collectionType: ${JSON.stringify(collectionType)}`);
// generate field types from table meta data
const fieldTypes = JSON.stringify(
@@ -160,7 +166,7 @@ export const generateConfigFromTableSchema = async (
};
}, {})
);
- await streamLogger(`fieldTypes: ${JSON.stringify(fieldTypes)}`);
+ await streamLogger.info(`fieldTypes: ${JSON.stringify(fieldTypes)}`);
const exports: any = {
fieldTypes,
@@ -171,12 +177,12 @@ export const generateConfigFromTableSchema = async (
documentSelectConfig,
sparksConfig,
};
- await streamLogger(`exports: ${JSON.stringify(exports)}`);
+ await streamLogger.info(`exports: ${JSON.stringify(exports)}`);
const fileData = Object.keys(exports).reduce((acc, currKey) => {
return `${acc}\nexport const ${currKey} = ${exports[currKey]}`;
}, ``);
- await streamLogger(`fileData: ${JSON.stringify(fileData)}`);
+ await streamLogger.info(`fileData: ${JSON.stringify(fileData)}`);
const path = require("path");
fs.writeFileSync(
diff --git a/ft_build/index.ts b/ft_build/index.ts
index 5e811f68..0d91234a 100644
--- a/ft_build/index.ts
+++ b/ft_build/index.ts
@@ -88,8 +88,8 @@ app.post("/", jsonParser, async (req: any, res: any) => {
});
}
- const streamLogger = await createStreamLogger(configPath, Date.now());
- await streamLogger("streamLogger created");
+ const streamLogger = await createStreamLogger(configPath);
+ await streamLogger.info("streamLogger created");
const success = await generateConfig(configPath, user, streamLogger);
if (!success) {
@@ -100,7 +100,7 @@ app.post("/", jsonParser, async (req: any, res: any) => {
});
return;
}
- await streamLogger("generateConfig success");
+ await streamLogger.info("generateConfig success");
let hasEnvError = false;
@@ -134,7 +134,7 @@ app.post("/", jsonParser, async (req: any, res: any) => {
commandErrorHandler({ user }, streamLogger)
);
- await streamLogger(`build complete`);
+ await streamLogger.success();
res.send({
success: true,
});
diff --git a/ft_build/utils.ts b/ft_build/utils.ts
index a0caef8b..334ea473 100644
--- a/ft_build/utils.ts
+++ b/ft_build/utils.ts
@@ -26,7 +26,7 @@ function commandErrorHandler(
streamLogger
) {
return async function (error, stdout, stderr) {
- await streamLogger(stdout);
+ await streamLogger.info(stdout);
if (!error) {
return;
@@ -93,26 +93,60 @@ function parseSparksConfig(
return "[]";
}
-async function createStreamLogger(
- tableConfigPath: string,
- startTimeStamp: number
-) {
- const fullLog: string[] = [];
+async function createStreamLogger(tableConfigPath: string) {
+ const startTimeStamp = Date.now();
+ const fullLog: {
+ log: string;
+ level: "info" | "error";
+ timestamp: number;
+ }[] = [];
const logRef = db
.doc(tableConfigPath)
.collection("ftBuildLogs")
.doc(startTimeStamp.toString());
- await logRef.set({ startTimeStamp });
+ await logRef.set({ startTimeStamp, status: "BUILDING" });
+
console.log(
`streamLogger created. tableConfigPath: ${tableConfigPath}, startTimeStamp: ${startTimeStamp}`
);
- return async (log: string) => {
- console.log(log);
- fullLog.push(log);
- await logRef.update({
- fullLog,
- });
+ return {
+ info: async (log: string) => {
+ console.log(log);
+ fullLog.push({
+ log,
+ level: "info",
+ timestamp: Date.now(),
+ });
+ await logRef.update({
+ fullLog,
+ });
+ },
+ error: async (log: string) => {
+ console.error(log);
+ fullLog.push({
+ log,
+ level: "error",
+ timestamp: Date.now(),
+ });
+ await logRef.update({
+ fullLog,
+ });
+ },
+ success: async () => {
+ console.log("streamLogger marked as SUCCESS");
+ await logRef.update({
+ status: "SUCCESS",
+ successTimeStamp: Date.now(),
+ });
+ },
+ fail: async () => {
+ console.log("streamLogger marked as FAIL");
+ await logRef.update({
+ status: "FAIL",
+ failTimeStamp: Date.now(),
+ });
+ },
};
}
diff --git a/www/src/components/Table/TableHeader/TableLogs.tsx b/www/src/components/Table/TableHeader/TableLogs.tsx
index b453ec93..06a9ff46 100644
--- a/www/src/components/Table/TableHeader/TableLogs.tsx
+++ b/www/src/components/Table/TableHeader/TableLogs.tsx
@@ -9,14 +9,18 @@ import _find from "lodash/find";
import _sortBy from "lodash/sortBy";
import moment from "moment";
-import Tabs from "@material-ui/core/Tabs";
-import Tab from "@material-ui/core/Tab";
-import { Chip } from "@material-ui/core";
+import {
+ Chip,
+ CircularProgress,
+ Typography,
+ Box,
+ Tabs,
+ Tab,
+} from "@material-ui/core";
import Modal from "components/Modal";
import { makeStyles } from "@material-ui/core/styles";
-import Typography from "@material-ui/core/Typography";
-import Box from "@material-ui/core/Box";
import LogsIcon from "@material-ui/icons/QueryBuilder";
+import SuccessIcon from "@material-ui/icons/CheckCircleOutline";
import TableHeaderButton from "./TableHeaderButton";
import Ansi from "ansi-to-react";
@@ -39,6 +43,12 @@ const useStyles = makeStyles((theme) => ({
tabs: {
borderRight: `1px solid ${theme.palette.divider}`,
},
+ tab: {
+ display: "flex",
+ flexWrap: "nowrap",
+ alignItems: "center",
+ justifyItems: "center",
+ },
logPanel: {
width: "100%",
@@ -59,15 +69,19 @@ const useStyles = makeStyles((theme) => ({
whiteSpace: "break-spaces",
userSelect: "text",
},
+ logTimestamp: {
+ color: "green",
+ },
}));
LogPanel.propTypes = {
logs: PropTypes.array,
+ status: PropTypes.string,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired,
};
-function LogRow({ logString, index }) {
+function LogRow({ logRecord, index }) {
const classes = useStyles();
return (
@@ -76,14 +90,20 @@ function LogRow({ logString, index }) {
{index}
- {logString.replaceAll("\\n", "\n").replaceAll("\\t", "\t")}
+
+ {moment(logRecord.timestamp).format("LTS")}
+
+ {" "}
+
+ {logRecord.log.replaceAll("\\n", "\n").replaceAll("\\t", "\t")}
+
);
}
function LogPanel(props) {
- const { logs, value, index, ...other } = props;
+ const { logs, status, value, index, ...other } = props;
const classes = useStyles();
return (
@@ -98,7 +118,7 @@ function LogPanel(props) {
{value === index && (
{logs?.map((log, index) => {
- return ;
+ return ;
})}
)}
@@ -166,11 +186,23 @@ export default function TableLogs() {
onChange={handleTabChange}
className={classes.tabs}
>
- {collectionState.rows.map((value, index) => (
+ {collectionState.rows.map((logEntry, index) => (
+
+ {moment(logEntry.startTimeStamp).format(
+ "MMMM D YYYY h:mm:ssa"
+ )}
+
+
+ {logEntry.status === "BUILDING" && (
+
+ )}
+ {logEntry.status === "SUCCESS" && }
+
+
+ }
{...a11yProps(index)}
/>
))}
@@ -180,6 +212,7 @@ export default function TableLogs() {
value={tabIndex}
index={index}
logs={logEntry?.fullLog}
+ status={logEntry?.status}
/>
))}