Skip to content

Commit b0a2fec

Browse files
authored
Added support for template schema (#5812)
- load template schema in frontend UI - display template schema in schema visualizer - updated types
1 parent 058e0c4 commit b0a2fec

File tree

19 files changed

+462
-181
lines changed

19 files changed

+462
-181
lines changed

frontend/app/src/entities/nodes/hooks/useObjectItems.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,12 @@ import {
55
getObjectRelationships,
66
} from "@/entities/nodes/object-items/getSchemaObjectColumns";
77
import { getPermission } from "@/entities/permission/utils";
8-
import {
9-
genericSchemasAtom,
10-
nodeSchemasAtom,
11-
profileSchemasAtom,
12-
} from "@/entities/schema/stores/schema.atom";
8+
import { getSchema } from "@/entities/schema/domain/get-schema";
139
import { ModelSchema } from "@/entities/schema/types";
1410
import { getTokens } from "@/entities/user-profile/api/getTokens";
1511
import useQuery from "@/shared/api/graphql/useQuery";
1612
import { Filter } from "@/shared/hooks/useFilters";
1713
import { gql } from "@apollo/client";
18-
import { useAtomValue } from "jotai";
1914

2015
const getQuery = (schema?: ModelSchema, filters?: Array<Filter>) => {
2116
if (!schema) return "query {ok}";
@@ -24,15 +19,8 @@ const getQuery = (schema?: ModelSchema, filters?: Array<Filter>) => {
2419
return getTokens;
2520
}
2621

27-
const nodes = useAtomValue(nodeSchemasAtom);
28-
const generics = useAtomValue(genericSchemasAtom);
29-
const profiles = useAtomValue(profileSchemasAtom);
30-
3122
const kindFilter = filters?.find((filter) => filter.name === "kind__value");
32-
33-
const kindFilterSchema = [...nodes, ...generics, ...profiles].find(
34-
({ kind }) => kind === kindFilter?.value
35-
);
23+
const { schema: kindFilterSchema } = getSchema(kindFilter?.value);
3624

3725
// All the filter values are being sent out as strings inside quotes.
3826
// This will not work if the type of filter value is not string.

frontend/app/src/entities/nodes/object-items/getSchemaObjectColumns.ts

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import {
55
relationshipsForTabs,
66
} from "@/config/constants";
77
import { ATTRIBUTE_KINDS_FOR_LIST_VIEW } from "@/entities/schema/constants";
8-
import { profileSchemasAtom } from "@/entities/schema/stores/schema.atom";
98
import { AttributeKind, GenericSchema, ModelSchema, NodeSchema } from "@/entities/schema/types";
109
import { isGenericSchema } from "@/entities/schema/utils/is-generic-schema";
11-
import { store } from "@/shared/stores";
1210
import { sortByOrderWeight } from "@/shared/utils/common";
1311
import * as R from "ramda";
1412

@@ -142,84 +140,3 @@ export const getObjectTabs = (tabs: any[], data: any) => {
142140
count: data[tab.name]?.count,
143141
}));
144142
};
145-
146-
// Include current value in the options to make it available in the select component
147-
export const getRelationshipOptions = (row: any, field: any, schemas: any[], generics: any[]) => {
148-
const value = row && (row[field.name]?.node ?? row[field.name]);
149-
150-
if (value?.edges) {
151-
return value.edges.map((edge: any) => ({
152-
name: edge.node.display_label,
153-
id: edge.node.id,
154-
}));
155-
}
156-
157-
const generic = generics.find((generic: any) => generic.kind === field.peer);
158-
159-
if (generic) {
160-
const options = (generic.used_by || []).map((name: string) => {
161-
const profiles = store.get(profileSchemasAtom);
162-
163-
const relatedSchema = [...schemas, ...profiles].find((s: any) => s.kind === name);
164-
165-
if (relatedSchema) {
166-
return {
167-
id: name,
168-
name: relatedSchema.name,
169-
};
170-
}
171-
});
172-
173-
return options;
174-
}
175-
176-
if (!value) {
177-
return [];
178-
}
179-
180-
const option = {
181-
name: value.display_label,
182-
id: value.id,
183-
};
184-
185-
// Initial option for relationships to make the current value available
186-
return [option];
187-
};
188-
189-
type tgetOptionsFromRelationship = {
190-
options: any[];
191-
schemas?: any;
192-
generic?: any;
193-
peerField?: string;
194-
};
195-
196-
export const getOptionsFromRelationship = ({
197-
options,
198-
schemas,
199-
generic,
200-
peerField,
201-
}: tgetOptionsFromRelationship) => {
202-
if (!generic) {
203-
return options.map((option: any) => ({
204-
name: peerField ? (option[peerField]?.value ?? option[peerField]) : option.display_label,
205-
id: option.id,
206-
kind: option.__typename,
207-
}));
208-
}
209-
210-
if (generic) {
211-
return (generic.used_by || []).map((name: string) => {
212-
const relatedSchema = schemas.find((s: any) => s.kind === name);
213-
214-
if (relatedSchema) {
215-
return {
216-
name: relatedSchema.name,
217-
id: name,
218-
kind: relatedSchema.kind,
219-
};
220-
}
221-
});
222-
}
223-
224-
return [];
225-
};

frontend/app/src/entities/nodes/types.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1+
import { components } from "@/shared/api/rest/types.generated";
2+
13
// https://docs.infrahub.app/reference/schema/relationship/#kind
2-
export type RelationshipKind =
3-
| "Generic"
4-
| "Attribute"
5-
| "Component"
6-
| "Parent"
7-
| "Group"
8-
| "Hierarchy"
9-
| "Profile";
4+
export type RelationshipKind = components["schemas"]["RelationshipKind"];
105

116
export type NodeCore = {
127
id: string;

frontend/app/src/entities/nodes/utils.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,8 @@ import {
66
IP_PREFIX_GENERIC,
77
} from "@/entities/ipam/constants";
88
import { RESOURCE_GENERIC_KIND } from "@/entities/resource-manager/constants";
9-
import {
10-
genericSchemasAtom,
11-
nodeSchemasAtom,
12-
profileSchemasAtom,
13-
} from "@/entities/schema/stores/schema.atom";
14-
import { isGenericSchema } from "@/entities/schema/utils/is-generic-schema";
15-
import { store } from "@/shared/stores";
16-
import { constructPath, overrideQueryParams } from "../../shared/api/rest/fetch";
9+
import { getSchema } from "@/entities/schema/domain/get-schema";
10+
import { constructPath, overrideQueryParams } from "@/shared/api/rest/fetch";
1711

1812
const regex = /^Related/; // starts with Related
1913

@@ -39,13 +33,10 @@ export const getObjectDetailsUrl2 = (
3933
]);
4034
}
4135

42-
const nodes = store.get(nodeSchemasAtom);
43-
const generics = store.get(genericSchemasAtom);
44-
const profiles = store.get(profileSchemasAtom);
45-
const schema = [...nodes, ...generics, ...profiles].find(({ kind }) => kind === objectKind);
36+
const { schema, isGeneric } = getSchema(objectKind);
4637
if (!schema) return "#";
4738

48-
if (!isGenericSchema(schema)) {
39+
if (!isGeneric) {
4940
const inheritFrom = schema.inherit_from;
5041

5142
if (inheritFrom?.includes(IP_PREFIX_GENERIC)) {

frontend/app/src/entities/schema/domain/get-schema.test.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,29 @@ import {
22
genericSchemasAtom,
33
nodeSchemasAtom,
44
profileSchemasAtom,
5+
templateSchemasAtom,
56
} from "@/entities/schema/stores/schema.atom";
67
import { store } from "@/shared/stores";
78
import { beforeEach, describe, expect, it } from "vitest";
89
import {
910
generateGenericSchema,
1011
generateNodeSchema,
1112
generateProfileSchema,
13+
generateTemplateSchema,
1214
} from "../../../../tests/fake/schema";
1315
import { getSchema } from "./get-schema";
1416

1517
describe("getSchema", () => {
1618
const nodeSchema = generateNodeSchema({ kind: "Node" });
1719
const genericSchema = generateGenericSchema({ kind: "Generic" });
1820
const profileSchema = generateProfileSchema({ kind: "Profile" });
21+
const templateSchema = generateTemplateSchema({ kind: "Template" });
1922

2023
beforeEach(() => {
2124
store.set(nodeSchemasAtom, [nodeSchema]);
22-
2325
store.set(genericSchemasAtom, [genericSchema]);
24-
2526
store.set(profileSchemasAtom, [profileSchema]);
27+
store.set(templateSchemasAtom, [templateSchema]);
2628
});
2729

2830
it("should return null schema when no kind is provided", () => {
@@ -32,6 +34,7 @@ describe("getSchema", () => {
3234
isGeneric: false,
3335
isNode: false,
3436
isProfile: false,
37+
isTemplate: false,
3538
});
3639
});
3740

@@ -42,6 +45,7 @@ describe("getSchema", () => {
4245
isGeneric: false,
4346
isNode: true,
4447
isProfile: false,
48+
isTemplate: false,
4549
});
4650
});
4751

@@ -52,6 +56,7 @@ describe("getSchema", () => {
5256
isGeneric: true,
5357
isNode: false,
5458
isProfile: false,
59+
isTemplate: false,
5560
});
5661
});
5762

@@ -62,6 +67,18 @@ describe("getSchema", () => {
6267
isGeneric: false,
6368
isNode: false,
6469
isProfile: true,
70+
isTemplate: false,
71+
});
72+
});
73+
74+
it("should return template schema when kind matches a template", () => {
75+
const result = getSchema("Template");
76+
expect(result).toEqual({
77+
schema: templateSchema,
78+
isGeneric: false,
79+
isNode: false,
80+
isProfile: false,
81+
isTemplate: true,
6582
});
6683
});
6784

@@ -72,6 +89,7 @@ describe("getSchema", () => {
7289
isGeneric: false,
7390
isNode: false,
7491
isProfile: false,
92+
isTemplate: false,
7593
});
7694
});
7795
});

frontend/app/src/entities/schema/domain/get-schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
genericSchemasAtom,
33
nodeSchemasAtom,
44
profileSchemasAtom,
5+
templateSchemasAtom,
56
} from "@/entities/schema/stores/schema.atom";
67
import { SchemaResult, resolveSchema } from "@/entities/schema/utils/resolve-schema";
78
import { store } from "@/shared/stores";
@@ -11,5 +12,6 @@ export const getSchema = (kind?: string | null): SchemaResult => {
1112
nodeSchemas: store.get(nodeSchemasAtom),
1213
genericSchemas: store.get(genericSchemasAtom),
1314
profileSchemas: store.get(profileSchemasAtom),
15+
templateSchemas: store.get(templateSchemasAtom),
1416
});
1517
};

frontend/app/src/entities/schema/stores/schema.atom.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { GenericSchema, Namespace, NodeSchema, ProfileSchema } from "@/entities/schema/types";
1+
import {
2+
GenericSchema,
3+
Namespace,
4+
NodeSchema,
5+
ProfileSchema,
6+
TemplateSchema,
7+
} from "@/entities/schema/types";
28
import { atom } from "jotai";
39

410
export const nodeSchemasAtom = atom<NodeSchema[]>([]);
511
export const genericSchemasAtom = atom<GenericSchema[]>([]);
612
export const profileSchemasAtom = atom<ProfileSchema[]>([]);
13+
export const templateSchemasAtom = atom<TemplateSchema[]>([]);
714
export const namespacesAtom = atom<Namespace[]>([]);
815

916
// Current schema hash for tracking changes

frontend/app/src/entities/schema/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { components } from "@/shared/api/rest/types.generated";
44
export type NodeSchema = components["schemas"]["APINodeSchema"];
55
export type GenericSchema = components["schemas"]["APIGenericSchema"];
66
export type ProfileSchema = components["schemas"]["APIProfileSchema"];
7+
export type TemplateSchema = components["schemas"]["APITemplateSchema"];
78

8-
export type ModelSchema = GenericSchema | NodeSchema | ProfileSchema;
9+
export type ModelSchema = GenericSchema | NodeSchema | ProfileSchema | TemplateSchema;
910

1011
export type RelationshipSchema = components["schemas"]["RelationshipSchema"];
1112

frontend/app/src/entities/schema/ui/decorators/withSchemaContext.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ import {
88
namespacesAtom,
99
nodeSchemasAtom,
1010
profileSchemasAtom,
11+
templateSchemasAtom,
1112
} from "@/entities/schema/stores/schema.atom";
1213
import { schemaKindLabelState } from "@/entities/schema/stores/schemaKindLabel.atom";
1314
import { schemaKindNameState } from "@/entities/schema/stores/schemaKindName.atom";
14-
import { GenericSchema, Namespace, NodeSchema, ProfileSchema } from "@/entities/schema/types";
15+
import {
16+
GenericSchema,
17+
Namespace,
18+
NodeSchema,
19+
ProfileSchema,
20+
TemplateSchema,
21+
} from "@/entities/schema/types";
1522
import { tokenSchema } from "@/entities/user-profile/ui/token-schema";
1623
import { Branch } from "@/shared/api/graphql/generated/graphql";
1724
import { fetchUrl } from "@/shared/api/rest/fetch";
@@ -41,6 +48,7 @@ export const withSchemaContext = (AppComponent: any) => (props: any) => {
4148
const setGenerics = useSetAtom(genericSchemasAtom);
4249
const setNamespaces = useSetAtom(namespacesAtom);
4350
const setProfiles = useSetAtom(profileSchemasAtom);
51+
const setTemplates = useSetAtom(templateSchemasAtom);
4452
const setState = useSetAtom(stateAtom);
4553
const branches = useAtomValue(branchesState);
4654
const [branchInQueryString] = useQueryParam(QSP.BRANCH, StringParam);
@@ -54,15 +62,17 @@ export const withSchemaContext = (AppComponent: any) => (props: any) => {
5462
main: string;
5563
nodes: NodeSchema[];
5664
generics: GenericSchema[];
57-
namespaces: Namespace[];
5865
profiles: ProfileSchema[];
66+
templates: TemplateSchema[];
67+
namespaces: Namespace[];
5968
} = await fetchUrl(CONFIG.SCHEMA_URL(branch?.name));
6069

6170
const hash = schemaData.main;
6271
const schema = sortByName([...schemaData.nodes, tokenSchema]);
6372
const generics = sortByName(schemaData.generics || []);
64-
const namespaces = sortByName(schemaData.namespaces || []);
6573
const profiles = sortByName(schemaData.profiles || []);
74+
const templates = sortByName(schemaData.templates || []);
75+
const namespaces = sortByName(schemaData.namespaces || []);
6676

6777
schema.forEach((s) => {
6878
s.attributes = sortByOrderWeight(s.attributes || []);
@@ -73,12 +83,14 @@ export const withSchemaContext = (AppComponent: any) => (props: any) => {
7383
...schema.map((s) => s.kind),
7484
...generics.map((s) => s.kind),
7585
...profiles.map((s) => s.kind),
86+
...templates.map((s) => s.kind),
7687
];
7788

7889
const schemaNames = [
7990
...schema.map((s) => s.label),
8091
...generics.map((s) => s.label),
8192
...profiles.map((s) => s.label),
93+
...templates.map((s) => s.label),
8294
];
8395
const schemaKindNameTuples = R.zip(schemaKinds, schemaNames);
8496
const schemaKindNameMap = {
@@ -99,6 +111,7 @@ export const withSchemaContext = (AppComponent: any) => (props: any) => {
99111
setSchemaKindLabelState(schemaKindLabelMap);
100112
setNamespaces(namespaces);
101113
setProfiles(profiles);
114+
setTemplates(templates);
102115
setState({ isReady: true });
103116
} catch (error) {
104117
toast(

frontend/app/src/entities/schema/ui/hooks/useSchema.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
genericSchemasAtom,
33
nodeSchemasAtom,
44
profileSchemasAtom,
5+
templateSchemasAtom,
56
} from "@/entities/schema/stores/schema.atom";
67
import { SchemaResult, resolveSchema } from "@/entities/schema/utils/resolve-schema";
78
import { useAtomValue } from "jotai/index";
@@ -10,10 +11,12 @@ export const useSchema = (kind: string | null | undefined): SchemaResult => {
1011
const nodeSchemas = useAtomValue(nodeSchemasAtom);
1112
const profileSchemas = useAtomValue(profileSchemasAtom);
1213
const genericSchemas = useAtomValue(genericSchemasAtom);
14+
const templateSchemas = useAtomValue(templateSchemasAtom);
1315

1416
return resolveSchema(kind, {
1517
nodeSchemas,
1618
genericSchemas,
1719
profileSchemas,
20+
templateSchemas,
1821
});
1922
};

0 commit comments

Comments
 (0)