Skip to content

Commit e3ceca9

Browse files
Add module-aware template lookup utility
1 parent 368002b commit e3ceca9

8 files changed

Lines changed: 80 additions & 3 deletions

File tree

src/frontend/src/CustomNodes/helpers/check-code-validity.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { componentsToIgnoreUpdate } from "@/constants/constants";
22
import type { OutputFieldType } from "@/types/api";
3+
import { resolveTemplateEntry } from "@/utils/component-template-utils";
34
import type { NodeDataType } from "../../types/flow";
45

56
// Returns true if the code is outdated (code string changed and not ignored)
@@ -53,12 +54,19 @@ const codeHasBreakingChange = (
5354
export const checkCodeValidity = (
5455
data: NodeDataType,
5556
templates: { [key: string]: any },
57+
templatesByModule?: { [key: string]: any },
5658
) => {
5759
if (!data?.node || !templates) return;
58-
const template = templates[data.type]?.template;
60+
const templateEntry = resolveTemplateEntry(
61+
data.node,
62+
templates,
63+
templatesByModule,
64+
data.type,
65+
);
66+
const template = templateEntry?.template;
5967
const currentCode = template?.code?.value;
6068
const thisNodesCode = data.node!.template?.code?.value;
61-
const originalOutputs = templates[data.type]?.outputs;
69+
const originalOutputs = templateEntry?.outputs;
6270
const userOutputs = data.node?.outputs;
6371
const originalTemplate = template;
6472
const userTemplate = data.node?.template;

src/frontend/src/stores/__tests__/flowStore.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ jest.mock("../typesStore", () => ({
8383
useTypesStore: {
8484
getState: () => ({
8585
templates: {},
86+
templatesByModule: {},
8687
types: {},
8788
}),
8889
},

src/frontend/src/stores/__tests__/typesStore.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@ jest.mock("../../utils/reactflowUtils", () => ({
2323
}),
2424
}));
2525

26+
jest.mock("../../utils/component-template-utils", () => ({
27+
buildTemplatesByModule: jest.fn(() => ({})),
28+
}));
29+
2630
// Mock imports
2731
const mockExtractSecretFieldsFromComponents =
2832
require("../../utils/reactflowUtils").extractSecretFieldsFromComponents;
2933
const mockTemplatesGenerator =
3034
require("../../utils/reactflowUtils").templatesGenerator;
35+
const mockBuildTemplatesByModule =
36+
require("../../utils/component-template-utils").buildTemplatesByModule;
3137
const mockTypesGenerator = require("../../utils/reactflowUtils").typesGenerator;
3238

3339
const mockAPIData: APIDataType = {
@@ -85,13 +91,15 @@ describe("useTypesStore", () => {
8591
return new Set(Object.keys(data));
8692
});
8793
mockTemplatesGenerator.mockReturnValue({});
94+
mockBuildTemplatesByModule.mockReturnValue({});
8895
mockTypesGenerator.mockReturnValue({});
8996

9097
act(() => {
9198
useTypesStore.setState({
9299
ComponentFields: new Set(),
93100
types: {},
94101
templates: {},
102+
templatesByModule: {},
95103
data: {},
96104
});
97105
});
@@ -104,6 +112,7 @@ describe("useTypesStore", () => {
104112
expect(result.current.ComponentFields).toEqual(new Set());
105113
expect(result.current.types).toEqual({});
106114
expect(result.current.templates).toEqual({});
115+
expect(result.current.templatesByModule).toEqual({});
107116
expect(result.current.data).toEqual({});
108117
});
109118
});
@@ -209,6 +218,9 @@ describe("useTypesStore", () => {
209218
it("should set types, templates, data, and component fields", () => {
210219
mockTypesGenerator.mockReturnValue(mockTypes);
211220
mockTemplatesGenerator.mockReturnValue(mockTemplates);
221+
mockBuildTemplatesByModule.mockReturnValue({
222+
"module.text": mockTemplates.TextInput,
223+
});
212224
mockExtractSecretFieldsFromComponents.mockReturnValue(
213225
new Set(["TextInput", "NumberInput"]),
214226
);
@@ -221,12 +233,16 @@ describe("useTypesStore", () => {
221233

222234
expect(mockTypesGenerator).toHaveBeenCalledWith(mockAPIData);
223235
expect(mockTemplatesGenerator).toHaveBeenCalledWith(mockAPIData);
236+
expect(mockBuildTemplatesByModule).toHaveBeenCalledWith(mockAPIData);
224237
expect(mockExtractSecretFieldsFromComponents).toHaveBeenCalledWith(
225238
mockAPIData,
226239
);
227240

228241
expect(result.current.types).toEqual(mockTypes);
229242
expect(result.current.templates).toEqual(mockTemplates);
243+
expect(result.current.templatesByModule).toEqual({
244+
"module.text": mockTemplates.TextInput,
245+
});
230246
expect(result.current.data).toEqual(mockAPIData);
231247
expect(result.current.ComponentFields).toEqual(
232248
new Set(["TextInput", "NumberInput"]),

src/frontend/src/stores/flowStore.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,14 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
9191
updateComponentsToUpdate: (nodes) => {
9292
const outdatedNodes: ComponentsToUpdateType[] = [];
9393
const templates = useTypesStore.getState().templates;
94+
const templatesByModule = useTypesStore.getState().templatesByModule;
9495
nodes.forEach((node) => {
9596
if (node.type === "genericNode") {
96-
const codeValidity = checkCodeValidity(node.data, templates);
97+
const codeValidity = checkCodeValidity(
98+
node.data,
99+
templates,
100+
templatesByModule,
101+
);
97102
if (codeValidity && codeValidity.outdated)
98103
outdatedNodes.push({
99104
id: node.id,

src/frontend/src/stores/typesStore.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
templatesGenerator,
77
typesGenerator,
88
} from "../utils/reactflowUtils";
9+
import { buildTemplatesByModule } from "../utils/component-template-utils";
910

1011
export const useTypesStore = create<TypesStoreType>((set, get) => ({
1112
ComponentFields: new Set(),
@@ -17,6 +18,7 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
1718
},
1819
types: {},
1920
templates: {},
21+
templatesByModule: {},
2022
data: {},
2123
setTypes: (data: APIDataType) => {
2224
set((old) => ({
@@ -27,6 +29,7 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
2729
...data,
2830
}),
2931
templates: templatesGenerator(data),
32+
templatesByModule: buildTemplatesByModule(data),
3033
}));
3134
},
3235
setTemplates: (newState: {}) => {

src/frontend/src/types/zustand/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type TypesStoreType = {
44
types: { [char: string]: string };
55
setTypes: (newState: {}) => void;
66
templates: { [char: string]: APIClassType };
7+
templatesByModule: { [module: string]: APIClassType };
78
setTemplates: (newState: {}) => void;
89
data: APIDataType;
910
setData: (newState: {}) => void;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { APIClassType, APIKindType, APIObjectType } from "@/types/api";
2+
3+
export function buildTemplatesByModule(
4+
data: APIObjectType,
5+
): Record<string, APIClassType> {
6+
return Object.keys(data).reduce((acc, curr) => {
7+
Object.values(data[curr]).forEach((component: APIKindType[keyof APIKindType]) => {
8+
const moduleName = component?.metadata?.module;
9+
if (moduleName && !acc[moduleName]) {
10+
acc[moduleName] = component as APIClassType;
11+
}
12+
});
13+
return acc;
14+
}, {} as Record<string, APIClassType>);
15+
}
16+
17+
export function resolveTemplateEntry(
18+
node: { metadata?: { module?: string } } | undefined,
19+
templatesByName: Record<string, APIClassType>,
20+
templatesByModule?: Record<string, APIClassType>,
21+
typeKey?: string,
22+
): APIClassType | undefined {
23+
const moduleName = node?.metadata?.module;
24+
if (moduleName && templatesByModule?.[moduleName]) {
25+
return templatesByModule[moduleName];
26+
}
27+
if (typeKey) {
28+
return templatesByName[typeKey];
29+
}
30+
return undefined;
31+
}

src/frontend/src/utils/reactflowUtils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,18 @@ export function templatesGenerator(data: APIObjectType) {
18381838
}, {});
18391839
}
18401840

1841+
export function templatesByModuleGenerator(data: APIObjectType) {
1842+
return Object.keys(data).reduce((acc, curr) => {
1843+
Object.values(data[curr]).forEach((component) => {
1844+
const moduleName = component?.metadata?.module;
1845+
if (moduleName && !acc[moduleName]) {
1846+
acc[moduleName] = component;
1847+
}
1848+
});
1849+
return acc;
1850+
}, {} as Record<string, APIClassType>);
1851+
}
1852+
18411853
/**
18421854
* Determines if a field is a SecretStr field type
18431855
*/

0 commit comments

Comments
 (0)