blob: 91086e16fbae5427bb679e353d6e3df96f3956b8 [file] [log] [blame]
import { IconType } from "react-icons";
import { FaProjectDiagram } from "react-icons/fa";
import { AiOutlineFunction, AiOutlineNodeIndex } from "react-icons/ai";
import { resolveNav } from "../nav";
import {
CodeArtifact,
DAGTemplateWithData,
NodeTemplate,
Project,
} from "../../../state/api/friendlyApi";
import { CatalogResponse } from "../../../state/api/backendApiRaw";
// SearchableCategory is a category of items that can be searched for
export type SearchableCategory = {
name: string; // Name of the category
shortcut: string; // Shortcut within search dialogue for the category
displayAll: boolean;
};
export type SearchableItem = {
name: string;
href: string; // TODO -- determine what else we need to render this in the react context
searchableText: string[];
category: SearchableCategory;
icon: IconType; // Icon to display next to the category
};
// Currently displayAll is not used, but
// it should be used to determine whether to display all items on default or
// require the user to type something to search for items
// TODO -- use displayAll
const SEARCHABLE_CATEGORIES = {
project: {
name: "projects",
// icon: (item: SearchableItem) => FaProjectDiagram,
shortcut: ">",
displayAll: true,
},
node: {
name: "nodes",
// icon: AiOutlineNodeIndex,
shortcut: ".",
displayAll: false,
},
fn: {
name: "functions",
// icon: AiOutlineFunction,
shortcut: ":",
displayAll: false,
},
page: {
name: "navigation",
// icon: IoNavigateOutline,
shortcut: "/",
displayAll: false,
},
};
const extractProjectSearchables = (projects: Project[]): SearchableItem[] => {
return projects.map((project) => {
return {
name: project.name,
href: `/dashboard/project/${project.id}`, // TODO -- figure out where to put this -- probably to the containing function? Or the catalog location?
searchableText: [project.name, project.description],
category: SEARCHABLE_CATEGORIES.project,
icon: FaProjectDiagram,
};
});
};
const extractNodeSearchables = (
nodeTemplates: NodeTemplate[],
projectId: number
): SearchableItem[] => {
// Group by node template name -- this allows us to get each implementation then filter through them to get the comparison for every one we encounter...
const nodeTemplatesGroupedByName = nodeTemplates.reduce(
(acc, nodeTemplate) => {
const { name } = nodeTemplate;
if (acc.get(name) === undefined) {
acc.set(name, []);
}
acc.get(name)?.push(nodeTemplate);
return acc;
},
new Map<string, NodeTemplate[]>()
);
return Array.from(nodeTemplatesGroupedByName.entries()).map(
([nodeTemplateName, nodeTemplates]) => {
const projectVersionIdsJoined = nodeTemplates
.map((i) => i.dag_template)
.sort()
.join(",");
return {
name: nodeTemplateName,
href: `/dashboard/project/${projectId}/version/${projectVersionIdsJoined}/visualize?focus=${JSON.stringify(
{ node: nodeTemplateName }
)}`,
searchableText: [nodeTemplateName],
category: SEARCHABLE_CATEGORIES.node,
icon: AiOutlineNodeIndex,
};
}
);
};
const extractCodeSearchables = (
codeArtifacts: CodeArtifact[],
projectId: number
): SearchableItem[] => {
// Group by node template name -- this allows us to get each implementation then filter through them to get the comparison for every one we encounter...
const codeArtifactsGroupedByName = codeArtifacts.reduce(
(acc, codeArtifact) => {
const { name } = codeArtifact;
if (acc.get(name) === undefined) {
acc.set(name, []);
}
acc.get(name)?.push(codeArtifact);
return acc;
},
new Map<string, CodeArtifact[]>()
);
const out = Array.from(codeArtifactsGroupedByName.entries()).map(
([codeArtifactName, codeArtifacts]) => {
const projectVersionIdsJoined = codeArtifacts
.map((artifact) => artifact.dag_template)
.sort()
.join(",");
return {
name: codeArtifactName,
href: `/dashboard/project/${projectId}/version/${projectVersionIdsJoined}/visualize?focus=${JSON.stringify(
{ function: codeArtifactName }
)}`,
searchableText: [codeArtifactName],
category: SEARCHABLE_CATEGORIES.fn,
icon: AiOutlineFunction,
};
}
);
return out;
};
const getDefaultSearchables = (
projectId: number | undefined,
projectVersion: DAGTemplateWithData[] | undefined
) => {
return resolveNav(projectId?.toString(), projectVersion).map(
({ name, href, icon }) => {
return {
name,
href,
searchableText: [name],
category: SEARCHABLE_CATEGORIES.page,
icon: icon,
};
}
);
};
export const extractSearchables = (
projectId: number | undefined,
allProjects: Project[] | undefined,
currentProjectVersions: DAGTemplateWithData[] | undefined,
catalogData: CatalogResponse | undefined
): SearchableItem[] => {
/**
* Parsing node templates -- we want to give priority to anything from the current project versions (dictated by the URL).
* That way, if we go to those, we get the full comparison.
*
* Then, we can add in the rest of the node templates from the catalog if we haven't seen them -- this allows us to search.
* The catalog query is also not super fast, so this enables us to search without having to wait for the catalog query to finish,
* although its likely we'll do that eagerly anyway
*/
const nodeTemplatesInProjectVersions = new Set<string>();
currentProjectVersions?.forEach((projectVersion) => {
projectVersion.nodes.forEach((nodeTemplate) => {
nodeTemplatesInProjectVersions.add(nodeTemplate.name);
});
});
const allNodeTemplates = [
// First add in all the ones that are part of the current project version
...(currentProjectVersions?.flatMap((i) => i.nodes) || []),
// Then add in all the ones that aren't
...(catalogData?.nodes.filter(
(item) => !nodeTemplatesInProjectVersions.has(item.name)
) || []),
];
/**
* Parsing code artifacts -- we want to basically do the same as the above
* If its in the curret project version, we want to use that one.
* Otherwise, we want to use the one from the catalog.
*/
const codeArtifactsInProjectVersions = new Set<string>();
currentProjectVersions?.forEach((projectVersion) => {
projectVersion.code_artifacts.forEach((codeArtifact) => {
codeArtifactsInProjectVersions.add(codeArtifact.name);
});
});
const allCodeArtifacts = [
...(currentProjectVersions?.flatMap((i) => i.code_artifacts) || []),
...(catalogData?.code_artifacts.filter(
(item) => !codeArtifactsInProjectVersions.has(item.name)
) || []),
];
return [
...extractProjectSearchables(allProjects || []),
...(projectId !== undefined
? extractNodeSearchables(allNodeTemplates, projectId)
: []),
...(projectId !== undefined
? extractCodeSearchables(allCodeArtifacts, projectId)
: []),
...(projectId !== undefined
? getDefaultSearchables(projectId, currentProjectVersions)
: []),
];
};
export const getSearchableCategories = () => {
return Object.values(SEARCHABLE_CATEGORIES);
};