Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/components/Controls/ExportRulesYAML.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { saveAs } from "file-saver";
import { ColumnOption } from "csv-stringify/browser/esm/sync";
import { MenuItem } from "@mui/material";
import JSZip from "jszip";
import { ruleFileName } from "../../utils/ruleFileName";

const columns: ColumnOption[] = [
{
Expand All @@ -21,9 +22,6 @@ const columns: ColumnOption[] = [
},
];

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

export default function ExportRulesYAML({ onClose }) {
const { dataService, orderBy, order, searchText, setAlertState } = useContext(
AppContext
Expand Down Expand Up @@ -70,9 +68,10 @@ export default function ExportRulesYAML({ onClose }) {
const zip = new JSZip();
for (const rule of rules) {
zip.file(
`${cast(
rule["json.Authorities.Standards.References.Rule Identifier.Id"]
)}.${rule["id"]}.yml`,
ruleFileName(
rule["json.Authorities.Standards.References.Rule Identifier.Id"],
rule["id"]
),
rule["content"]
);
}
Expand Down
26 changes: 26 additions & 0 deletions src/utils/ruleFileName.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MISSING_RULE_ID, ruleFileName } from "./ruleFileName";

describe("ruleFileName", () => {
const id = "3f2a9c1e-7b4d-4c1a-9e2f-0a1b2c3d4e5f";

it("uses the rule identifier as the file name prefix", () => {
expect(ruleFileName(["CORE-000001"], id)).toBe(`CORE-000001.${id}.yml`);
});

it("joins multiple rule identifiers with a comma", () => {
expect(ruleFileName(["CORE-000001", "CORE-000002"], id)).toBe(
`CORE-000001,CORE-000002.${id}.yml`
);
});

it.each([
["an empty array", []],
["an array of empty values", [""]],
["undefined", undefined],
])("uses a placeholder when the rule identifier is %s", (_, ruleId) => {
const fileName = ruleFileName(ruleId, id);

expect(fileName).toBe(`${MISSING_RULE_ID}.${id}.yml`);
expect(fileName.startsWith(".")).toBe(false);
});
});
16 changes: 16 additions & 0 deletions src/utils/ruleFileName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Placeholder used when a rule has no Rule Identifier.
*
* Without it, the exported file name would start with "." (e.g. ".<uuid>.yml"),
* which macOS treats as a hidden file.
*/
export const MISSING_RULE_ID = "NO_RULE_ID";

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

/**
* Builds the file name for a rule exported as YAML: `<RuleID>.<id>.yml`.
*/
export const ruleFileName = (ruleId: unknown, id: string): string =>
`${toFileNamePart(ruleId) || MISSING_RULE_ID}.${id}.yml`;