Skip to content

Commit 05e2c32

Browse files
authored
Merge pull request #6609 from opsmill/stable
Merge stable into release-1.3
2 parents af4b87a + 1b56f03 commit 05e2c32

File tree

15 files changed

+257
-111
lines changed

15 files changed

+257
-111
lines changed

backend/tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
ResponseClass = TypeVar("ResponseClass")
6666
DEFAULT_TESTING_LOG_LEVEL = "WARNING"
6767

68+
pytest.register_assert_rewrite("tests.db_snapshot")
69+
6870

6971
def pytest_addoption(parser):
7072
parser.addoption("--neo4j", action="store_true", dest="neo4j", default=False, help="enable neo4j tests")

changelog/6467.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Resolved an issue where the copy to clipboard did not work on insecure (HTTP) URLs.

changelog/6581.fixed.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Fixed an error preventing pool selection from being listed when peer has a custom namespace
2+
- Hide pool selection on relationship of cardinality many (it'll be added later)

frontend/app/src/entities/ipam/constants.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
import { RELATIONSHIP_VIEW_BLACKLIST } from "@/config/constants";
2-
import { IP_ADDRESS_POOL, IP_PREFIX_POOL } from "../resource-manager/constants";
32

43
export const IP_NAMESPACE_GENERIC = "BuiltinIPNamespace";
54
export const IP_ADDRESS_GENERIC = "BuiltinIPAddress";
65
export const IP_PREFIX_GENERIC = "BuiltinIPPrefix";
76

87
export const POOLS_PEER = [IP_ADDRESS_GENERIC, IP_PREFIX_GENERIC];
9-
export const POOLS_DICTIONNARY = {
10-
IpamIPAddress: IP_ADDRESS_POOL,
11-
IpamIPPrefix: IP_PREFIX_POOL,
12-
};
138

149
export const TREE_ROOT_ID = "root" as const;
1510

frontend/app/src/entities/nodes/api/dropdownOptions.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

frontend/app/src/shared/components/display/meta-details-tooltips.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { getObjectDetailsUrl } from "@/entities/nodes/utils";
12
import { AnyAttribute } from "@/shared/api/graphql/generated/graphql";
2-
import { constructPath } from "@/shared/api/rest/fetch";
33
import { Button } from "@/shared/components/buttons/button-primitive";
44
import { PropertyList } from "@/shared/components/table/property-list";
55
import { Badge } from "@/shared/components/ui/badge";
@@ -30,7 +30,7 @@ export default function MetaDetailsTooltip({
3030
{
3131
name: "Source",
3232
value: source ? (
33-
<Link to={constructPath(`/objects/${source.__typename}/${source.id}`)}>
33+
<Link to={getObjectDetailsUrl(source.__typename, source.id)}>
3434
{isFromProfile ? (
3535
<Badge variant="green" className="font-normal hover:underline">
3636
<Icon icon="mdi:shape-plus-outline" className="mr-1" />
@@ -55,9 +55,7 @@ export default function MetaDetailsTooltip({
5555
{
5656
name: "Owner",
5757
value: owner ? (
58-
<Link to={constructPath(`/objects/${owner.__typename}/${owner.id}`)}>
59-
{owner.display_label}
60-
</Link>
58+
<Link to={getObjectDetailsUrl(owner.__typename, owner.id)}>{owner.display_label}</Link>
6159
) : (
6260
"-"
6361
),

frontend/app/src/shared/components/form/fields/relationship-hierarchical.field.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { POOLS_PEER } from "@/entities/ipam/constants";
21
import { RelationshipNode } from "@/entities/nodes/relationships/domain/types";
32
import {
43
RelationshipHierarchicalInput,
@@ -11,7 +10,9 @@ import { PoolValue } from "@/shared/components/form/pool-selector";
1110
import {
1211
DynamicRelationshipFieldProps,
1312
FormRelationshipValue,
13+
PoolSource,
1414
} from "@/shared/components/form/type";
15+
import { getPoolKindFromSchema } from "@/shared/components/form/utils/get-pool-kind-from-schema";
1516
import { updateRelationshipFieldValue } from "@/shared/components/form/utils/updateFormFieldValue";
1617
import { PoolSelect } from "@/shared/components/inputs/pool-select";
1718
import { FormField, FormInput, FormMessage } from "@/shared/components/ui/form";
@@ -35,11 +36,18 @@ export default function RelationshipHierarchicalField({
3536
defaultValue={defaultValue}
3637
render={({ field }) => {
3738
const fieldData: FormRelationshipValue = field.value;
39+
const value: RelationshipNode | RelationshipNode[] | null =
40+
fieldData.value && "from_pool" in fieldData.value
41+
? {
42+
id: fieldData.value.from_pool.id,
43+
display_label: "Allocated by pool",
44+
__typename: (fieldData.source as PoolSource).kind,
45+
}
46+
: fieldData.value;
3847

39-
const peer = props.relationship.peer;
40-
const { schema, isNode } = useSchema(peer);
41-
const canSelectFromPool =
42-
isNode && !!schema.inherit_from?.some((from) => POOLS_PEER.includes(from));
48+
const { peer } = props.relationship;
49+
const { schema: peerSchema } = useSchema(peer);
50+
const poolKind = peerSchema ? getPoolKindFromSchema(peerSchema) : null;
4351
const selectedPoolId = fieldData?.source?.type === "pool" ? fieldData.source.id : null;
4452

4553
const onChange = (newValue: RelationshipNode | RelationshipNode[] | PoolValue | null) => {
@@ -62,20 +70,26 @@ export default function RelationshipHierarchicalField({
6270
<RelationshipHierarchicalManyInput
6371
{...field}
6472
peer={peer}
65-
value={fieldData.value as RelationshipNode[] | null}
73+
value={value as RelationshipNode[] | null}
6674
onChange={onChange}
6775
/>
6876
) : (
6977
<RelationshipHierarchicalInput
7078
{...field}
7179
peer={props.relationship.peer}
72-
value={fieldData.value as RelationshipNode | null}
80+
value={value as RelationshipNode | null}
7381
onChange={onChange}
7482
/>
7583
)}
7684
</FormInput>
77-
{canSelectFromPool && (
78-
<PoolSelect peer={peer} selectedPoolId={selectedPoolId} onChange={onChange} />
85+
86+
{props.relationship.cardinality === "one" && poolKind && peerSchema && (
87+
<PoolSelect
88+
poolKind={poolKind}
89+
peerSchema={peerSchema}
90+
selectedPoolId={selectedPoolId}
91+
onChange={onChange}
92+
/>
7993
)}
8094
</div>
8195

frontend/app/src/shared/components/form/fields/relationship.field.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { POOLS_PEER } from "@/entities/ipam/constants";
21
import { getRelationshipParent } from "@/entities/nodes/api/getRelationshipParent";
32
import { Node } from "@/entities/nodes/getObjectItemDisplayValue";
43
import {
@@ -15,6 +14,7 @@ import {
1514
DynamicRelationshipFieldProps,
1615
FormRelationshipValue,
1716
} from "@/shared/components/form/type";
17+
import { getPoolKindFromSchema } from "@/shared/components/form/utils/get-pool-kind-from-schema";
1818
import { updateRelationshipFieldValue } from "@/shared/components/form/utils/updateFormFieldValue";
1919
import { PoolSelect } from "@/shared/components/inputs/pool-select";
2020
import { RelationshipInput } from "@/shared/components/inputs/relationship-one";
@@ -355,10 +355,9 @@ const RelationshipField = ({
355355
render={({ field }) => {
356356
const fieldData: FormRelationshipValue = field.value;
357357

358-
const peer = relationship?.peer;
359-
const { schema, isNode } = useSchema(peer);
360-
const canSelectFromPool =
361-
isNode && !!schema.inherit_from?.some((from) => POOLS_PEER.includes(from));
358+
const { peer } = relationship;
359+
const { schema: peerSchema } = useSchema(peer);
360+
const poolKind = peerSchema ? getPoolKindFromSchema(peerSchema) : null;
362361
const selectedPoolId = fieldData?.source?.type === "pool" ? fieldData.source.id : null;
363362

364363
const onChange = (newValue: Node | PoolValue | null) => {
@@ -390,8 +389,14 @@ const RelationshipField = ({
390389
parent={{ name: parentRelationship?.name, value: selectedParent?.id }}
391390
/>
392391
</FormInput>
393-
{canSelectFromPool && (
394-
<PoolSelect peer={peer} selectedPoolId={selectedPoolId} onChange={onChange} />
392+
393+
{poolKind && peerSchema && (
394+
<PoolSelect
395+
poolKind={poolKind}
396+
peerSchema={peerSchema}
397+
selectedPoolId={selectedPoolId}
398+
onChange={onChange}
399+
/>
395400
)}
396401
</div>
397402
<FormMessage />
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { IP_ADDRESS_GENERIC, IP_PREFIX_GENERIC } from "@/entities/ipam/constants";
2+
import { IP_ADDRESS_POOL, IP_PREFIX_POOL } from "@/entities/resource-manager/constants";
3+
import { ModelSchema } from "@/entities/schema/types";
4+
import { describe, expect, it } from "vitest";
5+
import { generateGenericSchema, generateNodeSchema } from "../../../../../tests/fake/schema";
6+
import { getPoolKindFromSchema } from "./get-pool-kind-from-schema";
7+
8+
describe("getPoolKindFromSchema", () => {
9+
const baseNodeSchema = generateNodeSchema();
10+
const baseGenericSchema = generateGenericSchema();
11+
12+
it("should return IP_ADDRESS_POOL when schema is IP_ADDRESS_GENERIC", () => {
13+
// GIVEN
14+
const schema: ModelSchema = {
15+
...baseGenericSchema,
16+
kind: IP_ADDRESS_GENERIC,
17+
};
18+
19+
// WHEN
20+
const result = getPoolKindFromSchema(schema);
21+
22+
// THEN
23+
expect(result).toBe(IP_ADDRESS_POOL);
24+
});
25+
26+
it("should return IP_PREFIX_POOL when schema is IP_PREFIX_GENERIC", () => {
27+
// GIVEN
28+
const schema: ModelSchema = {
29+
...baseGenericSchema,
30+
kind: IP_PREFIX_GENERIC,
31+
};
32+
33+
// WHEN
34+
const result = getPoolKindFromSchema(schema);
35+
36+
// THEN
37+
expect(result).toBe(IP_PREFIX_POOL);
38+
});
39+
40+
it("should return null when schema is generic but kind doesn't match any pool type", () => {
41+
// GIVEN
42+
const schema: ModelSchema = {
43+
...baseGenericSchema,
44+
kind: "UnknownGeneric",
45+
};
46+
47+
// WHEN
48+
const result = getPoolKindFromSchema(schema);
49+
50+
// THEN
51+
expect(result).toBeNull();
52+
});
53+
54+
it("should return pool kind when schema inherits from a prefix pool type", () => {
55+
// GIVEN
56+
const schema: ModelSchema = {
57+
...baseNodeSchema,
58+
inherit_from: [IP_PREFIX_GENERIC, "SomeOtherType"],
59+
};
60+
61+
// WHEN
62+
const result = getPoolKindFromSchema(schema);
63+
64+
// THEN
65+
expect(result).toBe(IP_PREFIX_POOL);
66+
});
67+
68+
it("should return pool kind when schema inherits from an address pool type", () => {
69+
// GIVEN
70+
const schema: ModelSchema = {
71+
...baseNodeSchema,
72+
inherit_from: [IP_ADDRESS_GENERIC, "SomeOtherType"],
73+
};
74+
75+
// WHEN
76+
const result = getPoolKindFromSchema(schema);
77+
78+
// THEN
79+
expect(result).toBe(IP_ADDRESS_POOL);
80+
});
81+
82+
it("should return null when schema has no inheritance", () => {
83+
// GIVEN
84+
const schema: ModelSchema = {
85+
...baseNodeSchema,
86+
inherit_from: undefined,
87+
};
88+
89+
// WHEN
90+
const result = getPoolKindFromSchema(schema);
91+
92+
// THEN
93+
expect(result).toBeNull();
94+
});
95+
96+
it("should return null when schema inherits from non-pool types", () => {
97+
// GIVEN
98+
const schema: ModelSchema = {
99+
...baseNodeSchema,
100+
inherit_from: ["Type1", "Type2"],
101+
};
102+
103+
// WHEN
104+
const result = getPoolKindFromSchema(schema);
105+
106+
// THEN
107+
expect(result).toBeNull();
108+
});
109+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { IP_ADDRESS_GENERIC, IP_PREFIX_GENERIC } from "@/entities/ipam/constants";
2+
import { IP_ADDRESS_POOL, IP_PREFIX_POOL } from "@/entities/resource-manager/constants";
3+
import { ModelSchema } from "@/entities/schema/types";
4+
import { isOfKind } from "@/entities/schema/utils/is-of-kind";
5+
6+
export function getPoolKindFromSchema(schema: ModelSchema): string | null {
7+
if (isOfKind(IP_ADDRESS_GENERIC, schema)) {
8+
return IP_ADDRESS_POOL;
9+
}
10+
11+
if (isOfKind(IP_PREFIX_GENERIC, schema)) {
12+
return IP_PREFIX_POOL;
13+
}
14+
15+
return null;
16+
}

0 commit comments

Comments
 (0)