Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add diff conflict at the attribute level for value conflicts #4253

Merged
merged 44 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
31629d6
add cardnality in query
pa-lem Aug 28, 2024
6efb82e
use node attribute for relationship one
pa-lem Aug 28, 2024
a553eca
Merge branch 'release-0.16' of github.com:opsmill/infrahub into ple-I…
pa-lem Aug 28, 2024
f946dca
Merge branch 'release-0.16' of github.com:opsmill/infrahub into ple-I…
pa-lem Aug 29, 2024
debecbe
udpate relationshiip structure for attribute
pa-lem Aug 29, 2024
161a3d8
Merge branch 'release-0.16' of github.com:opsmill/infrahub into ple-I…
pa-lem Sep 2, 2024
fbaf7cc
Merge branch 'release-0.16' of github.com:opsmill/infrahub into ple-I…
pa-lem Sep 3, 2024
08ce586
add more details in response
pa-lem Sep 3, 2024
8de8644
make relationships match attribute node diff
pa-lem Sep 3, 2024
a86988f
fix test with new UI
pa-lem Sep 3, 2024
61ff4d8
default open for items with conflicts
pa-lem Sep 3, 2024
9a7d9a7
fix test with new UI
pa-lem Sep 3, 2024
102e5a7
fix test after rebase
bilalabbad Sep 3, 2024
184ec41
add diff conflict at the attribute level for value conflicts
ajtmccarty Sep 3, 2024
cbd6075
remove HAS_VALUE conflict from the property
ajtmccarty Sep 3, 2024
60d7c4c
Merge branch 'release-0.16' of github.com:opsmill/infrahub into ple-f…
pa-lem Sep 4, 2024
3890c57
Merge branch 'ple-fix-e2e-tests' of github.com:opsmill/infrahub into …
pa-lem Sep 4, 2024
4a5afcb
Merge branch 'release-0.16' of github.com:opsmill/infrahub into ple-I…
pa-lem Sep 4, 2024
6106cce
fix test
pa-lem Sep 4, 2024
4c52436
update test
pa-lem Sep 4, 2024
2694720
avoid retry failure
pa-lem Sep 4, 2024
8bd9e85
increase timeout for diff update query
pa-lem Sep 4, 2024
584649b
IFC-623 diff locking enhancements
ajtmccarty Sep 4, 2024
2c13b57
Merge branch 'ajtm-09042024-diff-locking-v2-IFC-623' into ajtm-e2e-di…
ajtmccarty Sep 4, 2024
037a088
add locked method to NATSLock
ajtmccarty Sep 5, 2024
2d0d6ac
add unit test
ajtmccarty Sep 5, 2024
aef7e5a
add some debug logging statements
ajtmccarty Sep 5, 2024
5a137f0
Merge branch 'ajtm-09042024-diff-locking-v2-IFC-623' into ajtm-e2e-di…
ajtmccarty Sep 5, 2024
47951ce
add exception handling for deleted proposed change
ajtmccarty Sep 5, 2024
f1256fa
Merge branch 'ajtm-09042024-diff-locking-v2-IFC-623' into ajtm-e2e-di…
ajtmccarty Sep 5, 2024
8d5e8ad
pylint exclusion
ajtmccarty Sep 5, 2024
0cb3d25
Merge branch 'ajtm-09042024-diff-locking-v2-IFC-623' into ajtm-e2e-di…
ajtmccarty Sep 5, 2024
dfed623
Merge branch 'ple-fix-e2e-tests' of github.com:opsmill/infrahub into …
pa-lem Sep 5, 2024
4bc1802
Merge branch 'release-0.16' of github.com:opsmill/infrahub into ple-d…
pa-lem Sep 5, 2024
81d6567
Merge branch 'ajtm-e2e-diff-tests-with-better-locking' of github.com:…
pa-lem Sep 5, 2024
bd0f150
add conflict at top level if needed
pa-lem Sep 5, 2024
13c3e30
add conflict badge in title for node attribute
pa-lem Sep 5, 2024
9e554a1
update types and details
pa-lem Sep 5, 2024
39a260a
update toggle button ui
pa-lem Sep 5, 2024
4d75879
update conflict resolution ui
pa-lem Sep 5, 2024
721d404
add diff conflict at the attribute level for value conflicts
ajtmccarty Sep 3, 2024
24ad2c5
remove HAS_VALUE conflict from the property
ajtmccarty Sep 3, 2024
aa7686f
Merge branch 'ple-diff-conflicts' of github.com:opsmill/infrahub into…
pa-lem Sep 5, 2024
eb7ee67
e2e test updates for new diff
pa-lem Sep 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backend/infrahub/graphql/queries/diff/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from infrahub.core import registry
from infrahub.core.constants import DiffAction, RelationshipCardinality
from infrahub.core.constants.database import DatabaseEdgeType
from infrahub.core.diff.model.path import NameTrackingId
from infrahub.core.diff.repository.repository import DiffRepository
from infrahub.core.query.diff import DiffCountChanges
Expand Down Expand Up @@ -75,6 +76,7 @@ class DiffAttribute(DiffSummaryCounts):
path_identifier = String(required=True)
properties = List(DiffProperty)
contains_conflict = Boolean(required=True)
conflict = Field(ConflictDetails, required=False)


class DiffSingleRelationship(DiffSummaryCounts):
Expand Down Expand Up @@ -207,13 +209,19 @@ def to_diff_attribute(
diff_properties = [
self.to_diff_property(enriched_property=e_prop, context=context) for e_prop in enriched_attribute.properties
]
conflict = None
for diff_prop in diff_properties:
if diff_prop.property_type == DatabaseEdgeType.HAS_VALUE.value and diff_prop.conflict:
conflict = diff_prop.conflict
diff_prop.conflict = None
return DiffAttribute(
name=enriched_attribute.name,
last_changed_at=enriched_attribute.changed_at.obj,
status=enriched_attribute.action,
path_identifier=enriched_attribute.path_identifier,
properties=diff_properties,
contains_conflict=enriched_attribute.contains_conflict,
conflict=conflict,
num_added=enriched_attribute.num_added,
num_updated=enriched_attribute.num_updated,
num_removed=enriched_attribute.num_removed,
Expand Down
41 changes: 25 additions & 16 deletions backend/tests/unit/graphql/test_diff_tree_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@
num_updated
num_conflicts
contains_conflict
conflict {
uuid
base_branch_action
base_branch_value
base_branch_changed_at
base_branch_label
diff_branch_action
diff_branch_value
diff_branch_changed_at
diff_branch_label
selected_branch
}
properties {
property_type
last_changed_at
Expand Down Expand Up @@ -353,6 +365,18 @@ async def test_diff_tree_one_attr_change(
"num_conflicts": 1,
"status": UPDATED_ACTION,
"contains_conflict": True,
"conflict": {
"uuid": enriched_conflict.uuid,
"base_branch_action": UPDATED_ACTION,
"base_branch_value": "#fedcba",
"base_branch_changed_at": enriched_conflict.base_branch_changed_at.to_string(with_z=False),
"base_branch_label": None,
"diff_branch_action": UPDATED_ACTION,
"diff_branch_value": "#abcdef",
"diff_branch_changed_at": enriched_conflict.diff_branch_changed_at.to_string(with_z=False),
"diff_branch_label": None,
"selected_branch": GraphQLConfictSelection.DIFF_BRANCH.name,
},
"properties": [
{
"property_type": "HAS_VALUE",
Expand All @@ -362,22 +386,7 @@ async def test_diff_tree_one_attr_change(
"previous_label": None,
"new_label": None,
"status": UPDATED_ACTION,
"conflict": {
"uuid": enriched_conflict.uuid,
"base_branch_action": UPDATED_ACTION,
"base_branch_value": "#fedcba",
"base_branch_changed_at": enriched_conflict.base_branch_changed_at.to_string(
with_z=False
),
"base_branch_label": None,
"diff_branch_action": UPDATED_ACTION,
"diff_branch_value": "#abcdef",
"diff_branch_changed_at": enriched_conflict.diff_branch_changed_at.to_string(
with_z=False
),
"diff_branch_label": None,
"selected_branch": GraphQLConfictSelection.DIFF_BRANCH.name,
},
"conflict": None,
}
],
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/app/src/components/buttons/toggle-buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const ToggleButtons = ({ tabs, ...props }: TabsProps) => {
<Button
key={tab.label}
onClick={tab.onClick}
variant={tab.isActive ? "active" : "outline"}
size={"sm"}
variant={tab.isActive ? "active" : "ghost"}
className={"cursor-pointer border-0 px-4 py-2 rounded-none"}
{...props}>
{tab.label}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ export const getProposedChangesDiffTree = gql`
contains_conflict
last_changed_at
name
conflict {
base_branch_label
base_branch_action
base_branch_changed_at
base_branch_value
diff_branch_label
diff_branch_action
diff_branch_changed_at
diff_branch_value
selected_branch
uuid
}
properties {
conflict {
base_branch_label
Expand Down
71 changes: 53 additions & 18 deletions frontend/app/src/screens/diff/node-diff/conflict.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { ToggleButtons } from "@/components/buttons/toggle-buttons";
import { Checkbox } from "@/components/inputs/checkbox";
import { ALERT_TYPES, Alert } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import graphqlClient from "@/graphql/graphqlClientApollo";
import { resolveConflict } from "@/graphql/mutations/diff/resolveConflict";
import { usePermission } from "@/hooks/usePermission";
import LoadingScreen from "@/screens/loading-screen/loading-screen";
import { currentBranchAtom } from "@/state/atoms/branches.atom";
import { proposedChangedState } from "@/state/atoms/proposedChanges.atom";
import { datetimeAtom } from "@/state/atoms/time.atom";
import { gql } from "@apollo/client";
import { Icon } from "@iconify-icon/react";
import { useAtomValue } from "jotai/index";
import { useState } from "react";
import { toast } from "react-toastify";
Expand All @@ -15,6 +19,7 @@ export const Conflict = ({ conflict }: any) => {
const date = useAtomValue(datetimeAtom);
const permission = usePermission();
const [isLoading, setIsLoading] = useState(false);
const proposedChangesDetails = useAtomValue(proposedChangedState);

const handleAccept = async (conflictValue: string) => {
try {
Expand All @@ -40,7 +45,9 @@ export const Conflict = ({ conflict }: any) => {

await graphqlClient.refetchQueries({ include: ["GET_PROPOSED_CHANGES_DIFF_TREE"] });

toast(<Alert type={ALERT_TYPES.SUCCESS} message="Conflict marked as resovled" />);
const message = newValue ? "Conflict marked as resovled" : "Conflict marked as not resovled";

toast(<Alert type={ALERT_TYPES.SUCCESS} message={message} />);

setIsLoading(false);
} catch (error) {
Expand All @@ -49,23 +56,51 @@ export const Conflict = ({ conflict }: any) => {
}
};

const tabs = [
{
label: "Base",
isActive: conflict.selected_branch === "BASE_BRANCH",
onClick: () => handleAccept("BASE_BRANCH"),
},
{
label: "Branch",
isActive: conflict.selected_branch === "DIFF_BRANCH",
onClick: () => handleAccept("DIFF_BRANCH"),
},
];

return (
<div className="flex items-center justify-end p-2">
<span className="mr-1">Accept:</span>
<ToggleButtons tabs={tabs} isLoading={isLoading} disabled={!permission.write.allow} />
<div className="flex items-center justify-end gap-2 p-2">
{isLoading && <LoadingScreen hideText size={16} />}

<span className="text-xs">Choose the branch to resolve the conflict:</span>

<div className="flex gap-2">
<div className="flex items-center gap-2">
<Checkbox
id={"base"}
disabled={isLoading || !permission.write.allow}
checked={conflict.selected_branch === "BASE_BRANCH"}
onChange={() => handleAccept("BASE_BRANCH")}
/>
<label
htmlFor={"base"}
className={
conflict.selected_branch === "BASE_BRANCH" ? "cursor-default" : "cursor-pointer"
}>
<Badge variant="green">
<Icon icon="mdi:layers-triple" className="mr-1" />
{proposedChangesDetails.destination_branch?.value}
</Badge>
</label>
</div>

<div className="flex items-center gap-2">
<Checkbox
id={"diff"}
disabled={isLoading || !permission.write.allow}
checked={conflict.selected_branch === "DIFF_BRANCH"}
onChange={() => handleAccept("DIFF_BRANCH")}
/>
<label
htmlFor={"diff"}
className={
conflict.selected_branch === "DIFF_BRANCH" ? "cursor-default" : "cursor-pointer"
}>
<Badge variant="blue">
<Icon icon="mdi:layers-triple" className="mr-1" />
{proposedChangesDetails.source_branch?.value}
</Badge>
</label>
</div>
</div>
</div>
);
};
9 changes: 8 additions & 1 deletion frontend/app/src/screens/diff/node-diff/node-attribute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { DiffRow } from "@/screens/diff/node-diff/utils";
import { DiffAttribute, DiffStatus } from "@/screens/diff/node-diff/types";
import { DiffThread } from "@/screens/diff/node-diff/thread";
import { DiffNodeProperty } from "@/screens/diff/node-diff/node-property";
import { Conflict } from "./conflict";
import { BadgeConflict } from "../diff-badge";

type DiffNodeAttributeProps = {
attribute: DiffAttribute;
Expand All @@ -25,7 +27,10 @@ export const DiffNodeAttribute = ({
hasConflicts={attribute.contains_conflict}
title={
<div className="flex justify-between items-center pr-2">
<div className="py-3 font-semibold">{attribute.name}</div>
<div className="flex items-center py-3 gap-2 font-semibold">
{attribute.name}
{attribute.conflict && <BadgeConflict>Conflict</BadgeConflict>}
</div>

{!branchName && attribute.path_identifier && (
<DiffThread path={attribute.path_identifier} />
Expand All @@ -35,6 +40,8 @@ export const DiffNodeAttribute = ({
left={previousValue}
right={newValue}>
<div className="divide-y border-t">
{attribute.conflict && <Conflict conflict={attribute.conflict} />}

{attribute.properties.map((property, index: number) => (
<DiffNodeProperty key={index} property={property} status={status} />
))}
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/src/screens/diff/node-diff/node-property.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type DiffNodePropertyProps = {
status: DiffStatus;
};

const getPreviousValue = (property: DiffProperty) => {
export const getPreviousValue = (property: DiffProperty) => {
const previousValue = formatValue(property.previous_value);
if (previousValue === null) return null;

Expand All @@ -36,7 +36,7 @@ const getPreviousValue = (property: DiffProperty) => {
);
};

const getNewValue = (property: DiffProperty) => {
export const getNewValue = (property: DiffProperty) => {
const newValue = formatValue(property.new_value);
if (newValue === null) return null;

Expand Down
51 changes: 33 additions & 18 deletions frontend/app/src/screens/diff/node-diff/node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { DiffNodeRelationship } from "./node-relationship";
import { DiffNodeAttribute } from "./node-attribute";
import { DiffThread } from "./thread";
import { useLocation, useParams } from "react-router-dom";
import { DiffBadge, formatValue } from "@/screens/diff/node-diff/utils";
import { DiffBadge } from "@/screens/diff/node-diff/utils";
import { useAtomValue } from "jotai";
import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom";
import type { DiffNode as DiffNodeType } from "@/screens/diff/node-diff/types";
import type { DiffNode as DiffNodeType, PropertyType } from "@/screens/diff/node-diff/types";
import { classNames } from "@/utils/common";
import { useEffect, useRef } from "react";
import { Icon } from "@iconify-icon/react";
import { getNewValue, getPreviousValue } from "./node-property";

type DiffNodeProps = {
node: DiffNodeType;
Expand Down Expand Up @@ -72,10 +73,14 @@ export const DiffNode = ({ sourceBranch, destinationBranch, node }: DiffNodeProp
key={index}
attribute={attribute}
status={node.status}
previousValue={
valueProperty?.previous_value && formatValue(valueProperty?.previous_value)
}
newValue={status !== "REMOVED" && formatValue(valueProperty?.new_value)}
previousValue={getPreviousValue({
...valueProperty,
conflict: attribute.conflict,
})}
newValue={getNewValue({
...valueProperty,
conflict: attribute.conflict,
})}
/>
);
})}
Expand All @@ -87,25 +92,35 @@ export const DiffNode = ({ sourceBranch, destinationBranch, node }: DiffNodeProp
const attribute = {
name: relationship.name,
contains_conflict: relationship.contains_conflict,
properties: [
{
conflict: element.conflict,
new_value: element.peer_label,
path_identifier: element.path_identifier,
previous_value: element.conflict?.base_branch_label,
property_type: "HAS_VALUE",
},
...element.properties,
],
properties: element.properties,
conflict: element.conflict,
path_identifier: element.path_identifier,
uuid: element.uuid,
};

const valueProperty = {
conflict: element.conflict,
new_value: element.peer_label,
path_identifier: element.path_identifier,
previous_value: element.conflict?.base_branch_label,
property_type: "HAS_VALUE" as PropertyType,
last_changed_at: element.last_changed_at,
status: element.status,
};

return (
<DiffNodeAttribute
key={index}
attribute={attribute}
status={node.status}
previousValue={element.conflict?.base_branch_label}
newValue={element.peer_label}
previousValue={getPreviousValue({
...valueProperty,
conflict: attribute.conflict,
})}
newValue={getNewValue({
...valueProperty,
conflict: attribute.conflict,
})}
/>
);
}
Expand Down
16 changes: 9 additions & 7 deletions frontend/app/src/screens/diff/node-diff/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export const DIFF_STATUS = {

export type DiffStatus = (typeof DIFF_STATUS)[keyof typeof DIFF_STATUS];

export type PropertyType =
| "HAS_VALUE"
| "HAS_OWNER"
| "HAS_SOURCE"
| "IS_VISIBLE"
| "IS_PROTECTED"
| "IS_RELATED";

export type DiffConflict = {
base_branch_action: DiffStatus;
base_branch_changed_at: string;
Expand All @@ -23,13 +31,7 @@ export type DiffProperty = {
conflict: DiffConflict | null;
new_value: any;
previous_value: any;
property_type:
| "HAS_VALUE"
| "HAS_OWNER"
| "HAS_SOURCE"
| "IS_VISIBLE"
| "IS_PROTECTED"
| "IS_RELATED";
property_type: PropertyType;
path_identifier: string | null;
status: DiffStatus;
};
Expand Down
Loading
Loading