Repository: cloud-custodian/cloud-custodian

Issue:
better rendering for jsonschema on the cli
when running custodian schema we currently do help docs followed by the json schema, the json schema rendering is pretty hard to read though, we should reformat it to a more of a table structure wherever possible. generally we have a pretty flat structure with at most one additional layer from action/filter elements.


Discussion:
@kapilt - 

Here is a basic example of a schema for `s3.actions.invoke-lambda` that has no additional layers:

We translate:

```
Schema
------

{
    "additionalProperties": false, 
    "required": [
        "function"
    ], 
    "type": "object", 
    "properties": {
        "function": {
            "type": "string"
        }, 
        "async": {
            "type": "boolean"
        }, 
        "qualifier": {
            "type": "string"
        }, 
        "batch_size": {
            "type": "integer"
        }
    }
}
```

into:

```
Schema
------

Additional properties allowed: False 

Field       Type     Values    Required    Description
----------  -------  --------  ----------  -------------
async       boolean
batch_size  integer
function    string             yes
qualifier   string
```

Two challenges:

1)

How should we handle nested array/object items?  Here is an example for `s3.actions.auto-tag-user`

Old schema:

```
{
    "additionalProperties": false, 
    "required": [
        "tag", 
        "type"
    ], 
    "type": "object", 
    "properties": {
        "user-type": {
            "items": {
                "enum": [
                    "IAMUser", 
                    "AssumedRole", 
                    "FederatedUser"
                ], 
                "type": "string"
            }, 
            "type": "array"
        }, 
        "tag": {
            "type": "string"
        }, 
        "update": {
            "type": "boolean"
        }, 
        "principal_id_tag": {
            "type": "string"
        }
    }
}
```

New schema:

```
Schema
------

Additional properties allowed: False 

Field             Type     Values            Required    Description
----------------  -------  ----------------  ----------  -------------
principal_id_tag  string
tag               string                     yes
update            boolean
user-type         array    See schema below
```

I was thinking we could have another table below for each item that is an array/object.  Currently most/all of the array items are of string type, but the S3 lifecycle branch will change that as it has an array of objects.

2)

What do we do when there is a huge list of enums?  e.g. for `s3.actions.put-metric`

```
Schema
------

Additional properties allowed: False 

Field        Type    Values                                                                                                                                                                                                                                                                                                                                           Required    Description
-----------  ------  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  ----------  -------------
dimensions   array   See schema below
key          string                                                                                                                                                                                                                                                                                                                                                   yes
metric_name  string                                                                                                                                                                                                                                                                                                                                                   yes
namespace    string                                                                                                                                                                                                                                                                                                                                                   yes
op                   count, average, distinct_count, sum
units                Seconds, Microseconds, Milliseconds, Bytes, Kilobytes, Megabytes, Gigabytes, Terabytes, Bits, Kilobits, Megabits, Gigabits, Terabits, Bytes/Second, Kilobytes/Second, Megabytes/Second, Gigabytes/Second, Terabytes/Second, Bits/Second, Kilobits/Second, Megabits/Second, Gigabits/Second, Terabits/Second, Count/Second, Percent, Count, None
```

The enum list is enormous.  We could do something similar where if the enum list is longer than X (perhaps either 5 items, or 40 characters) we add a note to "see list below" and print it below the table.
Alternative idea...instead of trying to make a pretty display on the command line we could:

1) Print a URL to the web page with the schema
2) Don't print the schema on the command line anymore (in order to make the link more obvious), but still print it on demand in the old format if `--json` is supplied

Resolving patch:
diff --git a/c7n/commands.py b/c7n/commands.py
--- a/c7n/commands.py
+++ b/c7n/commands.py
@@ -453,7 +453,10 @@ def schema_cmd(options):
         # Print schema
         print("\nSchema\n------\n")
         if hasattr(cls, 'schema'):
-            print(json.dumps(cls.schema, indent=4))
+            component_schema = dict(cls.schema)
+            component_schema.pop('additionalProperties', None)
+            component_schema.pop('type', None)
+            print(yaml.safe_dump(component_schema))
         else:
             # Shouldn't ever hit this, so exclude from cover
             print("No schema is available for this item.", file=sys.sterr)  # pragma: no cover


Test patch: