Repository: aws-cloudformation/cfn-lint

Issue:
DatabaseCluster.Endpoint.Address not recognized after --update-specs
### CloudFormation Lint Version

0.72.1, 0.72.2

### What operating system are you using?

Linux

### Describe the bug

When we use ``cfn-lint --update-specs``, the Endpoint.Address of the AWS::RDS::DBCluster isn't recognized anymore. This worked up to about 7 days ago.

Reproducing it is simple:
``docker run -it docker:20.10.12-alpine3.14``

In the Docker container:

```
apk add python3=~3.9
python3 -m ensurepip
pip3 install cfn-lint==0.72.2

<<Copy/paste below template into tst.yaml>>

cfn-lint --template tst.yaml
```

No output, works correctly

```
cfn-lint --update-specs
cfn-lint --template tst.yaml
   E1010 Invalid GetAtt DatabaseCluster.Endpoint.Address for resource MyCustomResource
   tst.yaml:33:9
```

According to the documentation, this should (still) work: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbcluster.html

Replacing 0.72.2 by 0.72.1 doesn't solve the issue.


### Expected behavior

We would expect the template to pass without errors

### Reproduction template

```
Parameters:
  Pwd:
    Type: String
    NoEcho: true
    Default: "MyPa$$Word"

Resources:
  DatabaseCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      Engine: aurora-mysql
      EngineMode: serverless
      ScalingConfiguration:
        MinCapacity: 1
        MaxCapacity: 2
        AutoPause: false
      StorageEncrypted: true
      KmsKeyId: "whatever"
      DatabaseName: whatever
      MasterUsername: whatever
      MasterUserPassword: !Ref Pwd
      BackupRetentionPeriod: 30
      DBSubnetGroupName: "whatever"
      DBClusterParameterGroupName: default.aurora-mysql5.7
      VpcSecurityGroupIds:
        - "whatever"

  MyCustomResource:
    Type: Custom::MyCustomResource
    Properties:
      PasswordParameterName: '/databasecluster/password'
      Database:
        Host: !GetAtt DatabaseCluster.Endpoint.Address
        Port: 3306
        PasswordParameterName: '/database/password'
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyCustomResource'
```


Discussion:
There was some changes in the most recent version of the spec that is causing me some issues.  I'm thinking about how I want to handle it.  Looks like we have added custom types to attributes.  `Endpoint.Address` is now `Endpoint` with a custom type.  I need to work this logic for properties that have a type Map that also accept the dot notation.  Working on a fix and I'll tried to get it patched.

```json
"AWS::RDS::DBCluster": {
   "Attributes": {
    "DBClusterArn": {
     "PrimitiveType": "String"
    },
    "DBClusterResourceId": {
     "PrimitiveType": "String"
    },
    "Endpoint": {
     "Type": "Endpoint"
    },
    "Endpoint.Address": {
     "PrimitiveType": "String"
    },
    "Endpoint.Port": {
     "PrimitiveType": "String"
    },
    "ReadEndpoint.Address": {
     "PrimitiveType": "String"
    }
   },
```

```json
"AWS::RDS::DBCluster.Endpoint": {
   "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-dbcluster-endpoint.html",
   "Properties": {
    "Address": {
     "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-dbcluster-endpoint.html#cfn-rds-dbcluster-endpoint-address",
     "PrimitiveType": "String",
     "Required": false,
     "UpdateType": "Mutable"
    },
    "Port": {
     "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-dbcluster-endpoint.html#cfn-rds-dbcluster-endpoint-port",
     "PrimitiveType": "String",
     "Required": false,
     "UpdateType": "Mutable"
    }
   }
  },
```

Resolving patch:
diff --git a/src/cfnlint/template.py b/src/cfnlint/template.py
--- a/src/cfnlint/template.py
+++ b/src/cfnlint/template.py
@@ -223,6 +223,7 @@ def get_valid_refs(self):
 
     def get_valid_getatts(self):
         resourcetypes = cfnlint.helpers.RESOURCE_SPECS["us-east-1"].get("ResourceTypes")
+        propertytypes = cfnlint.helpers.RESOURCE_SPECS["us-east-1"].get("PropertyTypes")
         results = {}
         resources = self.template.get("Resources", {})
 
@@ -233,6 +234,18 @@ def get_valid_getatts(self):
             "AWS::CloudFormation::CustomResource",
         )
 
+        def build_output_string(resource_type, property_name):
+            prop = propertytypes.get(f"{resource_type}.{property_name}")
+            if prop is None:
+                yield None, None
+            for k, v in prop.get("Properties", {}).items():
+                t = v.get("Type")
+                if t:
+                    for item in build_output_string(resource_type, v):
+                        yield f"{k}.{item[0]}", item[1]
+                else:
+                    yield k, v.get("PrimitiveType")
+
         for name, value in resources.items():
             if "Type" in value:
                 valtype = value["Type"]
@@ -256,9 +269,25 @@ def get_valid_getatts(self):
                                 for attname, attvalue in resourcetypes[valtype][
                                     "Attributes"
                                 ].items():
-                                    element = {}
-                                    element.update(attvalue)
-                                    results[name][attname] = element
+                                    if "Type" in attvalue:
+                                        if attvalue.get("Type") in ["List", "Map"]:
+                                            element = {}
+                                            element.update(attvalue)
+                                            results[name][attname] = element
+                                        else:
+                                            for item in build_output_string(
+                                                value["Type"], attname
+                                            ):
+                                                if item[0] is None:
+                                                    continue
+                                                element = {"PrimitiveType": item[1]}
+                                                results[name][
+                                                    f"{attname}.{item[0]}"
+                                                ] = element
+                                    else:
+                                        element = {}
+                                        element.update(attvalue)
+                                        results[name][attname] = element
 
         return results
 


Test patch: