mirror of
https://github.com/rowyio/rowy.git
synced 2026-02-24 04:01:17 +01:00
ft-build stream: individual log timestamp; status
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
</Typography>
|
||||
<Typography variant="body2" className={classes.logEntry}>
|
||||
<Ansi>{logString.replaceAll("\\n", "\n").replaceAll("\\t", "\t")}</Ansi>
|
||||
<Ansi className={classes.logTimestamp}>
|
||||
{moment(logRecord.timestamp).format("LTS")}
|
||||
</Ansi>
|
||||
{" "}
|
||||
<Ansi>
|
||||
{logRecord.log.replaceAll("\\n", "\n").replaceAll("\\t", "\t")}
|
||||
</Ansi>
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
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 && (
|
||||
<Box p={3} className={classes.logEntryWrapper}>
|
||||
{logs?.map((log, index) => {
|
||||
return <LogRow logString={log} index={index} />;
|
||||
return <LogRow logRecord={log} index={index} />;
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
@@ -166,11 +186,23 @@ export default function TableLogs() {
|
||||
onChange={handleTabChange}
|
||||
className={classes.tabs}
|
||||
>
|
||||
{collectionState.rows.map((value, index) => (
|
||||
{collectionState.rows.map((logEntry, index) => (
|
||||
<Tab
|
||||
label={moment(value.startTimeStamp).format(
|
||||
"MMMM D YYYY h:mm:ssa"
|
||||
)}
|
||||
label={
|
||||
<Box className={classes.tab}>
|
||||
<Box>
|
||||
{moment(logEntry.startTimeStamp).format(
|
||||
"MMMM D YYYY h:mm:ssa"
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
{logEntry.status === "BUILDING" && (
|
||||
<CircularProgress size={24} />
|
||||
)}
|
||||
{logEntry.status === "SUCCESS" && <SuccessIcon />}
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
{...a11yProps(index)}
|
||||
/>
|
||||
))}
|
||||
@@ -180,6 +212,7 @@ export default function TableLogs() {
|
||||
value={tabIndex}
|
||||
index={index}
|
||||
logs={logEntry?.fullLog}
|
||||
status={logEntry?.status}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user