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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ integ-test:

black:
black setup.py samtranslator/* tests/* integration/* bin/*.py
bin/json-format.py --write tests

black-check:
black --check setup.py samtranslator/* tests/* integration/* bin/*.py
bin/json-format.py --check tests

lint:
# Linter performs static analysis to catch latent bugs
pylint --rcfile .pylintrc samtranslator
# mypy performs type check
mypy samtranslator bin/add_transform_test.py
mypy samtranslator bin/add_transform_test.py bin/json-format.py

prepare-companion-stack:
pytest -v --no-cov integration/setup -m setup
Expand Down
105 changes: 105 additions & 0 deletions bin/json-format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python
"""JSON file formatter (without prettier)."""
import argparse
import json
import os.path
import sys


def format_json(json_str: str) -> str:
"""Opinionated format JSON file."""
obj = json.loads(json_str)
return json.dumps(obj, indent=2, sort_keys=True) + "\n"


class JSONFormatter:
check: bool
write: bool

scanned_file_found: int
unformatted_file_count: int

def __init__(self, check: bool, write: bool) -> None:
self.check = check
self.write = write

self.scanned_file_found = 0
self.unformatted_file_count = 0

def process_file(self, file_path: str) -> None:
with open(file_path, "r", encoding="utf-8") as f:
json_str = f.read()
try:
formatted_json_str = format_json(json_str)
except json.JSONDecodeError as error:
raise ValueError(f"{file_path}: Invalid JSON") from error
if json_str != formatted_json_str:
if self.write:
with open(file_path, "w", encoding="utf-8") as f:
f.write(formatted_json_str)
print(f"reformatted {file_path}")
if self.check:
print(f"would reformat {file_path}")
self.unformatted_file_count += 1
self.scanned_file_found += 1

def process_directory(self, directory_path: str) -> None:
for root, dirs, files in os.walk(directory_path):
for file in files:
file_path = os.path.join(root, file)
_, extension = os.path.splitext(file_path)
if extension != ".json":
continue
self.process_file(file_path)

def output_summary(self):
print(f"{self.scanned_file_found} file(s) scanned.")
if self.write:
print(f"{self.unformatted_file_count} file(s) reformatted.")
if self.check:
print(f"{self.unformatted_file_count} file(s) need reformat.")
if self.unformatted_file_count:
sys.exit(-1)


def main() -> None:
parser = argparse.ArgumentParser(description="JSON file formatter.")
parser.add_argument(
"paths", metavar="file|dir", type=str, nargs="+", help="JSON file or directory containing JSON files"
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-c",
"--check",
action="store_true",
help="Check if the given files are formatted, "
"print a human-friendly summary message and paths to un-formatted files",
)
group.add_argument(
"-w",
"--write",
action="store_true",
help="Edit files in-place. (Beware!)",
)

args = parser.parse_args()
formatter = JSONFormatter(args.check, args.write)

for path in args.paths:
if not os.path.exists(path):
raise ValueError(f"{path}: No such file or directory")
if os.path.isfile(path):
_, extension = os.path.splitext(path)
if extension != ".json":
raise ValueError(f"{path}: Not a JSON file")
formatter.process_file(path)
elif os.path.isdir(path):
formatter.process_directory(path)
else:
raise ValueError(f"{path}: Unsupported path")

formatter.output_summary()


if __name__ == "__main__":
main()
62 changes: 45 additions & 17 deletions tests/feature_toggle/input/feature_toggle_config.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
{
"__note__": "This is a dummy config for local testing. Any change here need to be migrated to SAM service.",
"feature-1": {
"beta": {
"us-west-2": {"type": "toggle", "enabled": true},
"us-east-1": {"type": "account-percentile", "enabled-%": 10},
"default": {"type": "toggle", "enabled": false},
"123456789123": {
"us-west-2": {"type": "toggle", "enabled": true},
"default": {"type": "toggle", "enabled": false}
}
"__note__": "This is a dummy config for local testing. Any change here need to be migrated to SAM service.",
"feature-1": {
"beta": {
"123456789123": {
"default": {
"enabled": false,
"type": "toggle"
},
"gamma": {
"default": {"type": "toggle", "enabled": false},
"123456789123": {
"us-east-1": {"type": "toggle", "enabled": false},
"default": {"type": "toggle", "enabled": false}
}
"us-west-2": {
"enabled": true,
"type": "toggle"
}
},
"default": {
"enabled": false,
"type": "toggle"
},
"us-east-1": {
"enabled-%": 10,
"type": "account-percentile"
},
"us-west-2": {
"enabled": true,
"type": "toggle"
}
},
"gamma": {
"123456789123": {
"default": {
"enabled": false,
"type": "toggle"
},
"prod": {"default": {"enabled": false}}
"us-east-1": {
"enabled": false,
"type": "toggle"
}
},
"default": {
"enabled": false,
"type": "toggle"
}
},
"prod": {
"default": {
"enabled": false
}
}
}
}
96 changes: 48 additions & 48 deletions tests/translator/output/alexa_skill.json
Original file line number Diff line number Diff line change
@@ -1,72 +1,72 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {},
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {},
"Resources": {
"AlexaSkillFuncRole": {
"Type": "AWS::IAM::Role",
"AlexaSkillFunc": {
"Properties": {
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "hello.zip"
},
"Description": "Created by SAM",
"Handler": "index.handler",
"MemorySize": 1024,
"Role": {
"Fn::GetAtt": [
"AlexaSkillFuncRole",
"Arn"
]
},
"Runtime": "nodejs12.x",
"Tags": [
{
"Value": "SAM",
"Key": "lambda:createdBy"
"Key": "lambda:createdBy",
"Value": "SAM"
}
],
"Timeout": 3
},
"Type": "AWS::Lambda::Function"
},
"AlexaSkillFuncAlexaSkillEventPermission": {
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Ref": "AlexaSkillFunc"
},
"Principal": "alexa-appkit.amazon.com"
},
"Type": "AWS::Lambda::Permission"
},
"AlexaSkillFuncRole": {
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
],
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
}
}
]
}
}
},
"AlexaSkillFuncAlexaSkillEventPermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Ref": "AlexaSkillFunc"
},
"Principal": "alexa-appkit.amazon.com"
}
},
"AlexaSkillFunc": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": "sam-demo-bucket",
"S3Key": "hello.zip"
},
"Description": "Created by SAM",
],
"Version": "2012-10-17"
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Tags": [
{
"Value": "SAM",
"Key": "lambda:createdBy"
"Key": "lambda:createdBy",
"Value": "SAM"
}
],
"MemorySize": 1024,
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"AlexaSkillFuncRole",
"Arn"
]
},
"Timeout": 3,
"Runtime": "nodejs12.x"
}
]
},
"Type": "AWS::IAM::Role"
}
}
}
Loading