Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions library_generation/cli/entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys

import click as click
from library_generation.generate_pr_description import generate_pr_descriptions
Expand Down Expand Up @@ -142,5 +143,28 @@ def generate(
)


@main.command()
@click.option(
"--generation-config-path",
required=False,
type=str,
help="""
Absolute or relative path to a generation_config.yaml.
Default to generation_config.yaml in the current working directory.
""",
)
def validate_generation_config(generation_config_path: str) -> None:
"""
Validate the given generation configuration.
"""
if generation_config_path is None:
generation_config_path = "generation_config.yaml"
try:
from_yaml(os.path.abspath(generation_config_path))
except ValueError as err:
print(err)
sys.exit(1)


if __name__ == "__main__":
main()
51 changes: 41 additions & 10 deletions library_generation/model/generation_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
from library_generation.model.library_config import LibraryConfig
from library_generation.model.gapic_config import GapicConfig

REPO_LEVEL_PARAMETER = "Repo level parameter"
LIBRARY_LEVEL_PARAMETER = "Library level parameter"
GAPIC_LEVEL_PARAMETER = "GAPIC level parameter"


class GenerationConfig:
"""
Expand Down Expand Up @@ -46,6 +50,7 @@ def __init__(
self.libraries = libraries
self.grpc_version = grpc_version
self.protoc_version = protoc_version
self.__validate()

def get_proto_path_to_library_name(self) -> dict[str, str]:
"""
Expand All @@ -62,6 +67,19 @@ def get_proto_path_to_library_name(self) -> dict[str, str]:
def is_monorepo(self) -> bool:
return len(self.libraries) > 1

def __validate(self) -> None:
seen_library_names = dict()
for library in self.libraries:
library_name = library.get_library_name()
if library_name in seen_library_names:
raise ValueError(
f"Both {library.name_pretty} and "
f"{seen_library_names.get(library_name)} have the same "
f"library name: {library_name}, please update one of the "
f"library to have a different library name."
)
seen_library_names[library_name] = library.name_pretty


def from_yaml(path_to_yaml: str) -> GenerationConfig:
"""
Expand All @@ -72,15 +90,15 @@ def from_yaml(path_to_yaml: str) -> GenerationConfig:
with open(path_to_yaml, "r") as file_stream:
config = yaml.safe_load(file_stream)

libraries = __required(config, "libraries")
libraries = __required(config, "libraries", REPO_LEVEL_PARAMETER)

parsed_libraries = list()
for library in libraries:
gapics = __required(library, "GAPICs")

parsed_gapics = list()
for gapic in gapics:
proto_path = __required(gapic, "proto_path")
proto_path = __required(gapic, "proto_path", GAPIC_LEVEL_PARAMETER)
new_gapic = GapicConfig(proto_path)
parsed_gapics.append(new_gapic)

Expand Down Expand Up @@ -114,23 +132,36 @@ def from_yaml(path_to_yaml: str) -> GenerationConfig:
parsed_libraries.append(new_library)

parsed_config = GenerationConfig(
gapic_generator_version=__required(config, "gapic_generator_version"),
gapic_generator_version=__required(
config, "gapic_generator_version", REPO_LEVEL_PARAMETER
),
grpc_version=__optional(config, "grpc_version", None),
protoc_version=__optional(config, "protoc_version", None),
googleapis_commitish=__required(config, "googleapis_commitish"),
libraries_bom_version=__required(config, "libraries_bom_version"),
owlbot_cli_image=__required(config, "owlbot_cli_image"),
synthtool_commitish=__required(config, "synthtool_commitish"),
template_excludes=__required(config, "template_excludes"),
googleapis_commitish=__required(
config, "googleapis_commitish", REPO_LEVEL_PARAMETER
),
libraries_bom_version=__required(
config, "libraries_bom_version", REPO_LEVEL_PARAMETER
),
owlbot_cli_image=__required(config, "owlbot_cli_image", REPO_LEVEL_PARAMETER),
synthtool_commitish=__required(
config, "synthtool_commitish", REPO_LEVEL_PARAMETER
),
template_excludes=__required(config, "template_excludes", REPO_LEVEL_PARAMETER),
libraries=parsed_libraries,
)

return parsed_config


def __required(config: Dict, key: str):
def __required(config: Dict, key: str, level: str = LIBRARY_LEVEL_PARAMETER):
if key not in config:
raise ValueError(f"required key {key} not found in yaml")
message = (
f"{level}, {key}, is not found in {config} in yaml."
if level != REPO_LEVEL_PARAMETER
else f"{level}, {key}, is not found in yaml."
)
raise ValueError(message)
return config[key]


Expand Down
35 changes: 34 additions & 1 deletion library_generation/test/cli/entry_point_unit_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import unittest
from click.testing import CliRunner
from library_generation.cli.entry_point import generate
from library_generation.cli.entry_point import generate, validate_generation_config

script_dir = os.path.dirname(os.path.realpath(__file__))
test_resource_dir = os.path.join(script_dir, "..", "resources", "test-config")


class EntryPointTest(unittest.TestCase):
Expand Down Expand Up @@ -44,3 +48,32 @@ def test_entry_point_with_baseline_without_current_raise_file_exception(self):
"current_generation_config is not specified when "
"baseline_generation_config is specified.",
)

def test_validate_generation_config_succeeds(
self,
):
runner = CliRunner()
# noinspection PyTypeChecker
result = runner.invoke(
validate_generation_config,
[f"--generation-config-path={test_resource_dir}/generation_config.yaml"],
)
self.assertEqual(0, result.exit_code)

def test_validate_generation_config_with_duplicate_library_name_raise_file_exception(
self,
):
runner = CliRunner()
# noinspection PyTypeChecker
result = runner.invoke(
validate_generation_config,
[
f"--generation-config-path={test_resource_dir}/generation_config_with_duplicate_library_name.yaml"
],
)
self.assertEqual(1, result.exit_code)
self.assertEqual(SystemExit, result.exc_info[0])
self.assertRegex(
result.output,
"have the same library name",
)
134 changes: 134 additions & 0 deletions library_generation/test/model/generation_config_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,137 @@ def test_is_monorepo_with_two_libraries_returns_true(self):
libraries=[library_1, library_2],
)
self.assertTrue(config.is_monorepo())

def test_validate_with_duplicate_library_name_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"the same library name",
GenerationConfig,
gapic_generator_version="",
googleapis_commitish="",
libraries_bom_version="",
owlbot_cli_image="",
synthtool_commitish="",
template_excludes=[],
libraries=[
LibraryConfig(
api_shortname="secretmanager",
name_pretty="Secret API",
product_documentation="",
api_description="",
gapic_configs=list(),
),
LibraryConfig(
api_shortname="another-secret",
name_pretty="Another Secret API",
product_documentation="",
api_description="",
gapic_configs=list(),
library_name="secretmanager",
),
],
)

def test_from_yaml_without_gapic_generator_version_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Repo level parameter, gapic_generator_version",
from_yaml,
f"{test_config_dir}/config_without_generator.yaml",
)

def test_from_yaml_without_googleapis_commitish_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Repo level parameter, googleapis_commitish",
from_yaml,
f"{test_config_dir}/config_without_googleapis.yaml",
)

def test_from_yaml_without_libraries_bom_version_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Repo level parameter, libraries_bom_version",
from_yaml,
f"{test_config_dir}/config_without_libraries_bom_version.yaml",
)

def test_from_yaml_without_owlbot_cli_image_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Repo level parameter, owlbot_cli_image",
from_yaml,
f"{test_config_dir}/config_without_owlbot.yaml",
)

def test_from_yaml_without_synthtool_commitish_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Repo level parameter, synthtool_commitish",
from_yaml,
f"{test_config_dir}/config_without_synthtool.yaml",
)

def test_from_yaml_without_template_excludes_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Repo level parameter, template_excludes",
from_yaml,
f"{test_config_dir}/config_without_temp_excludes.yaml",
)

def test_from_yaml_without_libraries_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Repo level parameter, libraries",
from_yaml,
f"{test_config_dir}/config_without_libraries.yaml",
)

def test_from_yaml_without_api_shortname_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Library level parameter, api_shortname",
from_yaml,
f"{test_config_dir}/config_without_api_shortname.yaml",
)

def test_from_yaml_without_api_description_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Library level parameter, api_description",
from_yaml,
f"{test_config_dir}/config_without_api_description.yaml",
)

def test_from_yaml_without_name_pretty_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Library level parameter, name_pretty",
from_yaml,
f"{test_config_dir}/config_without_name_pretty.yaml",
)

def test_from_yaml_without_product_documentation_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Library level parameter, product_documentation",
from_yaml,
f"{test_config_dir}/config_without_product_docs.yaml",
)

def test_from_yaml_without_gapics_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"Library level parameter, GAPICs",
from_yaml,
f"{test_config_dir}/config_without_gapics.yaml",
)

def test_from_yaml_without_proto_path_raise_exception(self):
self.assertRaisesRegex(
ValueError,
"GAPIC level parameter, proto_path",
from_yaml,
f"{test_config_dir}/config_without_proto_path.yaml",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
gapic_generator_version: 2.34.0
protoc_version: 25.2
googleapis_commitish: 1a45bf7393b52407188c82e63101db7dc9c72026
libraries_bom_version: 26.37.0
owlbot_cli_image: sha256:623647ee79ac605858d09e60c1382a716c125fb776f69301b72de1cd35d49409
synthtool_commitish: 6612ab8f3afcd5e292aecd647f0fa68812c9f5b5
template_excludes:
- ".github/*"
- ".kokoro/*"
- "samples/*"
- "CODE_OF_CONDUCT.md"
- "CONTRIBUTING.md"
- "LICENSE"
- "SECURITY.md"
- "java.header"
- "license-checks.xml"
- "renovate.json"
- ".gitignore"
libraries:
- api_shortname: cloudasset
name_pretty: Cloud Asset
product_documentation: "https://cloud.google.com/resource-manager/docs/cloud-asset-inventory/overview"
api_description: "provides inventory services based on a time series database."
library_name: "asset"
client_documentation: "https://cloud.google.com/java/docs/reference/google-cloud-asset/latest/overview"
distribution_name: "com.google.cloud:google-cloud-asset"
release_level: "stable"
issue_tracker: "https://issuetracker.google.com/issues/new?component=187210&template=0"
api_reference: "https://cloud.google.com/resource-manager/docs/cloud-asset-inventory/overview"
codeowner_team: "@googleapis/analytics-dpe"
excluded_poms: proto-google-iam-v1-bom,google-iam-policy,proto-google-iam-v1
excluded_dependencies: google-iam-policy
GAPICs:
- proto_path: google/cloud/asset/v1
- proto_path: google/cloud/asset/v1p1beta1
- proto_path: google/cloud/asset/v1p2beta1
- proto_path: google/cloud/asset/v1p5beta1
- proto_path: google/cloud/asset/v1p7beta1

- api_shortname: another-cloudasset
name_pretty: Cloud Asset Inventory
product_documentation: "https://cloud.google.com/resource-manager/docs/cloud-asset-inventory/overview"
api_description: "provides inventory services based on a time series database."
library_name: "asset"
client_documentation: "https://cloud.google.com/java/docs/reference/google-cloud-asset/latest/overview"
distribution_name: "com.google.cloud:google-cloud-asset"
release_level: "stable"
issue_tracker: "https://issuetracker.google.com/issues/new?component=187210&template=0"
api_reference: "https://cloud.google.com/resource-manager/docs/cloud-asset-inventory/overview"
GAPICs:
- proto_path: google/cloud/asset/v1
Loading