Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generate selected libraries #2598

Merged
merged 10 commits into from
Mar 26, 2024
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 53 additions & 18 deletions library_generation/generate_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import click
import library_generation.utilities as util
import click
import os
from library_generation.generate_composed_library import generate_composed_library
from library_generation.model.generation_config import GenerationConfig
from library_generation.model.generation_config import from_yaml
from library_generation.model.library_config import LibraryConfig
from library_generation.utils.monorepo_postprocessor import monorepo_postprocessing


Expand All @@ -39,13 +41,22 @@ def main(ctx):
""",
)
@click.option(
"--target-library-api-shortname",
"--target-library-names",
required=False,
default=None,
type=str,
help="""
If specified, only the `library` whose api_shortname equals to
target-library-api-shortname will be generated.
A list of libraries will be generated.

If specified, only the `library` whose library_name is in
target-library-names will be generated.
If not specified, all libraries in the configuration yaml will be generated.

The input string will be parsed to a list of string with comma as the
separator.

For example, apigeeconnect,alloydb-connectors will be parsed as a
list of two strings, apigeeconnect and alloydb-connectors.
""",
)
@click.option(
Expand All @@ -61,39 +72,46 @@ def main(ctx):
)
def generate(
generation_config_yaml: str,
target_library_api_shortname: str,
target_library_names: str,
repository_path: str,
):
generate_from_yaml(
generation_config_yaml=generation_config_yaml,
repository_path=repository_path,
target_library_api_shortname=target_library_api_shortname,
target_library_names=target_library_names.split(",")
if target_library_names is not None
else target_library_names,
)


def generate_from_yaml(
generation_config_yaml: str,
repository_path: str,
target_library_api_shortname: str = None,
target_library_names: list[str] = None,
) -> None:
"""
Parses a config yaml and generates libraries via
generate_composed_library.py
:param generation_config_yaml: Path to generation_config.yaml that contains
the metadata about library generation
:param repository_path: If specified, the generated files will be sent to
this location. If not specified, the repository will be generated to the
current working directory.
:param target_library_names: a list of libraries to be generated.
If specified, only the library whose library_name is in
target-library-names will be generated.
If specified with an empty list, then no library will be generated.
If not specified, all libraries in the configuration yaml will be generated.
"""
# convert paths to absolute paths so they can be correctly referenced in
# convert paths to absolute paths, so they can be correctly referenced in
# downstream scripts
generation_config_yaml = os.path.abspath(generation_config_yaml)
repository_path = os.path.abspath(repository_path)

config = from_yaml(generation_config_yaml)
target_libraries = config.libraries
if target_library_api_shortname is not None:
target_libraries = [
library
for library in config.libraries
if library.api_shortname == target_library_api_shortname
]

target_libraries = get_target_libraries(
config=config, target_library_names=target_library_names
)
repo_config = util.prepare_repo(
gen_config=config, library_config=target_libraries, repo_path=repository_path
)
Expand All @@ -118,5 +136,22 @@ def generate_from_yaml(
)


if __name__ == "__main__":
main()
def get_target_libraries(
config: GenerationConfig, target_library_names: list[str] = None
) -> list[LibraryConfig]:
"""
Returns LibraryConfig objects whose library_name is in target_library_names.

:param config: a GenerationConfig object.
:param target_library_names: library_name of target libraries.
If not specified, all libraries in the given config will be returned.
:return: LibraryConfig objects.
"""
if target_library_names is None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the list is empty? Does it mean we generate nothing or generate everything?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the list is empty

We generate nothing.

The rational is if we pass a list, then the libraries will be selected from the list. Therefore, an empty list means no generation.

If we don't pass a list, the default value is None and we generate everything.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG. Can you mention the behavior in the comment above? I wonder how it is going to be implemented in generate_repo.py later, maybe just no-op if the list is empty?

Copy link
Collaborator Author

@JoeWang1127 JoeWang1127 Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adde comments.

I wonder how it is going to be implemented in generate_repo.py later, maybe just no-op if the list is empty?

It will not call generate_composed_library.py but it will do a repo level post processing, which is no-op if no library is changed.

return config.libraries
target_libraries = set(target_library_names)
return [
library
for library in config.libraries
if library.get_library_name() in target_libraries
]
63 changes: 63 additions & 0 deletions library_generation/test/generate_repo_unit_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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 unittest

from library_generation.generate_repo import get_target_libraries
from library_generation.model.generation_config import GenerationConfig
from library_generation.model.library_config import LibraryConfig


class GenerateRepoTest(unittest.TestCase):
def test_get_target_library_returns_selected_libraries(self):
one_library = GenerateRepoTest.__get_an_empty_library_config()
one_library.api_shortname = "one_library"
another_library = GenerateRepoTest.__get_an_empty_library_config()
another_library.api_shortname = "another_library"
config = GenerateRepoTest.__get_an_empty_generation_config()
config.libraries.extend([one_library, another_library])
target_libraries = get_target_libraries(config, ["another_library"])
self.assertEqual([another_library], target_libraries)

def test_get_target_library_given_null_returns_all_libraries(self):
one_library = GenerateRepoTest.__get_an_empty_library_config()
one_library.api_shortname = "one_library"
another_library = GenerateRepoTest.__get_an_empty_library_config()
another_library.api_shortname = "another_library"
config = GenerateRepoTest.__get_an_empty_generation_config()
config.libraries.extend([one_library, another_library])
target_libraries = get_target_libraries(config)
self.assertEqual([one_library, another_library], target_libraries)

@staticmethod
def __get_an_empty_generation_config() -> GenerationConfig:
return GenerationConfig(
gapic_generator_version="",
googleapis_commitish="",
synthtool_commitish="",
owlbot_cli_image="",
template_excludes=[],
path_to_yaml="",
libraries=[],
)

@staticmethod
def __get_an_empty_library_config() -> LibraryConfig:
return LibraryConfig(
api_shortname="",
name_pretty="",
api_description="",
product_documentation="",
gapic_configs=[],
)
6 changes: 1 addition & 5 deletions library_generation/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,7 @@ def prepare_repo(
os.makedirs(output_folder, exist_ok=True)
libraries = {}
for library in library_config:
library_name = (
f"{language}-{library.library_name}"
if library.library_name
else f"{language}-{library.api_shortname}"
)
library_name = f"{language}-{library.get_library_name()}"
library_path = (
f"{repo_path}/{library_name}" if gen_config.is_monorepo else f"{repo_path}"
)
Expand Down
Loading