Skip to content

Commit 361adff

Browse files
committed
Implement scoped attribute selectors
1 parent 58793a2 commit 361adff

File tree

10 files changed

+520
-141
lines changed

10 files changed

+520
-141
lines changed

docs/source/spec/core/selectors.rst

+66-45
Original file line numberDiff line numberDiff line change
@@ -383,14 +383,24 @@ with a ``min`` property set to ``1``:
383383
[trait|range|min=1]
384384
385385
386+
Attribute projections
387+
---------------------
388+
389+
*Attribute projections* are values that perform set intersections with other
390+
values. A projection is formed using either the ``(values)`` or ``(keys)``
391+
:token:`pseudo-property <selector_pseudo_property>`.
392+
393+
386394
.. _values-property:
387395

388-
``(values)`` pseudo-property
389-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
396+
``(values)`` projection
397+
~~~~~~~~~~~~~~~~~~~~~~~
390398

391-
Values of a :token:`list <node_array>` can be selected using the special
392-
``(values)`` syntax. Each element from the value currently being evaluated
393-
is used as a new value to check subsequent properties against.
399+
The ``(values)`` property creates a *projection* of all values contained
400+
in a :token:`list <node_array>` or :token:`object <node_object>`. Each
401+
element from the value currently being evaluated is used as a new value
402+
to check subsequent properties against. A ``(values)`` projection on any
403+
value other than an array or object yields no result.
394404

395405
The following example matches all shapes that have an :ref:`enum-trait`
396406
that contains an enum definition with a ``tags`` property that is set to
@@ -400,10 +410,6 @@ that contains an enum definition with a ``tags`` property that is set to
400410
401411
[trait|enum|(values)|tags|(values)=internal]
402412
403-
Values of an :token:`object <node_object>` can also be selected using the
404-
special ``(values)`` syntax. Each value from object currently being evaluated
405-
is used as a new value to check subsequent properties against.
406-
407413
The following example matches all shapes that have an :ref:`externalDocumentation-trait`
408414
that has a value set to ``https://example.com``:
409415

@@ -421,12 +427,14 @@ that contains a '$' character:
421427
422428
.. _keys-property:
423429

424-
``(keys)`` pseudo-property
425-
~~~~~~~~~~~~~~~~~~~~~~~~~~
430+
``(keys)`` projection
431+
~~~~~~~~~~~~~~~~~~~~~
426432

427-
Keys of an object can be selected using the special ``(keys)`` syntax. Each
428-
key currently being evaluated is used as a new value to check subsequent
429-
properties against.
433+
The ``(keys)`` property creates a *projection* of all keys of an
434+
:token:`object <node_object>`. Each key of the object currently being
435+
evaluated is used as a new value to check subsequent properties against.
436+
A ``(keys)`` projection on any value other than an object yields no
437+
result.
430438

431439
The following example matches all shapes that have an ``externalDocumentation``
432440
trait that has an entry named ``Homepage``:
@@ -443,6 +451,16 @@ The following selector matches shapes that apply any traits in the
443451
[trait|(keys)^='smithy.example#']
444452
445453
454+
Projection comparisons
455+
~~~~~~~~~~~~~~~~~~~~~~
456+
457+
When a projection is compared against a scalar value, the comparison matches
458+
if any value in the projection satisfies the comparator assertion against the
459+
scalar value. When a projection is compared against another projection, the
460+
comparison matches if any value in the left projection satisfies the
461+
comparator when compared against any value in the right projection.
462+
463+
446464
Error handling
447465
~~~~~~~~~~~~~~
448466

@@ -463,15 +481,6 @@ A :token:`scoped attribute selector <selector_scoped_attr>` is similar to an
463481
attribute selector, but it allows multiple complex comparisons to be made
464482
against a scoped attribute.
465483

466-
In the following example, the ``trait|range`` attribute is used as the scoped
467-
attribute of the expression, and the selector matches all shapes marked with
468-
the :ref:`range-trait` where the ``min`` value is greater than the ``max``
469-
value:
470-
471-
.. code-block:: none
472-
473-
[@trait|range: @{min} > @{max}]
474-
475484

476485
Context values
477486
--------------
@@ -481,8 +490,20 @@ for the expression, followed by ``:``. The scoped attribute is accessed using
481490
a :token:`context value <selector_context_value>` in the form of
482491
``@{`` :token:`identifier` ``}``.
483492

484-
The ``(values)`` and ``(keys)`` pseudo-properties MAY be used in context
485-
values that branch off of the scoped attribute.
493+
In the following example, the ``trait|range`` attribute is used as the scoped
494+
attribute of the expression, and the selector matches all shapes marked with
495+
the :ref:`range-trait` where the ``min`` value is greater than the ``max``
496+
value:
497+
498+
.. code-block:: none
499+
500+
[@trait|range: @{min} > @{max}]
501+
502+
The ``(values)`` and ``(keys)`` projections MAY be used as the scoped
503+
attribute context value. When the scoped attribute context value is a
504+
projection, each flattened value of the projection is individually tested
505+
against each assertion. If any value from the projection matches the
506+
assertions, then the selector matches the shape.
486507

487508
The following selector matches shapes that have an :ref:`enum-trait` where one
488509
or more of the enum definitions is both marked as ``deprecated`` and contains
@@ -515,15 +536,15 @@ Like non-scoped selectors, multiple values can be provided using a comma
515536
separated list. One or more resolved attribute values MUST match one or more
516537
provided values.
517538

518-
The following selector matches all shapes with the :ref:`paginated-trait`
519-
where the ``inputToken`` is ``token`` or ``continuationToken``, and
520-
the ``outputToken`` is ``token`` or ``nextToken``:
539+
The following selector matches all shapes with the :ref:`httpApiKeyAuth-trait`
540+
where the ``in`` property is ``header`` and the ``name`` property is neither
541+
``x-api-token`` or ``authorization``:
521542

522543
.. code-block:: none
523544
524-
[@trait|paginated:
525-
@{inputToken}=token, continuationToken &&
526-
@{outputToken}=token, nextToken]
545+
[@trait|httpApiKeyAuth:
546+
@{name}=header &&
547+
@{in}!='x-api-token', 'authorization']
527548
528549
529550
Case insensitive comparisons
@@ -532,23 +553,23 @@ Case insensitive comparisons
532553
The ``i`` token used before ``&&`` or the closing ``]`` makes a comparison
533554
case-insensitive.
534555

535-
The following selector matches on the ``paginated`` trait using
556+
The following selector matches on the ``httpApiKeyAuth`` trait using
536557
case-insensitive comparisons:
537558

538559
.. code-block:: none
539560
540-
[@trait|paginated:
541-
@{inputToken}=token, continuationToken i &&
542-
@{outputToken}=token, nextToken i]
561+
[@trait|httpApiKeyAuth:
562+
@{name}=header i &&
563+
@{in}!='x-api-token', 'authorization' i]
543564
544-
The following selector matches on the ``paginated`` trait but only uses
545-
a case-insensitive comparison on the ``inputToken``:
565+
The following selector matches on the ``httpApiKeyAuth`` trait but only
566+
uses a case-insensitive comparison on ``in``:
546567

547568
.. code-block:: none
548569
549-
[@trait|paginated:
550-
@{inputToken}=token, continuationToken i &&
551-
@{outputToken}=token, nextToken]
570+
[@trait|httpApiKeyAuth:
571+
@{name}=header &&
572+
@{in}!='x-api-token', 'authorization' i]
552573
553574
554575
Neighbors
@@ -968,15 +989,15 @@ Selectors are defined by the following ABNF_ grammar.
968989
selector_attr :"[" `selector_key` *(`selector_comparator` `selector_values` ["i"]) "]"
969990
selector_key :`identifier` ["|" `selector_path`]
970991
selector_path :`selector_path_segment` *("|" `selector_path_segment`)
971-
selector_path_segment :`selector_value` / `selector_pseudo_key`
992+
selector_path_segment :`selector_value` / `selector_pseudo_property`
972993
selector_value :`selector_text` / `number` / `root_shape_id`
973-
selector_pseudo_key :"(" `identifier` ")"
994+
selector_pseudo_property :"(" `identifier` ")"
974995
selector_values :`selector_value` *("," `selector_value`)
975996
selector_comparator :"^=" / "$=" / "*=" / "!=" / ">=" / ">" / "<=" / "<" / "?=" / "="
976997
selector_absolute_root_shape_id :`namespace` "#" `identifier`
977-
selector_scoped_attr :"[@" `selector_key` ":" `selector_scoped_comparisons` "]"
978-
selector_scoped_comparisons :`selector_scoped_comparison` *("&&" `selector_scoped_comparison`)
979-
selector_scoped_comparison :`selector_scoped_value` `selector_comparator` `selector_scoped_values` ["i"]
998+
selector_scoped_attr :"[@" `selector_key` ":" `selector_scoped_assertions` "]"
999+
selector_scoped_assertions :`selector_scoped_assertion` *("&&" `selector_scoped_assertion`)
1000+
selector_scoped_assertion :`selector_scoped_value` `selector_comparator` `selector_scoped_values` ["i"]
9801001
selector_scoped_value :`selector_value` / `selector_context_value`
9811002
selector_context_value :"@{" `selector_path` "}"
9821003
selector_scoped_values :`selector_scoped_value` *("," `selector_scoped_value`)

0 commit comments

Comments
 (0)