Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions scripts/breaking_changes_checker/breaking_changes_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,22 @@ def is_client(self, module_name: Any, class_name: Any) -> bool:
return True
return False

def is_operation_group(self, module_name: Any, class_name: Any) -> bool:
"""Determine whether a class should be treated as an operation group.

Operation group classes follow the codegen convention of a name ending
with ``Operations`` (case-insensitive, which also covers the default
``Operations`` class) and being defined in an operations module (module
namespace ending with ``operations``). This mirrors the existing
``attr_type.lower().endswith("operations")`` heuristic used elsewhere to
recognize operation group properties on clients.
"""
if isinstance(class_name, jsondiff.Symbol) or not isinstance(class_name, str):
return False
if isinstance(module_name, jsondiff.Symbol) or not isinstance(module_name, str):
return False
return class_name.lower().endswith("operations") and module_name.endswith("operations")

def run_post_processing(self) -> None:
# Remove duplicate reporting of changes that apply to both sync and async package components
self.run_async_cleanup(self.breaking_changes)
Expand Down
11 changes: 11 additions & 0 deletions scripts/breaking_changes_checker/changelog_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ChangeType(str, Enum):
ADDED_ENUM_MEMBER = "AddedEnumMember"
ADDED_FUNCTION_PARAMETER = "AddedFunctionParameter"
ADDED_OPERATION_GROUP = "AddedOperationGroup"
ADDED_OPERATION_GROUP_CLASS = "AddedOperationGroupClass"

class ChangelogTracker(BreakingChangesTracker):
ADDED_CLIENT_MSG = \
Expand All @@ -46,6 +47,8 @@ class ChangelogTracker(BreakingChangesTracker):
"Enum `{}` added member `{}`"
ADDED_OPERATION_GROUP_MSG = \
"Client `{}` added operation group `{}`"
ADDED_OPERATION_GROUP_CLASS_MSG = \
"Added operation group `{}`"


def __init__(self, stable: Dict, current: Dict, package_name: str, **kwargs: Any) -> None:
Expand Down Expand Up @@ -87,6 +90,14 @@ def run_non_breaking_class_level_diff_checks(self, module: Dict) -> None:
self.module_name, class_name
)
self.features_added.append(fa)
elif self.is_operation_group(self.module_name, self.class_name):
# This is a new operation group class
fa = (
self.ADDED_OPERATION_GROUP_CLASS_MSG,
ChangeType.ADDED_OPERATION_GROUP_CLASS,
self.module_name, class_name
)
self.features_added.append(fa)
else:
# This is a new class
fa = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,10 +672,10 @@
- Model `WorkspaceTagOperations` added parameter `match_condition` in method `delete`
- Model `WorkspaceTagOperations` added parameter `etag` in method `update`
- Model `WorkspaceTagOperations` added parameter `match_condition` in method `update`
- Added model `ApiGatewayHostnameBindingOperations`
- Added model `ApiToolOperations`
- Added model `ClientApplicationOperations`
- Added model `ClientApplicationProductLinkOperations`
- Added operation group `ApiGatewayHostnameBindingOperations`
- Added operation group `ApiToolOperations`
- Added operation group `ClientApplicationOperations`
- Added operation group `ClientApplicationProductLinkOperations`

### Breaking Changes

Expand Down
53 changes: 53 additions & 0 deletions scripts/breaking_changes_checker/tests/test_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,59 @@ def test_new_class_property_added_init():
assert args == ['azure.ai.contentsafety', 'AnalyzeTextResult', 'new_class_att']


def test_added_operation_group_class_not_labeled_as_model():
# A newly added operation group class must be reported as "Added operation group `X`",
# not "Added model `X`". It appears in both the sync `...operations` module and the async
# `...aio.operations` module, but the async cleanup should collapse it into a single entry.
def _op_group_module():
return {
"class_nodes": {
"ExistingOperations": {
"type": None,
"methods": {},
"properties": {}
},
"PrivateLinkResourcesOperations": {
"type": None,
"methods": {},
"properties": {}
}
}
}

def _stable_op_group_module():
return {
"class_nodes": {
"ExistingOperations": {
"type": None,
"methods": {},
"properties": {}
}
}
}

current = {
"azure.mgmt.attestation.operations": _op_group_module(),
"azure.mgmt.attestation.aio.operations": _op_group_module(),
}
stable = {
"azure.mgmt.attestation.operations": _stable_op_group_module(),
"azure.mgmt.attestation.aio.operations": _stable_op_group_module(),
}

bc = ChangelogTracker(stable, current, "azure-mgmt-attestation")
bc.run_checks()

op_group_entries = [fa for fa in bc.features_added if fa[0] == ChangelogTracker.ADDED_OPERATION_GROUP_CLASS_MSG]
added_model_entries = [fa for fa in bc.features_added if fa[0] == ChangelogTracker.ADDED_CLASS_MSG]

# Exactly one operation group entry (sync/async duplicate collapsed) and no "Added model".
assert len(op_group_entries) == 1
assert not added_model_entries
_, _, _, class_name = op_group_entries[0]
assert class_name == "PrivateLinkResourcesOperations"


def test_new_class_property_added_init_only():
# Testing if we get a report on a new class property added only in the init
current = {
Expand Down
Loading