File: //snap/google-cloud-cli/current/lib/googlecloudsdk/command_lib/apigee/resource_args.py
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2021 Google LLC. All Rights Reserved.
#
# 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.
"""Specifications for resource-identifying command line parameters."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import collections
import re
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.command_lib.util.concepts import concept_parsers
# The singular name is used internally by gcloud to identify the reference type
# and should be named according to the rules in the Cloud SDK internal docs.
# The plural name refers to the entity name in the API resource path, e.g.,
# apigee.googleapis.com/v1/myEntityNamePlurals/...
_EntityNames = collections.namedtuple(
"EntityNames",
"singular plural docs_name valid_pattern secondary_description")
_ENTITY_TUPLES = [
_EntityNames("project", "projects", "projects", None,
"GCP project containing the {resource}."),
_EntityNames(
"organization", "organizations", "organization",
r"^[a-z][-a-z0-9]{0,30}[a-z0-9]$",
"Apigee organization containing the {resource}. If "
"unspecified, the Cloud Platform project's associated "
"organization will be used."),
_EntityNames("api", "apis", "API proxy", r"^[\s\w.-]{1,255}$",
"API proxy for the {resource}."),
_EntityNames("environment", "environments", "environment",
r"^[a-z][-a-z0-9]{0,30}[a-z0-9]$",
"Deployment environment of the {resource}."),
_EntityNames("revision", "revisions", "revision", None,
"Revision of the {resource}."),
_EntityNames("deployment", "deployments", "deployment", None,
"Relevant deployment of the {resource}."),
_EntityNames("operation", "operations", "operation", None,
"Operation operating on the {resource}."),
_EntityNames("product", "apiproducts", "API product",
r"^[A-Za-z0-9._$ %-]+$",
"Relevant product for the {resource}."),
_EntityNames("developer", "developers", "developer", None,
"Developer of the {resource}."),
_EntityNames("app", "apps", "application", None,
"Relevant application for the {resource}."),
_EntityNames("archive_deployment", "archiveDeployments",
"archive deployment", None,
"Archive deployment for {resource}")
]
ENTITIES = {item.singular: item for item in _ENTITY_TUPLES}
def _ValidPatternForEntity(name):
pattern = ENTITIES[name].valid_pattern
return r".*" if pattern is None else pattern
def ValidPatternForEntity(entity_name):
"""Returns a compiled regex that matches valid values for `entity_name`."""
return re.compile(_ValidPatternForEntity(entity_name))
def AttributeConfig(name, fallthroughs=None, help_text=None, validate=False):
"""Returns a ResourceParameterAttributeConfig for the attribute named `name`.
Args:
name: singular name of the attribute. Must exist in ENTITIES.
fallthroughs: optional list of gcloud fallthrough objects which should be
used to get this attribute's value if the user doesn't specify one.
help_text: help text to use for this resource parameter instead of the
default help text for the attribute.
validate: whether to check that user-provided value for this attribute
matches the expected pattern.
"""
validator = None
if validate:
validator = arg_parsers.RegexpValidator(
_ValidPatternForEntity(name),
"Must match the format of a valid {2} ({3})".format(*ENTITIES[name]))
return concepts.ResourceParameterAttributeConfig(
name=name,
parameter_name=ENTITIES[name].plural,
value_type=validator,
help_text=help_text or ENTITIES[name].secondary_description,
fallthroughs=fallthroughs)
def ResourceSpec(path, fallthroughs=tuple(), help_texts=None, validate=False):
"""Returns a ResourceSpec for the resource path `path`.
Args:
path: a list of attribute names. All must exist in ENTITIES.
fallthroughs: optional list of googlecloudsdk.command_lib.apigee.Fallthrough
objects which will provide default values for the attributes in `path`.
help_texts: a mapping of attribute names to help text strings, to use
instead of their default help text.
validate: whether to check that the user-provided resource matches the
expected naming conventions of the resource path.
"""
help_texts = collections.defaultdict(lambda: None, help_texts or {})
entities = [ENTITIES[name] for name in path]
ids = {}
for entity in entities:
relevant_fallthroughs = [
fallthrough for fallthrough in fallthroughs
if entity.singular in fallthrough
]
config = AttributeConfig(
entity.singular,
relevant_fallthroughs,
help_texts[entity.singular],
validate=validate)
ids[entity.plural + "Id"] = config
return concepts.ResourceSpec(
"apigee." + ".".join(entity.plural for entity in entities),
resource_name=entities[-1].docs_name,
**ids)
def AddSingleResourceArgument(parser,
resource_path,
help_text,
fallthroughs=tuple(),
positional=True,
argument_name=None,
required=None,
prefixes=False,
validate=False,
help_texts=None):
"""Creates a concept parser for `resource_path` and adds it to `parser`.
Args:
parser: the argparse.ArgumentParser to which the concept parser will be
added.
resource_path: path to the resource, in `entity.other_entity.leaf` format.
help_text: the help text to display when describing the resource as a whole.
fallthroughs: fallthrough providers for entities in resource_path.
positional: whether the leaf entity should be provided as a positional
argument, rather than as a flag.
argument_name: what to name the leaf entity argument. Defaults to the leaf
entity name from the resource path.
required: whether the user is required to provide this resource. Defaults to
True for positional arguments, False otherwise.
prefixes: whether to append prefixes to the non-leaf arguments.
validate: whether to check that the user-provided resource matches the
expected naming conventions of the resource path.
help_texts: custom help text for generated arguments. Defaults to each
entity using a generic help text.
"""
split_path = resource_path.split(".")
if argument_name is None:
leaf_element_name = split_path[-1]
if positional:
argument_name = leaf_element_name.upper()
else:
argument_name = "--" + leaf_element_name.replace("_", "-")
if required is None:
required = positional
concept_parsers.ConceptParser.ForResource(
argument_name,
ResourceSpec(split_path, fallthroughs, help_texts, validate=validate),
help_text,
required=required,
prefixes=prefixes).AddToParser(parser)