Skip to content

Commit

Permalink
Export rules yaml working
Browse files Browse the repository at this point in the history
  • Loading branch information
gerrycampion committed Feb 21, 2024
1 parent d9d2863 commit ded6316
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 54 deletions.
85 changes: 37 additions & 48 deletions src/components/Controls/Controls.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext, useState } from "react";
import { useContext, useState, MouseEvent } from "react";
import AppContext from "../AppContext";
import PromptDialog from "../PromptDialog/PromptDialog";
import { IconButton, Toolbar, Tooltip } from "@mui/material";
import { IconButton, Menu, Toolbar, Tooltip } from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import SaveIcon from "@mui/icons-material/Save";
import RestoreIcon from "@mui/icons-material/Restore";
Expand All @@ -10,9 +10,9 @@ import PublishIcon from "@mui/icons-material/Publish";
import FileDownloadIcon from "@mui/icons-material/FileDownload";
import QuickSearchToolbar from "../QuickSearchToolbar/QuickSearchToolbar";
import jsYaml from "js-yaml";
import JSZip from "jszip";
import { saveAs } from "file-saver";
import ExportRules from "./ExportRules";
import ExportRulesCSV from "./ExportRulesCSV";
import ExportArtifacts from "./ExportArtifacts";
import ExportRulesYAML from "./ExportRulesYAML";

export default function Controls() {
const [discardDialog, setDiscardDialog] = useState<boolean>(false);
Expand All @@ -31,12 +31,6 @@ export default function Controls() {
isRuleDirty,
setAlertState,
isRuleModifiable,
syntaxCheck,
schemaCheck,
jsonCheck,
loadDefineXMLCheck,
loadDatasetsCheck,
testCheck,
} = useContext(AppContext);

const newRule = () => {
Expand Down Expand Up @@ -97,39 +91,15 @@ export default function Controls() {
}
};

const exportArtifacts = async () => {
const zip = new JSZip();
zip.file("Rule.yml", modifiedRule);
zip.file(
"Rule_spaces.json",
JSON.stringify(syntaxCheck.details[0].details, null, 4)
);
zip.file(
"Schema_Validation.json",
JSON.stringify(schemaCheck.details[0].details, null, 4)
);
zip.file(
"Rule_underscores.json",
JSON.stringify(jsonCheck.details[0].details, null, 4)
);
zip.file("Define.xml", loadDefineXMLCheck.details[1]?.details ?? "");
zip.file("Datasets.xlsx", loadDatasetsCheck.details[0]?.details ?? "");
zip.file(
"Datasets.json",
JSON.stringify(loadDatasetsCheck.details[1]?.details ?? "", null, 4)
);
zip.file(
"Request.json",
JSON.stringify(testCheck.details[1]?.details ?? "", null, 4)
);
zip.file(
"Results.json",
JSON.stringify(testCheck.details[3]?.details ?? "", null, 4)
);

zip.generateAsync({ type: "blob" }).then((content) => {
saveAs(content, "Rule.zip");
});
const [exportAnchorEl, setExportAnchorEl] = useState<null | HTMLElement>(
null
);
const open = Boolean(exportAnchorEl);
const handleExport = (event: MouseEvent<HTMLButtonElement>) => {
setExportAnchorEl(event.currentTarget);
};
const handleExportClose = () => {
setExportAnchorEl(null);
};

return (
Expand Down Expand Up @@ -205,19 +175,38 @@ export default function Controls() {
</span>
</Tooltip>

<Tooltip title={"Export Artifacts"}>
<Tooltip id="export-button" title={"Export..."}>
<span>
<IconButton onClick={exportArtifacts} color="primary">
<IconButton onClick={handleExport} color="primary">
<FileDownloadIcon />
</IconButton>
</span>
</Tooltip>

<ExportRules />
<Menu
id="export-menu"
anchorEl={exportAnchorEl}
open={open}
onClose={handleExportClose}
MenuListProps={{
"aria-labelledby": "export-button",
}}
>
<ExportArtifacts onClose={handleExportClose} />
<ExportRulesCSV onClose={handleExportClose} />
<ExportRulesYAML onClose={handleExportClose} />
</Menu>

<QuickSearchToolbar label="Search YAML..." queryParam={"content"} />
</Toolbar>

{/* TODO:
Get rid of or use ControlButton
Implement YAML
Add closing to csv and yaml
Better naming for these state vars
Move export artifacts to own file
*/}

<PromptDialog
contentText="Discard changes?"
open={discardDialog}
Expand Down
60 changes: 60 additions & 0 deletions src/components/Controls/ExportArtifacts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useContext,} from "react";
import AppContext from "../AppContext";
import JSZip from "jszip";
import { saveAs } from "file-saver";
import { MenuItem } from "@mui/material";

export default function ExportArtifacts({
onClose
}) {
const {
modifiedRule,
syntaxCheck,
schemaCheck,
jsonCheck,
loadDefineXMLCheck,
loadDatasetsCheck,
testCheck,
} = useContext(AppContext);

const exportArtifacts = async () => {
const zip = new JSZip();
zip.file("Rule.yml", modifiedRule);
zip.file(
"Rule_spaces.json",
JSON.stringify(syntaxCheck.details[0].details, null, 4)
);
zip.file(
"Schema_Validation.json",
JSON.stringify(schemaCheck.details[0].details, null, 4)
);
zip.file(
"Rule_underscores.json",
JSON.stringify(jsonCheck.details[0].details, null, 4)
);
zip.file("Define.xml", loadDefineXMLCheck.details[1]?.details ?? "");
zip.file("Datasets.xlsx", loadDatasetsCheck.details[0]?.details ?? "");
zip.file(
"Datasets.json",
JSON.stringify(loadDatasetsCheck.details[1]?.details ?? "", null, 4)
);
zip.file(
"Request.json",
JSON.stringify(testCheck.details[1]?.details ?? "", null, 4)
);
zip.file(
"Results.json",
JSON.stringify(testCheck.details[3]?.details ?? "", null, 4)
);

zip.generateAsync({ type: "blob" }).then((content) => {
saveAs(content, "Rule.zip");
});

onClose();
};
return (
<MenuItem onClick={exportArtifacts}>Export debugging artifacts</MenuItem>
);

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import ControlButton from "./ControlButton";
import ChecklistIcon from "@mui/icons-material/Checklist";
import AppContext from "../AppContext";
import { useContext } from "react";
import { IFilter } from "../../types/IFilter";
import { saveAs } from "file-saver";
import { ColumnOption, stringify } from "csv-stringify/browser/esm/sync";
import { MenuItem } from "@mui/material";

const columns: ColumnOption[] = [
{
Expand Down Expand Up @@ -50,7 +49,9 @@ const cast = {
Array.isArray(value) ? value.join(", ") : JSON.stringify(value),
};

export default function ExportRules() {
export default function ExportRulesCSV({
onClose
}) {
const { dataService, orderBy, order, searchText, setAlertState } = useContext(
AppContext
);
Expand Down Expand Up @@ -112,11 +113,10 @@ export default function ExportRules() {
message: `Saved ${rules.length} Rules`,
severity: "success",
});
onClose();
};

return (
<ControlButton title={"Export Rules (as filtered)"} onClick={exportRules}>
<ChecklistIcon />
</ControlButton>
<MenuItem onClick={exportRules}>Export rules CSV (as filtered)</MenuItem>
);
}
93 changes: 93 additions & 0 deletions src/components/Controls/ExportRulesYAML.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import AppContext from "../AppContext";
import { useContext } from "react";
import { IFilter } from "../../types/IFilter";
import { saveAs } from "file-saver";
import { ColumnOption } from "csv-stringify/browser/esm/sync";
import { MenuItem } from "@mui/material";
import JSZip from "jszip";

const columns: ColumnOption[] = [
{
header: "id",
key: "id",
},
{
header: "ruleId",
key: "json.Authorities.Standards.References.Rule Identifier.Id",
},
{
header: "content",
key: "content",
},
];

const cast = (value) =>
Array.isArray(value) ? value.join(",") : JSON.stringify(value);

export default function ExportRulesYAML({ onClose }) {
const { dataService, orderBy, order, searchText, setAlertState } = useContext(
AppContext
);

const exportRules = async () => {
const params = {
orderBy: orderBy,
order: order,
select: columns.map((column) => column.key),
filters: Object.entries(searchText)
.filter(
([_, filterValue]: [string, string]) =>
!(filterValue == null || filterValue === "")
)
.map(
([filterName, filterValue]: [string, string]): IFilter => ({
name: filterName,
operator: "contains",
value: filterValue,
})
),
};
const rules = [];
setAlertState({
message: `Downloading Rules ${rules.length}/?...`,
severity: "info",
});
var currentRules = await dataService.get_rules_filter_sort(params);
rules.push(...currentRules.rules);
while (currentRules.next) {
setAlertState({
message: `Downloading Rules ${rules.length}/?...`,
severity: "info",
});
currentRules = await dataService.get_rules_filter_sort(currentRules.next);
rules.push(...currentRules.rules);
}
setAlertState({
message: `Saving ${rules.length} Rules...`,
severity: "info",
});

const zip = new JSZip();
for (const rule of rules) {
zip.file(
`${cast(
rule["json.Authorities.Standards.References.Rule Identifier.Id"]
)}.${rule["id"]}.yml`,
rule["content"]
);
}
zip.generateAsync({ type: "blob" }).then((content) => {
saveAs(content, "Rules.zip");
});

setAlertState({
message: `Saved ${rules.length} Rules`,
severity: "success",
});
onClose();
};

return (
<MenuItem onClick={exportRules}>Export rules YAML (as filtered)</MenuItem>
);
}

0 comments on commit ded6316

Please sign in to comment.