def testDependencyNode_Group(self):
    dependency_node = dependency_managers.DependencyNode.FromAttribute(
        self.group_arg_concept.Attribute())
    self.assertEqual('baz', dependency_node.name)
    self.assertEqual(self.group_arg_concept, dependency_node.concept)
    self.assertEqual(['first', 'second'],
                     sorted(dependency_node.dependencies.keys()))
    self.assertIsNone(dependency_node.arg_name)
    self.assertEqual([], dependency_node.fallthroughs)

    first = dependency_node.dependencies['first']
    self.assertEqual('first', first.concept.name)
    self.assertEqual(['bar', 'foo'],
                     sorted(first.dependencies.keys()))
    self.assertIsNone(first.arg_name)
    self.assertEqual([], first.fallthroughs)

    first_foo = first.dependencies['foo']
    self.assertEqual('first_foo', first_foo.concept.name)
    self.assertIsNone(first_foo.dependencies)
    self.assertEqual('--first-foo', first_foo.arg_name)
    self.assertEqual([deps.PropertyFallthrough(properties.VALUES.core.project)],
                     first_foo.fallthroughs)
def _CreateAttribute(data):
  """Creates a single resource attribute from YAML data.

  Args:
    data: {}, The dict of data from the YAML file for this single attribute.

  Returns:
    ResourceParameterAttributeConfig, the generated attribute.
  """
  attribute_name = data['attribute_name']
  help_text = data['help']

  fallthrough_data = data.get('fallthroughs', [])
  fallthroughs = [
      deps.Fallthrough(util.Hook.FromPath(f['hook']), hint=f['hint'])
      for f in fallthrough_data]

  prop_string = data.get('property')
  prop = properties.FromString(prop_string) if prop_string else None
  prop = prop or _DEFAULT_PROPS.get(attribute_name)
  if prop:
    fallthroughs.insert(0, deps.PropertyFallthrough(prop))

  completion_id_field = data.get('completion_id_field')
  completion_request_params = data.get('completion_request_params', [])
  final_params = {
      param.get('fieldName'): param.get('value')
      for param in completion_request_params}

  completer = data.get('completer')
  attribute = concepts.ResourceParameterAttributeConfig(
      name=attribute_name, help_text=help_text, completer=completer,
      fallthroughs=fallthroughs,
      completion_id_field=completion_id_field,
      completion_request_params=final_params)

  return (data['parameter_name'], attribute)
Esempio n. 3
0
    def testInitializeWithPropertyFallthroughs(self):
        """Tests that a resource is initialized correctly with property fallthrough.
    """
        fallthroughs_map = {
            'book': [deps.ArgFallthrough('--book')],
            'shelf': [deps.ArgFallthrough('--book-shelf')],
            'project': [
                deps.ArgFallthrough('--book-project'),
                deps.PropertyFallthrough(properties.VALUES.core.project)
            ]
        }
        parsed_args = self._GetMockNamespace(book='example',
                                             book_shelf='exampleshelf',
                                             book_project=None)
        resource_spec = concepts.ResourceSpec('example.projects.shelves.books',
                                              resource_name='book',
                                              **self._MakeAttributeConfigs())

        registry_resource = resource_spec.Initialize(fallthroughs_map,
                                                     parsed_args=parsed_args)

        self.assertEqual(
            'projects/{}/shelves/exampleshelf/books/example'.format(
                self.Project()), registry_resource.RelativeName())
  def testPresentationSpecGenerateInfo(self):
    """Tests that presentation spec correctly initializes a ConceptInfo."""
    resource = presentation_specs.ResourcePresentationSpec(
        'BOOK',
        self.resource_spec,
        'The book to act upon.',
        prefixes=False)
    concept_info = concept_parsers.ConceptParser([resource]).GetInfo('BOOK')

    self.assertEqual(self.resource_spec.name, concept_info.concept_spec.name)
    self.assertEqual(resource.name, concept_info.presentation_name)
    self.assertFalse(concept_info.plural)
    self.assertTrue(concept_info.allow_empty)
    self.assertEqual('The book to act upon.', concept_info.group_help)
    self.assertEqual({'book': 'BOOK',
                      'shelf': '--shelf'},
                     concept_info.attribute_to_args_map)
    # Ensure that the fallthroughs map is correctly created.
    expected = {
        'book': [],
        'shelf': [],
        'project': [
            deps.PropertyFallthrough(properties.VALUES.core.project)]}
    self.assertEqual(expected, concept_info.fallthroughs_map)
Esempio n. 5
0
def GetLocationAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'location',
      'The location of the {resource}.',
      fallthroughs=[
          deps.PropertyFallthrough(properties.VALUES.filestore.location)])
Esempio n. 6
0
def GetAttributeConfig():
  property_ = properties.VALUES.access_context_manager.policy
  return concepts.ResourceParameterAttributeConfig(
      name='policy',
      help_text='The ID of the access policy.',
      fallthroughs=[deps.PropertyFallthrough(property_)])
Esempio n. 7
0
class DepsTest(concepts_test_base.ConceptsTestBase, parameterized.TestCase):
    """Test for the calliope.concepts.deps module."""
    def Project(self):
        return 'fake-project'

    def SetUp(self):
        properties.VALUES.core.project.Set(self.Project())

    def testGenericFallthrough(self):
        """Test functionality of a generic fallthrough."""
        fallthrough = deps.Fallthrough(lambda: 'FOO', 'this is the hint')
        self.assertEqual('FOO', fallthrough.GetValue(self._GetMockNamespace()))
        self.assertEqual('this is the hint', fallthrough.hint)

    def testGenericFallthroughFails(self):
        """Test functionality of a generic fallthrough."""
        fallthrough = deps.Fallthrough(lambda: None, 'this is the hint')
        with self.assertRaises(deps.FallthroughNotFoundError):
            fallthrough.GetValue(self._GetMockNamespace())

    def testPropertyFallthrough(self):
        """Test functionality of a property fallthrough."""
        fallthrough = deps.PropertyFallthrough(properties.VALUES.core.project)
        self.assertEqual(self.Project(),
                         fallthrough.GetValue(self._GetMockNamespace()))

    def testPropertyFallthroughFails(self):
        """Test property fallthrough when the property is unset."""
        self.UnsetProject()
        fallthrough = deps.PropertyFallthrough(properties.VALUES.core.project)
        with self.assertRaises(deps.FallthroughNotFoundError):
            fallthrough.GetValue(self._GetMockNamespace())

    def testArgFallthrough(self):
        """Test functionality of a property fallthrough."""
        fallthrough = deps.ArgFallthrough('--a')
        self.assertEqual('foo',
                         fallthrough.GetValue(self._GetMockNamespace(a='foo')))

    @parameterized.named_parameters(
        ('Arg', deps.ArgFallthrough('--a'), True),
        ('Generic',
         deps.Fallthrough(lambda: 'projects/p/shelves/s/books/b',
                          hint='h'), False),
        ('GenericActive',
         deps.Fallthrough(lambda: 'projects/p/shelves/s/books/b',
                          hint='h',
                          active=True), True))
    def testAnchorFallthrough(self, orig_fallthrough, active):
        """Test the FullySpecifiedAnchorFallthrough gives other parameters."""
        proj_fallthrough = deps.FullySpecifiedAnchorFallthrough(
            orig_fallthrough, self.book_collection, 'projectsId')
        shelf_fallthrough = deps.FullySpecifiedAnchorFallthrough(
            orig_fallthrough, self.book_collection, 'shelvesId')
        parsed_args = self._GetMockNamespace(a='projects/p/shelves/s/books/b')

        self.assertEqual('p', proj_fallthrough.GetValue(parsed_args))
        self.assertEqual('s', shelf_fallthrough.GetValue(parsed_args))
        self.assertEqual(active, proj_fallthrough.active)
        self.assertEqual(active, shelf_fallthrough.active)

    @parameterized.named_parameters(
        ('CantParse', 'projectsId', 'b'),
        ('WrongParam', 'project', 'projects/p/shelves/s/books/b'))
    def testAnchorFallthroughFails(self, proj_param, anchor_value):
        """Test failures with FullySpecifiedAnchorFallthrough."""
        proj_fallthrough = deps.FullySpecifiedAnchorFallthrough(
            deps.ArgFallthrough('--a'), self.book_collection, proj_param)
        parsed_args = self._GetMockNamespace(a=anchor_value)

        with self.assertRaises(deps.FallthroughNotFoundError):
            proj_fallthrough.GetValue(parsed_args)

    @parameterized.named_parameters(
        ('ArgFallthroughFlag', deps.ArgFallthrough(
            '--resources', plural=True), ['xyz'], ['xyz']),
        ('ArgFallthroughSingle', deps.ArgFallthrough(
            '--resources', plural=True), 'xyz', ['xyz']),
        ('Property',
         deps.PropertyFallthrough(properties.VALUES.compute.zone,
                                  plural=True), 'xyz', ['xyz']),
        ('Fallthrough', deps.Fallthrough(lambda: 'xyz', hint='h',
                                         plural=True), None, ['xyz']))
    def testFallthroughsPlural(self, fallthrough, val, expected):
        properties.VALUES.compute.zone.Set(val)
        parsed_args = self._GetMockNamespace(resources=val)
        self.assertEqual(expected,
                         fallthrough.GetValue(parsed_args=parsed_args))

    @parameterized.named_parameters(
        ('ArgFallthroughFlag',
         [deps.ArgFallthrough('--resources', plural=True)], ['xyz'], ['xyz']),
        ('ArgFallthroughSingle',
         [deps.ArgFallthrough('--resources', plural=True)], 'xyz', ['xyz']),
        ('Property', [
            deps.PropertyFallthrough(properties.VALUES.compute.zone,
                                     plural=True)
        ], 'xyz', ['xyz']), ('Fallthrough', [
            deps.Fallthrough(lambda: 'xyz', hint='h', plural=True)
        ], None, ['xyz']))
    def testGet_Plural(self, fallthroughs, val, expected):
        properties.VALUES.compute.zone.Set(val)
        result = deps.Get('resource', {'resource': fallthroughs},
                          parsed_args=self._GetMockNamespace(resources=val))
        self.assertEqual(expected, result)

    def testGet_ArgsGiven(self):
        """Test the deps object can initialize attributes using ArgFallthrough."""
        fallthroughs_map = {
            'name': [deps.ArgFallthrough('--myresource-name')],
            'project': [
                deps.ArgFallthrough('--myresource-project'),
                deps.ArgFallthrough('--project'),
                deps.PropertyFallthrough(properties.VALUES.core.project)
            ]
        }
        parsed_args = self._GetMockNamespace(
            myresource_name='example', myresource_project='exampleproject')
        self.assertEqual(
            'example',
            deps.Get('name', fallthroughs_map, parsed_args=parsed_args))
        self.assertEqual(
            'exampleproject',
            deps.Get('project', fallthroughs_map, parsed_args=parsed_args))

    def testGet_UseProperty(self):
        """Test the deps object can initialize attributes using PropertyFallthrough.
    """
        result = deps.Get(
            'project', {
                'project': [
                    deps.ArgFallthrough('--myresource-project'),
                    deps.ArgFallthrough('--project'),
                    deps.PropertyFallthrough(properties.VALUES.core.project)
                ]
            },
            parsed_args=self._GetMockNamespace(myresource_project=None))
        self.assertEqual(self.Project(), result)

    def testGet_BothFail(self):
        """Test the deps object raises an error if an attribute can't be found."""
        self.UnsetProject()
        fallthroughs_map = {
            'project': [
                deps.ArgFallthrough('--myresource-project'),
                deps.ArgFallthrough('--project'),
                deps.PropertyFallthrough(properties.VALUES.core.project)
            ]
        }
        parsed_args = self._GetMockNamespace(myresource_project=None)
        regex = re.escape(
            'Failed to find attribute [project]. The attribute can be set in the '
            'following ways: \n'
            '- provide the argument [--myresource-project] on the command line\n'
            '- provide the argument [--project] on the command line\n'
            '- set the property [core/project]')
        with self.assertRaisesRegex(deps.AttributeNotFoundError, regex):
            deps.Get('project', fallthroughs_map, parsed_args=parsed_args)

    def testGet_AnotherProperty(self):
        """Test the deps object handles non-project property.
    """
        properties.VALUES.compute.zone.Set('us-east1b')
        result = deps.Get(
            'zone', {
                'zone': [
                    deps.ArgFallthrough('--myresource-zone'),
                    deps.PropertyFallthrough(properties.VALUES.compute.zone)
                ]
            },
            parsed_args=self._GetMockNamespace(myresource_zone=None))
        self.assertEqual('us-east1b', result)

    def testGet_BothFail_AnotherProperty(self):
        """Test the deps error has the correct message for non-project properties.
    """
        properties.VALUES.compute.zone.Set(None)
        fallthroughs_map = {
            'zone': [
                deps.ArgFallthrough('--myresource-zone'),
                deps.PropertyFallthrough(properties.VALUES.compute.zone),
                deps.Fallthrough(lambda: None, 'custom hint')
            ]
        }
        parsed_args = self._GetMockNamespace(myresource_zone=None)
        regex = re.escape(
            'Failed to find attribute [zone]. The attribute can be set in the '
            'following ways: \n'
            '- provide the argument [--myresource-zone] on the command line\n'
            '- set the property [compute/zone]\n'
            '- custom hint')
        with self.assertRaisesRegex(deps.AttributeNotFoundError, regex):
            deps.Get('zone', fallthroughs_map, parsed_args=parsed_args)
Esempio n. 8
0
def InstanceAttributeConfig():
    """Get instance resource attribute with default value."""
    return concepts.ResourceParameterAttributeConfig(
        name='instance',
        help_text='The Cloud Spanner instance for the {resource}.',
        fallthroughs=[deps.PropertyFallthrough(_INSTANCE)])
Esempio n. 9
0
def ClusterAttributeConfig():
    return concepts.ResourceParameterAttributeConfig(
        name='cluster',
        help_text='Specific to Cloud Run on Kubernetes Engine: '
        'The name of the Kubernetes Engine cluster to use.',
        fallthroughs=[deps.PropertyFallthrough(properties.VALUES.run.cluster)])
Esempio n. 10
0
def ClusterAttributeConfig():
    return concepts.ResourceParameterAttributeConfig(
        name='cluster',
        help_text='The name of the cluster to use.',
        fallthroughs=[deps.PropertyFallthrough(properties.VALUES.run.cluster)])
Esempio n. 11
0
def RegionAttributeConfig():
    fallthroughs = [deps.PropertyFallthrough(properties.VALUES.builds.region)]
    return concepts.ResourceParameterAttributeConfig(
        name='region',
        fallthroughs=fallthroughs,
        help_text='The Cloud location for the {resource}.')
Esempio n. 12
0
    def __init__(self,
                 resource_collection,
                 resource_name=None,
                 api_version=None,
                 **kwargs):
        """Initializes a ResourceSpec.

    To use a ResourceSpec, give a collection path such as
    'cloudiot.projects.locations.registries', and optionally an
    API version.

    For each parameter in the collection path, an attribute is added to the
    resource spec. Names can be created by default or overridden in the
    attribute_configs dict, which maps from the parameter name to a
    ResourceParameterAttributeConfig object. ResourceParameterAttributeConfigs
    also contain information about the help text that describes the attribute.

    Attribute naming: By default, attributes are named after their collection
    path param names, or "name" if they are the "anchor" attribute (the final
    parameter in the path).

    Args:
      resource_collection: The collection path of the resource.
      resource_name: The name of the resource, which will be used in attribute
        help text.
      api_version: Overrides the default version in the resource
        registry.
      **kwargs: Parameter names (such as 'projectsId') from the
        collection path, mapped to ResourceParameterAttributeConfigs.
    """
        self._name = resource_name
        self.collection = resource_collection
        self._resources = resources.REGISTRY.Clone()
        self._collection_info = self._resources.GetCollectionInfo(
            resource_collection, api_version=api_version)
        collection_params = self._collection_info.GetParams('')
        self._attributes = []
        self._param_names_map = {}
        anchor = False

        # Add attributes.
        for i, param_name in enumerate(collection_params):
            if i == len(collection_params) - 1:
                anchor = True
            attribute_config = kwargs.get(param_name,
                                          ResourceParameterAttributeConfig())
            attribute_name = self._AttributeName(param_name,
                                                 attribute_config,
                                                 anchor=anchor)
            fallthroughs = []
            if attribute_config.prop:
                fallthroughs.append(
                    deps_lib.PropertyFallthrough(attribute_config.prop))
            new_attribute = Attribute(name=attribute_name,
                                      help_text=attribute_config.help_text,
                                      required=True,
                                      fallthroughs=fallthroughs,
                                      completer=attribute_config.completer)
            self._attributes.append(new_attribute)
            # Keep a map from attribute names to param names. While attribute names
            # are used for error messaging and arg creation/parsing, resource parsing
            # during command runtime requires parameter names.
            self._param_names_map[new_attribute.name] = param_name
class ParsingTests(concepts_test_base.MultitypeTestBase,
                   parameterized.TestCase):
  """Tests of the entire parsing mechanism."""

  def AssertParsedResultEquals(self, expected, actual, is_multitype=False):
    if is_multitype:
      actual = actual.result
    if expected is None:
      self.assertIsNone(actual)
    else:
      self.assertEqual(expected, actual.RelativeName())

  def AssertParsedListEquals(self, expected, actual, is_multitype=False):
    if is_multitype:
      actual = [item.result for item in actual]
    self.assertEqual(expected, [resource.RelativeName() for resource in actual])

  def PresentationSpecType(self, is_multitype=False):
    if is_multitype:
      return presentation_specs.MultitypeResourcePresentationSpec
    return presentation_specs.ResourcePresentationSpec

  def testSingleParameter(self):
    """Test a resource with only 1 parameter that doesn't get generated."""
    resource = presentation_specs.ResourcePresentationSpec(
        'project',
        concepts.ResourceSpec(
            'example.projects',
            'project',
            projectsId=concepts.ResourceParameterAttributeConfig(
                name='project',
                help_text='The Cloud Project of the {resource}.',
                fallthroughs=[
                    deps.PropertyFallthrough(properties.VALUES.core.project)])),
        'Group Help',
        prefixes=False)
    info = concept_parsers.ConceptParser([resource]).GetInfo('project')

    # No args should be generated.
    args = [arg.name for arg in info.GetAttributeArgs()]
    self.assertEqual([], args)

    concept_parser = concept_parsers.ConceptParser([resource])
    concept_parser.AddToParser(self.parser)

    # Parsing still works and the spec is still registered as 'project' on
    # CONCEPTS even though nothing was generated.
    properties.VALUES.core.project.Set('foo')
    namespace = self.parser.parser.parse_args([])
    self.assertEqual('projects/foo',
                     namespace.CONCEPTS.project.Parse().RelativeName())

  def testAllFallthrough(self):
    """Test a resource where everything has a fallthough."""
    def Fallthrough():
      return '!'
    resource = presentation_specs.ResourcePresentationSpec(
        'book',
        concepts.ResourceSpec(
            'example.projects.shelves.books',
            'project',
            projectsId=concepts.ResourceParameterAttributeConfig(
                name='project', help_text='Auxilio aliis.',
                fallthroughs=[
                    deps.PropertyFallthrough(properties.VALUES.core.project)]),
            shelvesId=concepts.ResourceParameterAttributeConfig(
                name='shelf', help_text='Auxilio aliis.',
                fallthroughs=[deps.Fallthrough(Fallthrough, hint='hint')]),
            booksId=concepts.ResourceParameterAttributeConfig(
                name='book', help_text='Auxilio aliis.',
                fallthroughs=[deps.Fallthrough(Fallthrough, hint='hint')])),
        'Group Help',
        prefixes=False)

    concept_parser = concept_parsers.ConceptParser([resource])
    concept_parser.AddToParser(self.parser)
    properties.VALUES.core.project.Set('foo')
    namespace = self.parser.parser.parse_args([])
    self.assertEqual('projects/foo/shelves/!/books/!',
                     namespace.CONCEPTS.book.Parse().RelativeName())

  def testConceptParserForResource(self):
    """Test the ForResource method."""
    concept_parser = concept_parsers.ConceptParser.ForResource(
        '--book',
        self.resource_spec,
        'The book to act upon.',
        flag_name_overrides={'project': '--book-project'})
    concept_parser.AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(
        ['--book', 'example', '--shelf', 'exampleshelf', '--book-project',
         'example-project'])
    self.assertEqual(
        'projects/example-project/shelves/exampleshelf/books/example',
        namespace.CONCEPTS.book.Parse().RelativeName())

  def testConceptParserForResourceMultipleResources(self):
    """Test the ForResource method."""
    concept_parsers.ConceptParser.ForResource(
        '--book',
        self.resource_spec,
        'The book to act upon.',
        flag_name_overrides={'project': '--book-project'}
    ).AddToParser(self.parser)

    concept_parsers.ConceptParser.ForResource(
        '--other-book',
        self.resource_spec,
        'The other book to act upon.',
        flag_name_overrides={
            'book': '--other-book',
            'project': '--other-book-project',
            'shelf': '--other-book-shelf',
        }
    ).AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(
        ['--book', 'example', '--shelf', 'exampleshelf', '--book-project',
         'example-project', '--other-book', 'example2', '--other-book-shelf',
         'exampleshelf2', '--other-book-project', 'example-project2'])
    self.assertEqual(
        'projects/example-project/shelves/exampleshelf/books/example',
        namespace.CONCEPTS.book.Parse().RelativeName())
    self.assertEqual(
        'projects/example-project2/shelves/exampleshelf2/books/example2',
        namespace.CONCEPTS.other_book.Parse().RelativeName())

  def testConceptParserForResourceRequiredPositional(self):
    """Test the ForResource method parses a required positional correctly.

    This causes the arg to be formatted as a list by the parser, so the
    handler needs to convert it back to a single value.
    """
    concept_parser = concept_parsers.ConceptParser.ForResource(
        'book',
        self.resource_spec,
        'The book to act upon.',
        required=True)
    concept_parser.AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(
        ['example', '--shelf', 'exampleshelf'])
    self.assertEqual(
        'projects/{}/shelves/exampleshelf/books/example'.format(self.Project()),
        namespace.CONCEPTS.book.Parse().RelativeName())

  def testConceptParserForResourceNonRequired(self):
    """Test non-required resource arg allows entire resource to be unspecified.
    """
    concept_parser = concept_parsers.ConceptParser.ForResource(
        '--book',
        self.resource_spec,
        'The book to act upon.')
    concept_parser.AddToParser(self.parser)
    self.parser.parser.parse_args([])

  def testConceptParserForResourceRequired(self):
    """Test the ForResource method when arg is required."""
    concept_parser = concept_parsers.ConceptParser.ForResource(
        '--book',
        self.resource_spec,
        'The book to act upon.',
        required=True)
    concept_parser.AddToParser(self.parser)

    with self.AssertRaisesArgumentErrorMatches(
        'argument (--book : --shelf): Must be specified.'):
      self.parser.parser.parse_args([])

  def testConceptParserForResourceModal(self):
    """Test the ForResource method creates modal anchor arg."""
    concept_parser = concept_parsers.ConceptParser.ForResource(
        '--book',
        self.resource_spec,
        'The book to act upon.',
        required=True)
    concept_parser.AddToParser(self.parser)

    with self.AssertRaisesArgumentErrorMatches(
        'argument --shelf: --book must be specified.'):
      self.parser.parser.parse_args(['--shelf', 'myshelf'])

  def testConceptParserForResourceRequiredPositionalRaises(self):
    """Test the ForResource method with a required positional arg."""
    concept_parser = concept_parsers.ConceptParser.ForResource(
        'BOOK',
        self.resource_spec,
        'The book to act upon.',
        required=True)
    concept_parser.AddToParser(self.parser)

    with self.AssertRaisesArgumentErrorMatches(
        'argument --shelf: BOOK must be specified.'):
      self.parser.parser.parse_args(['--shelf', 'exampleshelf'])

  def testConceptParserAndPropertyFallthroughs(self):
    """Tests that the concept parser correctly gets project from property.
    """
    concept_parser = concept_parsers.ConceptParser.ForResource(
        '--book',
        self.resource_spec,
        'The book to act upon.')
    concept_parser.AddToParser(self.parser)

    parsed_args = self.parser.parser.parse_args(['--book', 'examplebook',
                                                 '--shelf', 'exampleshelf'])

    self.assertEqual(
        'projects/{}/shelves/exampleshelf/books/examplebook'.format(
            self.Project()),
        parsed_args.CONCEPTS.book.Parse().RelativeName())

  def testConceptParserForResourceWithPositional(self):
    """Test the ForResource method with a positional arg."""
    concept_parser = concept_parsers.ConceptParser.ForResource(
        'BOOK',
        self.resource_spec,
        'The book to act upon.')
    concept_parser.AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(['example',
                                               '--shelf', 'exampleshelf'])
    self.assertEqual(
        'projects/{}/shelves/exampleshelf/books/example'.format(self.Project()),
        namespace.CONCEPTS.book.Parse().RelativeName())

  @parameterized.named_parameters(
      ('Names', False,
       ['example', '--book-shelf', 'exampleshelf', '--other', 'otherbook',
        '--other-shelf', 'othershelf'],
       'projects/fake-project/shelves/exampleshelf/books/example',
       'projects/fake-project/shelves/othershelf/books/otherbook'),
      ('FullySpecified', False,
       ['projects/p1/shelves/s1/books/b1',
        '--other', 'projects/p2/shelves/s2/books/b2'],
       'projects/p1/shelves/s1/books/b1', 'projects/p2/shelves/s2/books/b2'),
      ('MultitypeNames', True,
       ['example', '--book-shelf', 'exampleshelf', '--other', 'otherbook',
        '--other-shelf', 'othershelf'],
       'projects/fake-project/shelves/exampleshelf/books/example',
       'projects/fake-project/shelves/othershelf/books/otherbook'),
      ('MultitypeFullySpecified', True,
       ['projects/p1/shelves/s1/books/b1',
        '--other', 'projects/p2/shelves/s2/books/b2'],
       'projects/p1/shelves/s1/books/b1', 'projects/p2/shelves/s2/books/b2'),
      ('MultitypeNamesDiffTypes', True,
       ['example', '--book-shelf', 'exampleshelf', '--other', 'otherbook',
        '--other-case', 'othercase'],
       'projects/fake-project/shelves/exampleshelf/books/example',
       'projects/fake-project/cases/othercase/books/otherbook'),
      ('MultitypeFullySpecifiedDiffTypes', True,
       ['projects/p1/shelves/s1/books/b1',
        '--other', 'projects/p2/cases/c2/books/b2'],
       'projects/p1/shelves/s1/books/b1', 'projects/p2/cases/c2/books/b2'))
  def testTwoResourceArgs(self, is_multitype, args, first_expected,
                          second_expected):
    """Test a concept parser with two resource args."""
    resource_spec = (
        self.two_way_shelf_case_book if is_multitype else self.resource_spec)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        'book',
        resource_spec,
        'The book to act upon.',
        prefixes=True)
    other_resource = self.PresentationSpecType(is_multitype=is_multitype)(
        '--other',
        resource_spec,
        'The second book to act upon.',
        prefixes=True)
    concept_parser = concept_parsers.ConceptParser([resource, other_resource])
    concept_parser.AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(args)
    self.AssertParsedResultEquals(
        first_expected,
        namespace.CONCEPTS.book.Parse(),
        is_multitype=is_multitype)
    self.AssertParsedResultEquals(
        second_expected,
        namespace.CONCEPTS.other.Parse(),
        is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('Names', False, '--book', False,
       ['--book', 'b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('Full', False, '--book', False,
       ['--book', 'projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('Positional', False, 'book', False,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('PositionalRequired', False, 'book', True,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('MultitypeNames', True, '--book', False,
       ['--book', 'b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('MultitypeFull', True, '--book', False,
       ['--book', 'projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('MultitypeDiffType', True, '--book', False,
       ['--book', 'organizations/o1/shelves/s1/books/b1'],
       'organizations/o1/shelves/s1/books/b1'),
      ('MultitypeRequired', True, '--book', True,
       ['--book', 'projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('MultitypePositional', True, 'book', False,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('MultitypePositionalRequired', True, 'book', True,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('MultitypePositionalRequiredDiffType', True, 'book', True,
       ['organizations/o1/shelves/s1/books/b1'],
       'organizations/o1/shelves/s1/books/b1'))
  def testParse(self, is_multitype, name, required, args, expected):
    resource_spec = (self.two_way_resource if is_multitype
                     else self.resource_spec)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        name,
        resource_spec,
        'Group Help',
        flag_name_overrides={'project': '--book-project'},
        prefixes=False,
        required=required,
        plural=False)
    concept_parsers.ConceptParser([resource]).AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(args)

    self.AssertParsedResultEquals(
        expected, namespace.CONCEPTS.book.Parse(), is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('Names', '--book', False,
       ['--book', 'b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('NamesWithExtra', '--book', False,
       ['--book', 'b1', '--shelf', 's1', '--case', 'c1',
        '--book-project', 'p1'],
       'projects/p1/cases/c1/shelves/s1/books/b1'),
      ('Full', '--book', False,
       ['--book', 'projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('FullWithExtra', '--book', False,
       ['--book', 'projects/p1/cases/c1/shelves/s1/books/b1'],
       'projects/p1/cases/c1/shelves/s1/books/b1'),
      ('Required', '--book', True,
       ['--book', 'projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('RequiredWithExtra', '--book', True,
       ['--book', 'projects/p1/cases/c1/shelves/s1/books/b1'],
       'projects/p1/cases/c1/shelves/s1/books/b1'),
      ('Positional', 'book', False,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('PositionalWithExtra', 'book', False,
       ['b1', '--case', 'c1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/cases/c1/shelves/s1/books/b1'),
      ('PositionalRequired', 'book', True,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('PositionalRequiredWithExtra', 'book', True,
       ['b1', '--case', 'c1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/cases/c1/shelves/s1/books/b1'),
      ('PositionalRequiredFullySpecified', 'book', True,
       ['projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('PositionalRequiredFullySpecifiedWithExtra', 'book', True,
       ['projects/p1/cases/c1/shelves/s1/books/b1'],
       'projects/p1/cases/c1/shelves/s1/books/b1'))
  def testParseMultitypeExtraAttribute(self, name, required, args, expected):
    resource = self.PresentationSpecType(is_multitype=True)(
        name,
        self.multitype_extra_attribute_in_path,
        'Group Help',
        flag_name_overrides={'project': '--book-project'},
        prefixes=False,
        required=required,
        plural=False)
    concept_parsers.ConceptParser([resource]).AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(args)

    self.AssertParsedResultEquals(
        expected, namespace.CONCEPTS.book.Parse(), is_multitype=True)

  @parameterized.named_parameters(
      ('Names', '--book', False,
       ['--book', 'b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('Full', '--book', False,
       ['--book', 'projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('Parent', '--book', False,
       ['--book-project', 'p1', '--shelf', 's1'], 'projects/p1/shelves/s1'),
      ('Empty', '--book', False,
       [], None),
      ('Required', '--book', True,
       ['--book', 'projects/p1/shelves/s1/books/b1'],
       'projects/p1/shelves/s1/books/b1'),
      ('Positional', 'book', False,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'),
      ('PositionalRequired', 'book', True,
       ['b1', '--shelf', 's1', '--book-project', 'p1'],
       'projects/p1/shelves/s1/books/b1'))
  def testParseParentChild(self, name, required, args, expected):
    resource_spec = self.parent_child_resource
    resource = self.PresentationSpecType(is_multitype=True)(
        name,
        resource_spec,
        'Group Help',
        flag_name_overrides={'project': '--book-project'},
        prefixes=False,
        required=required,
        plural=False)
    concept_parsers.ConceptParser([resource]).AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(args)

    self.AssertParsedResultEquals(
        expected, namespace.CONCEPTS.book.Parse(), is_multitype=True)

  @parameterized.named_parameters(
      ('Names', False, '--books', False,
       ['--books', 'b1,b2', '--shelf', 's1', '--book-project', 'p1'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p1/shelves/s1/books/b2']),
      ('Full', False, '--books', False,
       ['--books',
        'projects/p1/shelves/s1/books/b1,projects/p2/shelves/s2/books/b2'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p2/shelves/s2/books/b2']),
      ('Positional', False, 'books', False,
       ['b1', 'b2', '--shelf', 's1', '--book-project', 'p1'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p1/shelves/s1/books/b2']),
      ('PositionalRequired', False, 'books', True,
       ['b1', 'b2', '--shelf', 's1', '--book-project', 'p1'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p1/shelves/s1/books/b2']),
      ('MultitypeNames', True, '--books', False,
       ['--books', 'b1,b2', '--shelf', 's1', '--book-project', 'p1'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p1/shelves/s1/books/b2']),
      ('MultitypeFull', True, '--books', False,
       ['--books',
        'projects/p1/shelves/s1/books/b1,projects/p2/shelves/s2/books/b2'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p2/shelves/s2/books/b2']),
      ('MultitypeDiffTypes', True, '--books', False,
       ['--books',
        'projects/p1/shelves/s1/books/b1,'
        'organizations/o1/shelves/s2/books/b2'],
       ['projects/p1/shelves/s1/books/b1',
        'organizations/o1/shelves/s2/books/b2']),
      ('MultitypeRequiredDiffTypes', True, '--books', True,
       ['--books',
        'projects/p1/shelves/s1/books/b1,'
        'organizations/o1/shelves/s2/books/b2'],
       ['projects/p1/shelves/s1/books/b1',
        'organizations/o1/shelves/s2/books/b2']),
      ('MultitypePositional', True, 'books', False,
       ['b1', 'b2', '--shelf', 's1', '--book-project', 'p1'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p1/shelves/s1/books/b2']),
      ('MultitypePositionalRequired', True, 'books', True,
       ['b1', 'b2', '--shelf', 's1', '--book-project', 'p1'],
       ['projects/p1/shelves/s1/books/b1', 'projects/p1/shelves/s1/books/b2']),
      ('MultitypePositionalDiffTypes', True, 'books', False,
       ['projects/p1/shelves/s1/books/b1',
        'organizations/o1/shelves/s2/books/b2'],
       ['projects/p1/shelves/s1/books/b1',
        'organizations/o1/shelves/s2/books/b2']),
      ('MultitypePositionalRequiredDiffTypes', True, 'books', True,
       ['projects/p1/shelves/s1/books/b1',
        'organizations/o1/shelves/s2/books/b2'],
       ['projects/p1/shelves/s1/books/b1',
        'organizations/o1/shelves/s2/books/b2']))
  def testParsePlural(self, is_multitype, name, required, args, expected):
    resource_spec = (self.two_way_resource if is_multitype
                     else self.resource_spec)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        name,
        resource_spec,
        'Group Help',
        flag_name_overrides={'project': '--book-project'},
        prefixes=False,
        required=required,
        plural=True)
    concept_parsers.ConceptParser([resource]).AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(args)

    self.AssertParsedListEquals(
        expected, namespace.CONCEPTS.books.Parse(), is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('Nonrequired', False, '--book', False, []),
      ('Required', False, '--book', True, []),
      ('NonrequiredPositional', False, 'book', False, []),
      ('RequiredPositional', False, 'book', True, []),
      ('MultitypeNonrequired', True, '--book', False, ['--book-project', '!']),
      ('MultitypeRequired', True, '--book', True, ['--book-project', '!']),
      ('MultitypeNonrequiredPositional', True, 'book', False,
       ['--book-project', '!']),
      ('MultitypeRequiredPositional', True, 'book', True,
       ['--book-project', '!']))
  def testParseAnchorFallthrough(self, is_multitype, name, rsrc_required, args):
    """Tests resource can be parsed when there are fallthroughs for anchor."""
    resource_spec = self.SetUpFallthroughSpec(is_multitype=is_multitype)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        name,
        resource_spec,
        'Group Help',
        flag_name_overrides={'project': '--book-project'},
        prefixes=False,
        required=rsrc_required)
    concept_parser = concept_parsers.ConceptParser([resource])
    concept_parser.AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(args)
    self.AssertParsedResultEquals('projects/!/shelves/!/books/!',
                                  namespace.CONCEPTS.book.Parse(),
                                  is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('Nonrequired', False, '--books', False,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), []),
      ('Required', False, '--books', True,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), []),
      ('NonrequiredPositional', False, 'books', False,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), []),
      ('RequiredPositional', False, 'books', True,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), []),
      ('PropertyFallthrough', False, '--books', False,
       deps.PropertyFallthrough(properties.VALUES.core.project), []),
      ('ArgFallthrough', False, '--books', False,
       deps.ArgFallthrough('--other'), ['--other', 'xyz']),
      ('MultitypeNonrequired', True, '--books', False,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), ['--book-project', 'xyz']),
      ('MultitypeRequired', True, '--books', True,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), ['--book-project', 'xyz']),
      ('MultitypeNonrequiredPositional', True, 'books', False,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), ['--book-project', 'xyz']),
      ('MultitypeRequiredPositional', True, 'books', True,
       deps.Fallthrough(lambda: ['xyz'], hint='h'), ['--book-project', 'xyz']),
      ('MultitypePropertyFallthrough', True, '--books', False,
       deps.PropertyFallthrough(properties.VALUES.core.project),
       ['--book-project', 'xyz']))
  def testParsePluralAnchorFallthrough(self, is_multitype, name, rsrc_required,
                                       fallthrough, args):
    """Tests plural resource args parse when there's an anchor fallthrough."""
    properties.VALUES.core.project.Set('xyz')
    resource_spec = self.SetUpFallthroughSpec(fallthrough=fallthrough,
                                              is_multitype=is_multitype)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        name,
        resource_spec,
        'Group Help',
        prefixes=False,
        required=rsrc_required,
        plural=True,
        flag_name_overrides={'project': '--book-project'}
    )
    concept_parser = concept_parsers.ConceptParser([resource])
    concept_parser.AddToParser(self.parser)
    self.parser.add_argument('--other', help='h')

    namespace = self.parser.parser.parse_args(args)
    self.AssertParsedListEquals(
        ['projects/xyz/shelves/xyz/books/xyz'],
        namespace.CONCEPTS.books.Parse(),
        is_multitype=is_multitype)

  def testParsePluralAnchorFallthroughMultitypeArgFallthrough(self):
    properties.VALUES.core.project.Set('xyz')
    fallthrough = deps.ArgFallthrough('--other')
    resource_spec = self.SetUpFallthroughSpec(fallthrough=fallthrough,
                                              is_multitype=True)
    # Remove the fallthrough for the organization.
    for attribute in resource_spec.attributes:
      if attribute.name == 'organization':
        attribute.fallthroughs = []
    resource = self.PresentationSpecType(is_multitype=True)(
        '--books',
        resource_spec,
        'Group Help',
        prefixes=False,
        required=False,
        plural=True,
        flag_name_overrides={'project': '--book-project'}
    )
    concept_parser = concept_parsers.ConceptParser([resource])
    concept_parser.AddToParser(self.parser)
    self.parser.add_argument('--other', help='h')

    namespace = self.parser.parser.parse_args(['--other', 'xyz'])
    self.AssertParsedListEquals(
        ['projects/xyz/shelves/xyz/books/xyz'],
        namespace.CONCEPTS.books.Parse(),
        is_multitype=True)

  @parameterized.named_parameters(
      ('Full', False,
       ['projects/abc/shelves/abc/books/abc',
        'projects/def/shelves/def/books/def'], [],
       ['projects/abc/shelves/abc/books/abc',
        'projects/def/shelves/def/books/def']),
      ('Name', False,
       ['abc', 'def'], [],
       ['projects/xyz/shelves/xyz/books/abc',
        'projects/xyz/shelves/xyz/books/def']),
      ('MultitypeFull', True,
       ['projects/abc/shelves/abc/books/abc',
        'projects/def/shelves/def/books/def'], [],
       ['projects/abc/shelves/abc/books/abc',
        'projects/def/shelves/def/books/def']),
      ('MultitypeFullDiffCollections', True,
       ['projects/abc/shelves/abc/books/abc',
        'organizations/def/shelves/def/books/def'], [],
       ['projects/abc/shelves/abc/books/abc',
        'organizations/def/shelves/def/books/def'])
  )
  def testParsePluralAnchorFallthroughMultiple(
      self, is_multitype, fallthrough_value, args, expected):
    """Tests plural resource args parse when there's an anchor fallthrough."""
    spec = copy.deepcopy(
        self.SetUpFallthroughSpec(
            deps.Fallthrough(lambda: ['xyz'], hint='h'),
            is_multitype=is_multitype))
    fallthrough = deps.Fallthrough(lambda: fallthrough_value, hint='h',
                                   active=True)
    for attribute in spec.attributes:
      if attribute.name == 'book':
        attribute.fallthroughs = [fallthrough] + attribute.fallthroughs
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        '--books',
        spec,
        'Group Help',
        prefixes=False,
        plural=True,
        flag_name_overrides={'project': '--book-project'}
    )
    concept_parser = concept_parsers.ConceptParser([resource])
    concept_parser.AddToParser(self.parser)

    namespace = self.parser.parser.parse_args(args)
    self.AssertParsedListEquals(
        expected,
        namespace.CONCEPTS.books.Parse(),
        is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('Overridden', 'examplebook', False,
       'projects/exampleproject/shelves/exampleshelf/books/examplebook'),
      ('NotOverridden', '', False,
       'projects/junk/shelves/junk/books/junk'),
      ('OverriddenPlural', 'examplebook,projects/p1/shelves/s1/books/b1', True,
       ['projects/exampleproject/shelves/exampleshelf/books/examplebook',
        'projects/p1/shelves/s1/books/b1']),
      ('NotOverriddenPlural', '', True,
       ['projects/junk/shelves/junk/books/junk']))
  def testParseFullySpecifiedAnchorFallthrough(self, book_arg, plural,
                                               expected_value):
    """Only get values from a parsed anchor if that anchor is being used."""
    fallthroughs = [
        deps.Fallthrough(
            lambda: 'projects/junk/shelves/junk/books/junk', hint='h')]
    spec = copy.deepcopy(self.resource_spec)
    spec.attributes[-1].fallthroughs = fallthroughs
    # These should be used as fallthroughs unless the anchor fallthrough comes
    # from the fully specified fallthrough.
    spec.attributes[0].fallthroughs = [
        deps.Fallthrough(lambda: 'exampleproject', hint='h')]
    spec.attributes[1].fallthroughs = [
        deps.Fallthrough(lambda: 'exampleshelf', hint='h')]
    concept_parser = concept_parsers.ConceptParser.ForResource(
        '--book',
        spec,
        'The book to act upon.',
        plural=plural)
    concept_parser.AddToParser(self.parser)

    parsed_args = self.parser.parser.parse_args(['--book', book_arg])

    if plural:
      result = [book.RelativeName()
                for book in parsed_args.CONCEPTS.book.Parse()]
    else:
      result = parsed_args.CONCEPTS.book.Parse().RelativeName()
    self.assertEqual(expected_value, result)

  @parameterized.named_parameters(
      ('', False),
      ('Multitype', True))
  def testResourceArgParsedInGroup(self, is_multitype):
    """Test a concept parser with two resource args."""
    group = self.parser.add_group('A group')
    resource_spec = (
        self.resource_spec if not is_multitype else self.two_way_resource)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        'book',
        resource_spec,
        'The book to act upon.',
        group=group,
        prefixes=False,
        flag_name_overrides={'project': '--book-project'})
    concept_parsers.ConceptParser([resource]).AddToParser(self.parser)
    namespace = self.parser.parser.parse_args(
        ['example', '--shelf', 'exampleshelf', '--book-project',
         'example-project'])
    self.AssertParsedResultEquals(
        'projects/example-project/shelves/exampleshelf/books/example',
        namespace.CONCEPTS.book.Parse(),
        is_multitype=is_multitype)

  def testConceptParserForResourceAndCommandFallthroughs(self):
    """Tests that command level fallthroughs are prioritized over others."""
    concept_parser = concept_parsers.ConceptParser.ForResource(
        '--book',
        self.resource_spec,
        'The book to act upon.',
        command_level_fallthroughs={'project': ['--other-project']})
    concept_parser.AddToParser(self.parser)
    self.parser.add_argument('--other-project', help='h')

    parsed_args = self.parser.parser.parse_args(
        ['--book', 'examplebook',
         '--shelf', 'exampleshelf',
         '--other-project', 'otherproject'])

    self.assertEqual(
        'projects/otherproject/shelves/exampleshelf/books/examplebook',
        parsed_args.CONCEPTS.book.Parse().RelativeName())

  @parameterized.named_parameters(
      ('OF', False),
      ('Multitype', True))
  def testCommandFallthroughs(self, is_multitype):
    """Tests that command level fallthroughs are prioritized over others."""
    resource_spec = (
        self.resource_spec if not is_multitype else self.two_way_resource)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        '--book',
        resource_spec,
        'The book to act upon.')
    concept_parsers.ConceptParser(
        [resource],
        command_level_fallthroughs={'--book.project': ['--other-project']}
    ).AddToParser(self.parser)
    self.parser.add_argument('--other-project', help='h')

    parsed_args = self.parser.parser.parse_args(
        ['--book', 'examplebook',
         '--shelf', 'exampleshelf',
         '--other-project', 'otherproject'])

    self.AssertParsedResultEquals(
        'projects/otherproject/shelves/exampleshelf/books/examplebook',
        parsed_args.CONCEPTS.book.Parse(),
        is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('OF', False),
      ('Multitype', True))
  def testCommandFallthroughsNotUsed(self, is_multitype):
    """Tests that command level fallthroughs are prioritized over others."""
    resource_spec = (
        self.resource_spec if not is_multitype else self.two_way_resource)
    resource = self.PresentationSpecType(is_multitype=is_multitype)(
        '--book',
        resource_spec,
        'The book to act upon.',
        flag_name_overrides={'project': '--book-project'})
    concept_parsers.ConceptParser(
        [resource],
        command_level_fallthroughs={'--book.project': ['--other-project']}
    ).AddToParser(self.parser)
    self.parser.add_argument('--other-project', help='h')

    parsed_args = self.parser.parser.parse_args(
        ['--book', 'examplebook',
         '--shelf', 'exampleshelf',
         '--book-project', 'exampleproject',
         '--other-project', 'otherproject'])

    self.AssertParsedResultEquals(
        'projects/exampleproject/shelves/exampleshelf/books/examplebook',
        parsed_args.CONCEPTS.book.Parse(),
        is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('Used', False,
       ['--book', 'examplebook', '--shelf', 'exampleshelf', '--other-book',
        'otherexample'],
       'shelves/exampleshelf/books/otherexample'),
      ('NotUsed', False,
       ['--book', 'examplebook', '--shelf', 'exampleshelf', '--other-book',
        'otherexample', '--other-book-shelf', 'othershelf'],
       'shelves/othershelf/books/otherexample'),
      ('MultitypeUsed', True,
       ['--book', 'examplebook', '--shelf', 'exampleshelf', '--other-book',
        'otherexample'],
       'shelves/exampleshelf/books/otherexample'),
      ('MultitypeNotUsed', True,
       ['--book', 'examplebook', '--shelf', 'exampleshelf', '--other-book',
        'otherexample', '--other-book-shelf', 'othershelf'],
       'shelves/othershelf/books/otherexample'))
  def testCommandFallthroughsOtherResource(
      self, is_multitype, args_to_parse, expected):
    """Tests that command level fallthroughs are prioritized over others."""
    resource_spec = (
        self.two_way_shelf_case_book if is_multitype else self.resource_spec)
    concept_parser = concept_parsers.ConceptParser(
        [self.PresentationSpecType(is_multitype=is_multitype)(
            '--book',
            resource_spec,
            'The book to act upon.'),
         self.PresentationSpecType(is_multitype=is_multitype)(
             '--other-book',
             resource_spec,
             'The other book',
             prefixes=True)],
        command_level_fallthroughs={'--other-book.shelf': ['--book.shelf']})
    concept_parser.AddToParser(self.parser)

    parsed_args = self.parser.parser.parse_args(args_to_parse)

    self.AssertParsedResultEquals(
        'projects/{}/shelves/exampleshelf/books/examplebook'.format(
            self.Project()),
        parsed_args.CONCEPTS.book.Parse(),
        is_multitype=is_multitype)
    self.AssertParsedResultEquals(
        'projects/{}/{}'.format(self.Project(), expected),
        parsed_args.CONCEPTS.other_book.Parse(),
        is_multitype=is_multitype)

  @parameterized.named_parameters(
      ('FormattingOfValue', False, {'--other-book.shelf': ['--book.x.y']},
       'invalid fallthrough value: [--book.x.y]. Must be in the form BAR.b or '
       '--baz'),
      ('FormattingOfKey', False, {'shelf': ['--book.shelf']},
       'invalid fallthrough key: [shelf]. Must be in format "FOO.a" where FOO '
       'is the presentation spec name and a is the attribute name.'),
      ('KeySpecNotFound', False, {'FOO.shelf': ['--book.shelf']},
       'invalid fallthrough key: [FOO.shelf]. Spec name is not present in the '
       'presentation specs. Available names: [--book, --other-book]'),
      ('KeyAttributeNotFound', False, {'--other-book.case': ['--book.shelf']},
       'invalid fallthrough key: [--other-book.case]. spec named '
       '[--other-book] has no attribute named [case]'),
      ('ValueSpecNotFound', False, {'--other-book.shelf': ['FOO.shelf']},
       'invalid fallthrough value: [FOO.shelf]. Spec name is not present in '
       'the presentation specs. Available names: [--book, --other-book]'),
      ('ValueAttributeNotFound', False,
       {'--other-book.shelf': ['--book.case']},
       'invalid fallthrough value: [--book.case]. spec named [--book] '
       'has no attribute named [case]'),
      ('MultitypeAttributeNotFound', True,
       {'--other-book.shelf': ['--book.junk']},
       'invalid fallthrough value: [--book.junk]. spec named [--book] '
       'has no attribute named [junk]'))
  def testCommandFallthroughInvalid(self, is_multitype, fallthroughs, expected):
    resource_spec = (
        self.two_way_shelf_case_book if is_multitype else self.resource_spec)
    with self.assertRaisesRegexp(ValueError,
                                 re.escape(expected)):
      concept_parsers.ConceptParser(
          [self.PresentationSpecType(is_multitype=is_multitype)(
              '--book',
              resource_spec,
              'The book to act upon.'),
           self.PresentationSpecType(is_multitype=is_multitype)(
               '--other-book',
               resource_spec,
               'The other book',
               prefixes=True)],
          command_level_fallthroughs=fallthroughs)

  def testCommandFallthroughMultitypeError(self):
    """If a conflicting arg fallthrough is given, error out."""
    resource_spec = self.two_way_shelf_case_book
    concept_parsers.ConceptParser(
        [presentation_specs.MultitypeResourcePresentationSpec(
            '--book',
            resource_spec,
            'The book to act upon.'),
         presentation_specs.MultitypeResourcePresentationSpec(
             '--other-book',
             resource_spec,
             'The other book',
             prefixes=True)],
        command_level_fallthroughs={'--other-book.shelf': ['--book.shelf']}
    ).AddToParser(self.parser)
    namespace = self.parser.parser.parse_args(
        ['--book', 'b1', '--shelf', 's1', '--other-book', 'b1',
         '--other-book-case', 'c1'])
    with self.assertRaisesRegex(multitype.ConflictingTypesError,
                                re.escape('[shelf, book, case]')):
      namespace.CONCEPTS.other_book.Parse()

  def testCommandFallthroughsArgNotFound(self):
    specs = [
        presentation_specs.ResourcePresentationSpec(
            '--book',
            self.resource_spec,
            'The book to act upon.'),
        presentation_specs.ResourcePresentationSpec(
            '--other-book',
            self.resource_spec,
            'The other book',
            prefixes=True)]
    concept_parser = concept_parsers.ConceptParser(
        specs,
        command_level_fallthroughs={
            '--other-book.project': ['--book.project']})
    message = (
        'Invalid fallthrough value [--book.project]: No argument associated '
        'with attribute [project] in concept argument named [--book]')
    with self.assertRaisesRegexp(ValueError, re.escape(message)):
      concept_parser.GetInfo(specs[1].name)
Esempio n. 14
0
 def testPropertyFallthroughFails(self):
     """Test property fallthrough when the property is unset."""
     self.UnsetProject()
     fallthrough = deps.PropertyFallthrough(properties.VALUES.core.project)
     with self.assertRaises(deps.FallthroughNotFoundError):
         fallthrough.GetValue(self._GetMockNamespace())
Esempio n. 15
0
 def testPropertyFallthrough(self):
     """Test functionality of a property fallthrough."""
     fallthrough = deps.PropertyFallthrough(properties.VALUES.core.project)
     self.assertEqual(self.Project(),
                      fallthrough.GetValue(self._GetMockNamespace()))
Esempio n. 16
0
def ProjectAttributeConfig():
    """Get project resource attribute with default value."""
    return concepts.ResourceParameterAttributeConfig(
        name='project',
        help_text='The Cloud Project for the {resource}.',
        fallthroughs=[deps.PropertyFallthrough(_PROJECT)])
Esempio n. 17
0
def GetOrganizationAttributeConfig():
    property_ = properties.VALUES.access_context_manager.organization
    return concepts.ResourceParameterAttributeConfig(
        name='organization',
        help_text='The ID of the organization.',
        fallthroughs=[deps.PropertyFallthrough(property_)])
Esempio n. 18
0
def _DataprocRegionFallthrough():
    return [
        deps.ArgFallthrough('--region'),
        deps.PropertyFallthrough(properties.VALUES.dataproc.region)
    ]
            final_attributes.append(ignored_attribute)
        else:
            # It doesn't match (or there are no more registered params) and the
            # field is not being ignored, error.
            raise InvalidResourceArgumentLists(expected_param_names,
                                               registered_param_names)

    if raw_attributes:
        # All expected fields were processed but there are still registered
        # attribute params remaining, they must be extra.
        raise InvalidResourceArgumentLists(expected_param_names,
                                           registered_param_names)

    return final_attributes


DEFAULT_PROJECT_ATTRIBUTE_CONFIG = ResourceParameterAttributeConfig(
    name='project',
    help_text='Project ID of the Google Cloud Platform project for '
    'the {resource}.',
    fallthroughs=[
        # Typically argument fallthroughs should be configured at the command
        # level, but the --project flag is currently available in every command.
        deps_lib.ArgFallthrough('--project'),
        deps_lib.PropertyFallthrough(properties.VALUES.core.project)
    ])
DEFAULT_RESOURCE_ATTRIBUTE_CONFIGS = {
    'project': DEFAULT_PROJECT_ATTRIBUTE_CONFIG
}
_DEFAULT_CONFIGS = {'project': DEFAULT_PROJECT_ATTRIBUTE_CONFIG}
Esempio n. 20
0
from googlecloudsdk.api_lib.privateca import base
from googlecloudsdk.api_lib.privateca import constants as api_constants
from googlecloudsdk.api_lib.privateca import locations
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.calliope.concepts import deps
from googlecloudsdk.calliope.concepts import handlers
from googlecloudsdk.calliope.concepts import util
from googlecloudsdk.command_lib.kms import resource_args as kms_args
from googlecloudsdk.command_lib.privateca import exceptions as privateca_exceptions
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.core import properties
import six

# Flag fallthroughs that rely on properties.
LOCATION_PROPERTY_FALLTHROUGH = deps.PropertyFallthrough(
    properties.VALUES.privateca.location)
PROJECT_PROPERTY_FALLTHROUGH = deps.PropertyFallthrough(
    properties.VALUES.core.project)


def ReusableConfigAttributeConfig():
    # ReusableConfig is always an anchor attribute so help_text is unused.
    return concepts.ResourceParameterAttributeConfig(name='reusable_config')


def CertificateAttributeConfig(fallthroughs=None):
    # Certificate is always an anchor attribute so help_text is unused.
    return concepts.ResourceParameterAttributeConfig(name='certificate',
                                                     fallthroughs=fallthroughs
                                                     or [])
Esempio n. 21
0
def ProjectAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      name='project',
      help_text='The Cloud project for the {resource}. If not set, it will '
                'use the project set in properties.',
      fallthroughs=[deps.PropertyFallthrough(properties.VALUES.core.project)])
# limitations under the License.
"""Resource arguments for Backup for GKE commands."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.calliope.concepts import deps
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.core import properties

LOCATION_RESOURCE_PARAMETER_ATTRIBUTE = concepts.ResourceParameterAttributeConfig(
    name='location',
    fallthroughs=[
        deps.PropertyFallthrough(
            properties.VALUES.gkebackup.Property('location')),
    ],
    help_text='Google Cloud location.')


def AddBackupArg(parser):
    concept_parsers.ConceptParser.ForResource(
        'backup',
        GetBackupResourceSpec(),
        """
      Name of the backup to create. Once the backup is created, this name can't
      be changed. This must be 63 or fewer characters long and must be unique
      within the project, location, and backup plan. The name may be provided
      either as a relative name, e.g.
      `projects/<project>/locations/<location>/backupPlans/<backupPlan>/backups/<backup>`
      or as a single ID name (with the parent resources provided via options or