1# Copyright 2020 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import glob 16import re 17import os 18import yaml 19from typing import List, Dict 20from synthtool.log import logger 21 22 23def _read_sample_metadata_comment(sample_file: str) -> Dict: 24 """Additional meta-information can be provided through embedded comments: 25 26 // sample-metadata: 27 // title: ACL (Access Control) 28 // description: Demonstrates setting access control rules. 29 // usage: node iam.js --help 30 """ 31 sample_metadata = {} # type: Dict[str, str] 32 with open(sample_file) as f: 33 contents = f.read() 34 match = re.search( 35 r"(?P<metadata>// *sample-metadata:([^\n]+|\n//)+)", contents, re.DOTALL 36 ) 37 if match: 38 # the metadata yaml is stored in a comments, remove the 39 # prefix so that we can parse the yaml contained. 40 sample_metadata_string = re.sub(r"((#|//) ?)", "", match.group("metadata")) 41 try: 42 sample_metadata = yaml.load( 43 sample_metadata_string, Loader=yaml.SafeLoader 44 )["sample-metadata"] 45 except yaml.scanner.ScannerError: 46 # warn and continue on bad metadata 47 logger.warning(f"bad metadata detected in {sample_file}") 48 return sample_metadata 49 50 51def _sample_metadata(file: str) -> Dict[str, str]: 52 metadata = { 53 "title": _decamelize(os.path.splitext(os.path.basename(file))[0]), 54 "file": file, 55 } 56 return {**metadata, **_read_sample_metadata_comment(file)} 57 58 59def all_samples(sample_globs: List[str]) -> List[Dict[str, str]]: 60 """Walks samples directory and builds up samples data-structure 61 62 Args: 63 sample_globs: (List[str]): List of path globs to search for samples 64 65 Returns: 66 A list of sample metadata in the format: 67 { 68 "title": "Requester Pays", 69 "file": "samples/requesterPays.js" 70 } 71 The file path is the relative path from the repository root. 72 """ 73 files = [] 74 for sample_glob in sample_globs: 75 for file in glob.glob(sample_glob, recursive=True): 76 files.append(file) 77 return [_sample_metadata(file) for file in sorted(files)] 78 79 80def _decamelize(value: str): 81 """Parser to convert fooBar.js to Foo Bar.""" 82 if not value: 83 return "" 84 str_decamelize = re.sub("^.", value[0].upper(), value) # apple -> Apple. 85 str_decamelize = re.sub( 86 "([A-Z]+)([A-Z])([a-z0-9])", r"\1 \2\3", str_decamelize 87 ) # ACLBatman -> ACL Batman. 88 return re.sub("([a-z0-9])([A-Z])", r"\1 \2", str_decamelize) # FooBar -> Foo Bar. 89