Skip to content

Commit 31accf1

Browse files
authored
Merge pull request #45 from Exabyte-io/chore/SOF-6463
Chore/sof 6463
2 parents a48d84f + 53d1c83 commit 31accf1

File tree

4 files changed

+167
-1
lines changed

4 files changed

+167
-1
lines changed

src/utils/file.js

+30
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import fs from "fs";
2+
import path from "path";
3+
14
const FILE_EXTENSION_TO_PROGRAMMING_LANGUAGE_MAP = {
25
in: "fortran",
36
sh: "shell",
@@ -28,3 +31,30 @@ export function formatFileSize(size, decimals = 2) {
2831
const units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
2932
return parseFloat((size / 1024 ** index).toFixed(decimals)) + " " + units[index];
3033
}
34+
35+
/** Get list of paths for files in a directory and filter by file extensions if provided.
36+
* @param {string} dirPath - Path to current directory, i.e. $PWD
37+
* @param {string[]} fileExtensions - File extensions to filter, e.g. `.yml`
38+
* @param {boolean} resolvePath - whether to resolve the paths of files
39+
* @returns {string[]} - Array of file paths
40+
*/
41+
export function getFilesInDirectory(dirPath, fileExtensions = [], resolvePath = true) {
42+
let fileNames = fs.readdirSync(dirPath);
43+
if (fileExtensions.length) {
44+
fileNames = fileNames.filter((dirItem) => fileExtensions.includes(path.extname(dirItem)));
45+
}
46+
if (resolvePath) return fileNames.map((fileName) => path.resolve(dirPath, fileName));
47+
return fileNames;
48+
}
49+
50+
/**
51+
* Get list of directories contained in current directory.
52+
* @param {string} currentPath - current directory
53+
* @return {*}
54+
*/
55+
export function getDirectories(currentPath) {
56+
return fs
57+
.readdirSync(currentPath, { withFileTypes: true })
58+
.filter((dirent) => dirent.isDirectory())
59+
.map((dirent) => dirent.name);
60+
}

src/utils/filter.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import lodash from "lodash";
2+
3+
/**
4+
* Check if one path matches regular expression or exact string.
5+
* @param {{path: string}} pathObject - Entity or object with path property
6+
* @param {Array<{path: string}|{regex: RegExp}>} filterObjects - Filter conditions
7+
* @return {boolean}
8+
*/
9+
function isPathSupported(pathObject, filterObjects) {
10+
return filterObjects.some((filterObj) => {
11+
if (filterObj.path) {
12+
return filterObj.path === pathObject.path;
13+
}
14+
if (filterObj.regex) {
15+
return filterObj.regex.test(pathObject.path);
16+
}
17+
return false;
18+
});
19+
}
20+
21+
/**
22+
* Check if _all_ paths in concatenated path match filtering conditions.
23+
* @param {{path: string}} pathObject - Path object with concatenated path (multipath)
24+
* @param {string} multiPathSeparator - String sequence used for concatenation of paths
25+
* @param {Array<{path: string}|{regex: RegExp}>} filterObjects - Filter conditions
26+
* @return {boolean}
27+
*/
28+
function isMultiPathSupported(pathObject, multiPathSeparator, filterObjects) {
29+
const expandedPaths = pathObject.path.split(multiPathSeparator).map((p) => ({ path: p }));
30+
return expandedPaths.every((expandedPath) => isPathSupported(expandedPath, filterObjects));
31+
}
32+
33+
/**
34+
* Filter list of entity paths or entities by paths and regular expressions.
35+
* @param {Object[]} entitiesOrPaths - Array of objects defining entity path
36+
* @param {Array<{ path: string }|{ regex: string }|{ regex: RegExp }>} filterObjects - Array of path or regular expression objects
37+
* @param {string} multiPathSeparator - string by which paths should be split
38+
* @return {Object[]} - filtered entity path objects or entities
39+
*/
40+
export function filterEntityList({ entitiesOrPaths, filterObjects = [], multiPathSeparator = "" }) {
41+
const filterObjects_ = filterObjects.map((o) => (o.regex ? { regex: new RegExp(o.regex) } : o));
42+
43+
let filtered;
44+
if (multiPathSeparator) {
45+
filtered = entitiesOrPaths.filter((e) =>
46+
isMultiPathSupported(e, multiPathSeparator, filterObjects_),
47+
);
48+
} else {
49+
filtered = entitiesOrPaths.filter((e) => isPathSupported(e, filterObjects_));
50+
}
51+
52+
return lodash.uniqBy(filtered, "path");
53+
}

src/utils/index.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import { convertToCompactCSVArrayOfObjects, safeMakeArray } from "./array";
33
import { cloneClass, extendClass, extendClassStaticProps, extendThis } from "./class";
44
import { deepClone } from "./clone";
55
import { refreshCodeMirror } from "./codemirror";
6-
import { formatFileSize, getProgrammingLanguageFromFileExtension } from "./file";
6+
import {
7+
formatFileSize,
8+
getDirectories,
9+
getFilesInDirectory,
10+
getProgrammingLanguageFromFileExtension,
11+
} from "./file";
12+
import { filterEntityList } from "./filter";
713
import { addUnit, removeUnit, replaceUnit, setNextLinks, setUnitsHead } from "./graph";
814
import {
915
calculateHashFromObject,
@@ -78,4 +84,7 @@ export {
7884
JsYamlTypes,
7985
JsYamlAllSchemas,
8086
renderTextWithSubstitutes,
87+
filterEntityList,
88+
getFilesInDirectory,
89+
getDirectories,
8190
};

tests/utils/filter.tests.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { expect } from "chai";
2+
3+
import { filterEntityList } from "../../src/utils/filter";
4+
5+
describe("entity filter", () => {
6+
const entities = [
7+
{ name: "A", path: "/root/entity/a" },
8+
{ name: "B", path: "/root/entity/b" },
9+
{ name: "C", path: "/root/entity/c" },
10+
{ name: "D", path: "/root/entity/d" },
11+
];
12+
13+
it("should filter an entity list with paths", () => {
14+
const filterObjects = [{ path: "/root/entity/b" }, { path: "/root/entity/c" }];
15+
const filtered = filterEntityList({ filterObjects, entitiesOrPaths: entities });
16+
const expected = [
17+
{ name: "B", path: "/root/entity/b" },
18+
{ name: "C", path: "/root/entity/c" },
19+
];
20+
expect(filtered).to.have.deep.members(expected);
21+
});
22+
23+
it("should filter an entity list with regular expressions", () => {
24+
const filterObjects = [{ regex: /\/root\/entity\/[bc]/ }];
25+
const filtered = filterEntityList({ filterObjects, entitiesOrPaths: entities });
26+
const expected = [
27+
{ name: "B", path: "/root/entity/b" },
28+
{ name: "C", path: "/root/entity/c" },
29+
];
30+
expect(filtered).to.have.deep.members(expected);
31+
});
32+
33+
it("should filter an entity list with both paths and regular expressions", () => {
34+
const filterObjects = [{ path: "/root/entity/b" }, { regex: /\/root\/entity\/[c]/ }];
35+
const filtered = filterEntityList({ filterObjects, entitiesOrPaths: entities });
36+
const expected = [
37+
{ name: "B", path: "/root/entity/b" },
38+
{ name: "C", path: "/root/entity/c" },
39+
];
40+
expect(filtered).to.have.deep.members(expected);
41+
});
42+
43+
it("should filter an entity list containing concatenated paths", () => {
44+
const filterObjects = [{ path: "/root/entity/b" }, { path: "/root/entity/c" }];
45+
const multiPathEntities = [
46+
{ name: "AB", path: "/root/entity/a::/root/entity/b" },
47+
{ name: "BC", path: "/root/entity/b::/root/entity/c" },
48+
];
49+
const multiPathSeparator = "::";
50+
const filtered = filterEntityList({
51+
filterObjects,
52+
entitiesOrPaths: multiPathEntities,
53+
multiPathSeparator,
54+
});
55+
const expected = [{ name: "BC", path: "/root/entity/b::/root/entity/c" }];
56+
expect(filtered).to.have.deep.members(expected);
57+
});
58+
59+
it("should filter an entity list containing concatenated paths using regex", () => {
60+
const filterObjects = [{ path: "/root/entity/b" }, { regex: /\/root\/entity\/[c]/ }];
61+
const multiPathEntities = [
62+
{ name: "AB", path: "/root/entity/a::/root/entity/b" },
63+
{ name: "BC", path: "/root/entity/b::/root/entity/c" },
64+
];
65+
const multiPathSeparator = "::";
66+
const filtered = filterEntityList({
67+
filterObjects,
68+
entitiesOrPaths: multiPathEntities,
69+
multiPathSeparator,
70+
});
71+
const expected = [{ name: "BC", path: "/root/entity/b::/root/entity/c" }];
72+
expect(filtered).to.have.deep.members(expected);
73+
});
74+
});

0 commit comments

Comments
 (0)