Skip to content

Commit d77c65c

Browse files
authored
[aws][feat] Ignore datetime for history by default (#2256)
1 parent 4d6ff4a commit d77c65c

File tree

10 files changed

+43
-31
lines changed

10 files changed

+43
-31
lines changed

fixcore/fixcore/model/graph_access.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,19 +334,22 @@ def content_hash(
334334
def history_hash(js: Json, kind: Kind) -> str:
335335
sha256 = hashlib.sha256()
336336

337-
def walk_element(el: JsonElement, el_kind: Kind) -> None:
337+
def walk_element(el: JsonElement, el_kind: Kind, maybe_prop: Optional[Property]) -> None:
338338
if el is None:
339339
pass
340340
elif isinstance(el_kind, ComplexKind):
341341
walk_complex(el, el_kind)
342342
elif isinstance(el_kind, ArrayKind):
343343
if isinstance(el, list):
344344
for elem in el:
345-
walk_element(elem, el_kind.inner)
345+
walk_element(elem, el_kind.inner, maybe_prop)
346346
elif isinstance(el_kind, DictionaryKind):
347347
if isinstance(el, dict):
348348
for _, v in sorted(el.items()):
349-
walk_element(v, el_kind.value_kind)
349+
walk_element(v, el_kind.value_kind, maybe_prop)
350+
elif isinstance(el_kind, (DateKind, DateTimeKind)): # default: ignore, opt-in to keep
351+
if maybe_prop and maybe_prop.meta_get("keep_history", bool, False):
352+
sha256.update(str(el).encode("utf-8"))
350353
elif isinstance(el_kind, SimpleKind):
351354
sha256.update(str(el).encode("utf-8"))
352355

@@ -358,12 +361,12 @@ def walk_complex(el: JsonElement, el_kind: ComplexKind) -> None:
358361
and (prop.name not in PropsToIgnoreForHistory)
359362
and (prop_val := el.get(prop.name))
360363
):
361-
walk_element(prop_val, prop_kind)
364+
walk_element(prop_val, prop_kind, prop)
362365
if not el_kind.metadata.get("ignore_history"): # if defined on type, do not walk the hierarchy
363366
for base in el_kind.resolved_bases().values():
364367
walk_complex(el, base)
365368

366-
walk_element(js, kind)
369+
walk_element(js, kind, None)
367370
return sha256.hexdigest()[0:8]
368371

369372
@staticmethod

fixcore/tests/fixcore/model/graph_access_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ def test_content_hash() -> None:
9090
assert sha1 == sha2
9191

9292

93+
def test_history_hash(person_model: Model) -> None:
94+
address = person_model["Address"]
95+
a = {"id": "a1", "zip": "s1", "city": "c1", "mtime": "2021-06-18T10:31:34Z"}
96+
b = {"id": "a1", "zip": "s1", "city": "c1", "mtime": "2022-06-18T10:31:34Z"}
97+
c = {"id": "a1", "zip": "s2", "city": "c2", "mtime": "2022-06-18T10:31:34Z"}
98+
assert GraphBuilder.history_hash(a, address) == GraphBuilder.history_hash(b, address)
99+
assert GraphBuilder.history_hash(a, address) != GraphBuilder.history_hash(c, address)
100+
101+
93102
def test_root(graph_access: GraphAccess) -> None:
94103
assert graph_access.root() == "1"
95104

plugins/aws/fix_plugin_aws/resource/dynamodb.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,8 @@ class AwsDynamoDbPointInTimeRecovery:
340340
"latest_restorable_date_time": S("LatestRestorableDateTime"),
341341
}
342342
status: Optional[str] = field(default=None, metadata={"description": "The current state of point in time recovery: ENABLED - Point in time recovery is enabled. DISABLED - Point in time recovery is disabled."}) # fmt: skip
343-
earliest_restorable_date_time: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "Specifies the earliest point in time you can restore your table to. You can restore your table to any point in time during the last 35 days."}) # fmt: skip
344-
latest_restorable_date_time: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "LatestRestorableDateTime is typically 5 minutes before the current time."}) # fmt: skip
343+
earliest_restorable_date_time: Optional[datetime] = field(default=None, metadata={"description": "Specifies the earliest point in time you can restore your table to. You can restore your table to any point in time during the last 35 days."}) # fmt: skip
344+
latest_restorable_date_time: Optional[datetime] = field(default=None, metadata={"description": "LatestRestorableDateTime is typically 5 minutes before the current time."}) # fmt: skip
345345

346346

347347
@define(eq=False, slots=False)

plugins/aws/fix_plugin_aws/resource/ecs.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ class AwsEcsTask(EcsTaggable, AwsResource):
488488
task_capacity_provider_name: Optional[str] = field(default=None)
489489
task_cluster_arn: Optional[str] = field(default=None)
490490
task_connectivity: Optional[str] = field(default=None, metadata=dict(ignore_history=True))
491-
task_connectivity_at: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
491+
task_connectivity_at: Optional[datetime] = field(default=None)
492492
task_container_instance_arn: Optional[str] = field(default=None)
493493
task_containers: List[AwsEcsContainer] = field(factory=list)
494494
task_cpu: Optional[str] = field(default=None)
@@ -504,14 +504,14 @@ class AwsEcsTask(EcsTaggable, AwsResource):
504504
task_overrides: Optional[AwsEcsTaskOverride] = field(default=None)
505505
task_platform_version: Optional[str] = field(default=None)
506506
task_platform_family: Optional[str] = field(default=None)
507-
task_pull_started_at: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
508-
task_pull_stopped_at: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
509-
task_started_at: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
510-
task_started_by: Optional[str] = field(default=None, metadata=dict(ignore_history=True))
511-
task_stop_code: Optional[str] = field(default=None, metadata=dict(ignore_history=True))
512-
task_stopped_at: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
513-
task_stopped_reason: Optional[str] = field(default=None, metadata=dict(ignore_history=True))
514-
task_stopping_at: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
507+
task_pull_started_at: Optional[datetime] = field(default=None)
508+
task_pull_stopped_at: Optional[datetime] = field(default=None)
509+
task_started_at: Optional[datetime] = field(default=None)
510+
task_started_by: Optional[str] = field(default=None)
511+
task_stop_code: Optional[str] = field(default=None)
512+
task_stopped_at: Optional[datetime] = field(default=None)
513+
task_stopped_reason: Optional[str] = field(default=None)
514+
task_stopping_at: Optional[datetime] = field(default=None)
515515
task_definition_arn: Optional[str] = field(default=None)
516516
task_version: Optional[int] = field(default=None)
517517
task_ephemeral_storage: Optional[int] = field(default=None)

plugins/aws/fix_plugin_aws/resource/iam.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ class AwsIamUser(AwsResource, BaseUser, BaseIamPrincipal):
659659
user_policies: List[AwsIamPolicyDetail] = field(factory=list)
660660
user_permissions_boundary: Optional[AwsIamAttachedPermissionsBoundary] = field(default=None)
661661
password_enabled: Optional[bool] = field(default=None)
662-
password_last_used: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
662+
password_last_used: Optional[datetime] = field(default=None)
663663
password_last_changed: Optional[datetime] = field(default=None)
664664
password_next_rotation: Optional[datetime] = field(default=None)
665665
mfa_active: Optional[bool] = field(default=None)

plugins/aws/fix_plugin_aws/resource/rds.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ class AwsRdsInstance(RdsTaggable, AwsResource, BaseDatabase):
464464
rds_db_subnet_group: Optional[AwsRdsDBSubnetGroup] = field(default=None)
465465
rds_preferred_maintenance_window: Optional[str] = field(default=None)
466466
rds_pending_modified_values: Optional[AwsRdsPendingModifiedValues] = field(default=None)
467-
rds_latest_restorable_time: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
467+
rds_latest_restorable_time: Optional[datetime] = field(default=None)
468468
rds_multi_az: Optional[bool] = field(default=None)
469469
rds_auto_minor_version_upgrade: Optional[bool] = field(default=None)
470470
rds_read_replica_source_db_instance_identifier: Optional[str] = field(default=None)
@@ -973,12 +973,12 @@ class AwsRdsCluster(RdsTaggable, AwsResource, BaseDatabase):
973973
rds_db_cluster_parameter_group: Optional[str] = field(default=None)
974974
rds_db_subnet_group_name: Optional[str] = field(default=None)
975975
rds_automatic_restart_time: Optional[datetime] = field(default=None)
976-
rds_earliest_restorable_time: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
976+
rds_earliest_restorable_time: Optional[datetime] = field(default=None)
977977
rds_endpoint: Optional[str] = field(default=None)
978978
rds_reader_endpoint: Optional[str] = field(default=None)
979979
rds_custom_endpoints: List[str] = field(factory=list)
980980
rds_multi_az: Optional[bool] = field(default=None)
981-
rds_latest_restorable_time: Optional[datetime] = field(default=None, metadata=dict(ignore_history=True))
981+
rds_latest_restorable_time: Optional[datetime] = field(default=None)
982982
rds_port: Optional[int] = field(default=None)
983983
rds_master_username: Optional[str] = field(default=None)
984984
rds_db_cluster_option_group_memberships: List[AwsRdsDBClusterOptionGroupStatus] = field(factory=list)

plugins/aws/fix_plugin_aws/resource/secretsmanager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class AwsSecretsManagerSecret(HasResourcePolicy, AwsResource):
6767
rotation_rules: Optional[AwsSecretsManagerRotationRulesType] = field(default=None, metadata={"description": "A structure that defines the rotation configuration for the secret."}) # fmt: skip
6868
last_rotated_date: Optional[datetime] = field(default=None, metadata={"description": "The most recent date and time that the Secrets Manager rotation process was successfully completed. This value is null if the secret hasn't ever rotated."}) # fmt: skip
6969
last_changed_date: Optional[datetime] = field(default=None, metadata={"description": "The last date and time that this secret was modified in any way."}) # fmt: skip
70-
last_accessed_date: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "The date that the secret was last accessed in the Region. This field is omitted if the secret has never been retrieved in the Region."}) # fmt: skip
70+
last_accessed_date: Optional[datetime] = field(default=None, metadata={"description": "The date that the secret was last accessed in the Region. This field is omitted if the secret has never been retrieved in the Region."}) # fmt: skip
7171
deleted_date: Optional[datetime] = field(default=None, metadata={"description": "The date and time the deletion of the secret occurred. Not present on active secrets. The secret can be recovered until the number of days in the recovery window has passed, as specified in the RecoveryWindowInDays parameter of the DeleteSecret operation."}) # fmt: skip
7272
next_rotation_date: Optional[datetime] = field(default=None, metadata={"description": "The next rotation is scheduled to occur on or before this date. If the secret isn't configured for rotation, Secrets Manager returns null."}) # fmt: skip
7373
secret_versions_to_stages: Optional[Dict[str, List[str]]] = field(default=None, metadata={"description": "A list of all of the currently assigned SecretVersionStage staging labels and the SecretVersionId attached to each one. Staging labels are used to keep track of the different versions during the rotation process. A version that does not have any SecretVersionStage is considered deprecated and subject to deletion. Such versions are not included in this list."}) # fmt: skip

plugins/aws/fix_plugin_aws/resource/ssm.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class AwsSSMInstance(AwsResource):
7171
}
7272
instance_id: Optional[str] = field(default=None, metadata={"description": "The managed node ID."}) # fmt: skip
7373
ping_status: Optional[str] = field(default=None, metadata={"description": "Connection status of SSM Agent. The status Inactive has been deprecated and is no longer in use."}) # fmt: skip
74-
last_ping: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "The date and time when the agent last pinged the Systems Manager service."}) # fmt: skip
74+
last_ping: Optional[datetime] = field(default=None, metadata={"description": "The date and time when the agent last pinged the Systems Manager service."}) # fmt: skip
7575
agent_version: Optional[str] = field(default=None, metadata={"description": "The version of SSM Agent running on your Linux managed node."}) # fmt: skip
7676
is_latest_version: Optional[bool] = field(default=None, metadata={"description": "Indicates whether the latest version of SSM Agent is running on your Linux managed node. This field doesn't indicate whether or not the latest version is installed on Windows managed nodes, because some older versions of Windows Server use the EC2Config service to process Systems Manager requests."}) # fmt: skip
7777
platform_type: Optional[str] = field(default=None, metadata={"description": "The operating system platform type."}) # fmt: skip
@@ -84,8 +84,8 @@ class AwsSSMInstance(AwsResource):
8484
ip_address: Optional[str] = field(default=None, metadata={"description": "The IP address of the managed node."}) # fmt: skip
8585
computer_name: Optional[str] = field(default=None, metadata={"description": "The fully qualified host name of the managed node."}) # fmt: skip
8686
association_status: Optional[str] = field(default=None, metadata={"description": "The status of the association."}) # fmt: skip
87-
last_association_execution_date: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "The date the association was last run."}) # fmt: skip
88-
last_successful_association_execution_date: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "The last date the association was successfully run."}) # fmt: skip
87+
last_association_execution_date: Optional[datetime] = field(default=None, metadata={"description": "The date the association was last run."}) # fmt: skip
88+
last_successful_association_execution_date: Optional[datetime] = field(default=None, metadata={"description": "The last date the association was successfully run."}) # fmt: skip
8989
association_overview: Optional[AwsSSMInstanceAggregatedAssociationOverview] = field(default=None, metadata={"description": "Information about the association."}) # fmt: skip
9090
source_id: Optional[str] = field(default=None, metadata={"description": "The ID of the source resource. For IoT Greengrass devices, SourceId is the Thing name."}) # fmt: skip
9191
source_type: Optional[str] = field(default=None, metadata={"description": "The type of the source resource. For IoT Greengrass devices, SourceType is AWS::IoT::Thing."}) # fmt: skip

plugins/azure/fix_plugin_azure/resource/authorization.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class AzureAuthorizationDenyAssignment(MicrosoftResource):
102102
condition: Optional[str] = field(default=None, metadata={'description': 'The conditions on the deny assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase foo_storage_container '}) # fmt: skip
103103
condition_version: Optional[str] = field(default=None, metadata={"description": "Version of the condition."})
104104
created_by: Optional[str] = field(default=None, metadata={'description': 'Id of the user who created the assignment'}) # fmt: skip
105-
created_on: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "Time it was created"}) # fmt: skip
105+
created_on: Optional[datetime] = field(default=None, metadata={"description": "Time it was created"}) # fmt: skip
106106
deny_assignment_name: Optional[str] = field(default=None, metadata={'description': 'The display name of the deny assignment.'}) # fmt: skip
107107
description: Optional[str] = field(default=None, metadata={'description': 'The description of the deny assignment.'}) # fmt: skip
108108
do_not_apply_to_child_scopes: Optional[bool] = field(default=None, metadata={'description': 'Determines if the deny assignment applies to child scopes. Default value is false.'}) # fmt: skip
@@ -112,7 +112,7 @@ class AzureAuthorizationDenyAssignment(MicrosoftResource):
112112
principals: Optional[List[AzurePrincipal]] = field(default=None, metadata={'description': 'Array of principals to which the deny assignment applies.'}) # fmt: skip
113113
scope: Optional[str] = field(default=None, metadata={"description": "The deny assignment scope."})
114114
updated_by: Optional[str] = field(default=None, metadata={"ignore_history": True, 'description': 'Id of the user who updated the assignment'}) # fmt: skip
115-
updated_on: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "Time it was updated"}) # fmt: skip
115+
updated_on: Optional[datetime] = field(default=None, metadata={"description": "Time it was updated"}) # fmt: skip
116116

117117

118118
@define(eq=False, slots=False)
@@ -179,15 +179,15 @@ class AzureAuthorizationRoleAssignment(MicrosoftResource):
179179
condition: Optional[str] = field(default=None, metadata={'description': 'The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase foo_storage_container '}) # fmt: skip
180180
condition_version: Optional[str] = field(default=None, metadata={'description': 'Version of the condition. Currently the only accepted value is 2.0 '}) # fmt: skip
181181
created_by: Optional[str] = field(default=None, metadata={'description': 'Id of the user who created the assignment'}) # fmt: skip
182-
created_on: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "Time it was created"}) # fmt: skip
182+
created_on: Optional[datetime] = field(default=None, metadata={"description": "Time it was created"}) # fmt: skip
183183
delegated_managed_identity_resource_id: Optional[str] = field(default=None, metadata={'description': 'Id of the delegated managed identity resource'}) # fmt: skip
184184
description: Optional[str] = field(default=None, metadata={"description": "Description of role assignment"})
185185
principal_id: Optional[str] = field(default=None, metadata={"description": "The principal ID."})
186186
principal_type: Optional[str] = field(default=None, metadata={'description': 'The principal type of the assigned principal ID.'}) # fmt: skip
187187
role_definition_id: Optional[str] = field(default=None, metadata={"description": "The role definition ID."})
188188
scope: Optional[str] = field(default=None, metadata={"description": "The role assignment scope."})
189189
updated_by: Optional[str] = field(default=None, metadata={"ignore_history": True, 'description': 'Id of the user who updated the assignment'}) # fmt: skip
190-
updated_on: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "Time it was updated"}) # fmt: skip
190+
updated_on: Optional[datetime] = field(default=None, metadata={"description": "Time it was updated"}) # fmt: skip
191191

192192
def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
193193
# role definition
@@ -259,12 +259,12 @@ class AzureAuthorizationRoleDefinition(MicrosoftResource, BaseRole, PhantomBaseR
259259
}
260260
assignable_scopes: Optional[List[str]] = field(default=None, metadata={'description': 'Role definition assignable scopes.'}) # fmt: skip
261261
created_by: Optional[str] = field(default=None, metadata={'description': 'Id of the user who created the assignment'}) # fmt: skip
262-
created_on: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "Time it was created"}) # fmt: skip
262+
created_on: Optional[datetime] = field(default=None, metadata={"description": "Time it was created"}) # fmt: skip
263263
description: Optional[str] = field(default=None, metadata={"description": "The role definition description."})
264264
azure_role_permissions: Optional[List[AzurePermission]] = field(default=None, metadata={'description': 'Role definition permissions.'}) # fmt: skip
265265
role_name: Optional[str] = field(default=None, metadata={"description": "The role name."})
266266
updated_by: Optional[str] = field(default=None, metadata={"ignore_history": True, 'description': 'Id of the user who updated the assignment'}) # fmt: skip
267-
updated_on: Optional[datetime] = field(default=None, metadata={"ignore_history": True, "description": "Time it was updated"}) # fmt: skip
267+
updated_on: Optional[datetime] = field(default=None, metadata={"description": "Time it was updated"}) # fmt: skip
268268

269269

270270
@define(eq=False, slots=False)

0 commit comments

Comments
 (0)