Repository: aws-cloudformation/cfn-lint

Issue:
Failure to parse custom registry schemas with referenced types 
### CloudFormation Lint Version

cfn-lint 0.86.4

### What operating system are you using?

Mac

### Describe the bug

CFN Lint fails to parse custom registry schemas when these schemas include referenced properties.

In our case, we need to validate CloudFormation templates with DataDog Monitors. DataDog Monitors Schema can be found [here](https://github.com/DataDog/datadog-cloudformation-resources/blob/master/datadog-monitors-monitor-handler/datadog-monitors-monitor.json) so we downloaded the schema and executed:

```
cfn-lint cfn_template.yml --registry-schemas <SCHEMAS_FOLDER>
```

where `<SCHEMAS_FOLDER>` is the folder where the schema has been downloaded.

After doing that, we receive the following error:

```
E0002 Unknown exception while processing rule E1010: 'type'
cfn_schema/datadog-monitors-monitor.json:1:1

E0002 Unknown exception while processing rule E1029: 'type'
cfn_schema/datadog-monitors-monitor.json:1:1
```

This error arises because the definition of the schema includes references to data types defined in the `definitions` section. If we modify the schema and "de-refer" the references, this error disappears.

### Expected behavior

We expect `cnf-lint` to work seamlessly with schemas which contains references to data types.  We expect `cfn-lint` to validate DataDog Monitor resources with the definition provided by the provider. 

### Reproduction template

```yaml
AWSTemplateFormatVersion: "2010-09-09"

Resources:
  DatadogMonitorLambda1Errors:
    Type: "Datadog::Monitors::Monitor"
    Properties:
      Type: "metric alert"
      Name: "example"
      Query: "sum(last_5m):avg:aws.lambda.errors > 1"
      Message: "example"
      Tags:
        - "tag1"
        - "tag2"
      Options:
        EnableLogsSample: true
        NotifyAudit: false
        Locked: false
        TimeoutH: 0
        NewHostDelay: 300
        RequireFullWindow: false
        RenotifyInterval: 0
        Thresholds:
          Critical: 0
        NotifyNoData: false
        EvaluationDelay: 900
        IncludeTags: true
      Priority: 1
```


Discussion:

Resolving patch:
diff --git a/src/cfnlint/template/template.py b/src/cfnlint/template/template.py
--- a/src/cfnlint/template/template.py
+++ b/src/cfnlint/template/template.py
@@ -351,6 +351,46 @@ def get_valid_refs(self):
             results[pseudoparam] = element
         return results
 
+    def _get_valid_getatts_from_schema(self, schema, pointer):
+        results = {}
+        try:
+            item = resolve_pointer(schema, pointer)
+        except KeyError:
+            return results
+
+        pointer = pointer.replace("/properties/", "").replace("#/definitions/", "")
+
+        item_type = item.get("type")
+        if item_type is None:
+            if "$ref" in item:
+                results.update(
+                    self._get_valid_getatts_from_schema(schema, item.get("$ref"))
+                )
+            else:
+                return results
+        _type = None
+        primitive_type = None
+        if item_type == "string":
+            primitive_type = "String"
+        elif item_type == "number":
+            primitive_type = "Double"
+        elif item_type == "integer":
+            primitive_type = "Integer"
+        elif item_type == "boolean":
+            primitive_type = "Boolean"
+        elif item_type == "array":
+            _type = "List"
+            primitive_type = "String"
+
+        results[".".join(pointer.split("/"))] = {}
+        if _type:
+            results[".".join(pointer.split("/"))]["Type"] = _type
+            results[".".join(pointer.split("/"))]["PrimitiveItemType"] = primitive_type
+        elif primitive_type:
+            results[".".join(pointer.split("/"))]["PrimitiveType"] = primitive_type
+
+        return results
+
     # pylint: disable=too-many-locals
     def get_valid_getatts(self):
         resourcetypes = cfnlint.helpers.RESOURCE_SPECS["us-east-1"].get("ResourceTypes")
@@ -424,40 +464,11 @@ def build_output_string(resource_type, property_name):
                             if value["Type"] == schema["typeName"]:
                                 results[name] = {}
                                 for ro_property in schema.get("readOnlyProperties", []):
-                                    try:
-                                        item = resolve_pointer(schema, ro_property)
-                                    except KeyError:
-                                        continue
-                                    item_type = item["type"]
-                                    _type = None
-                                    primitive_type = None
-                                    if item_type == "string":
-                                        primitive_type = "String"
-                                    elif item_type == "number":
-                                        primitive_type = "Double"
-                                    elif item_type == "integer":
-                                        primitive_type = "Integer"
-                                    elif item_type == "boolean":
-                                        primitive_type = "Boolean"
-                                    elif item_type == "array":
-                                        _type = "List"
-                                        primitive_type = "String"
-
-                                    ro_property = ro_property.replace(
-                                        "/properties/", ""
+                                    results[name].update(
+                                        self._get_valid_getatts_from_schema(
+                                            schema, ro_property
+                                        )
                                     )
-                                    results[name][".".join(ro_property.split("/"))] = {}
-                                    if _type:
-                                        results[name][".".join(ro_property.split("/"))][
-                                            "Type"
-                                        ] = _type
-                                        results[name][".".join(ro_property.split("/"))][
-                                            "PrimitiveItemType"
-                                        ] = primitive_type
-                                    elif primitive_type:
-                                        results[name][".".join(ro_property.split("/"))][
-                                            "PrimitiveType"
-                                        ] = primitive_type
 
         return results
 


Test patch: