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

Add OperationContextParams + validator #2264

Merged
merged 8 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
81 changes: 81 additions & 0 deletions docs/source-2.0/additional-specs/rules-engine/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ order of the most specific to least specific value locations:

#. `smithy.rules#staticContextParams trait`_
#. `smithy.rules#contextParam trait`_
#. `smithy.rules#operationContextParams trait`_
#. `smithy.rules#clientContextParams trait`_
#. Built-in bindings
#. Built-in binding default values
Expand Down Expand Up @@ -240,6 +241,82 @@ operation:
operation GetThing {}


.. smithy-trait:: smithy.rules#operationContextParams
.. _smithy.rules#operationContextParams-trait:

``smithy.rules#operationContextParams`` trait
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Summary
Defines one or more rule set parameters that MUST be bound to values
specified in the operation input.
Trait selector
``operation``
Value type
``map`` of ``string`` containing a rule set parameter name to a
``operationContextParam`` structure.

The ``operationContextParam`` structure has the following properties:

.. list-table::
:header-rows: 1
:widths: 10 23 67

* - Property
- Type
- Description
* - path
- ``string``
- **Required**. A JMESPath expression to select element(s) from the operation input to bind to.

Each parameter is identified using it’s name as specified in the rule set. The
type of a ``operationContextParam`` MUST be compatible with the parameter type
specified in the rule set.

The following example specifies a parameter bound to an array of object keys in the
operation input using a JMESPath expression:

.. code-block:: smithy

@operationContextParams(
ObjectKeys: {
path: "Delete.Objects[*].Key"
}
)
operation DeleteObjects {
input: DeleteObjectsRequest
}

structure DeleteObjectsRequest {
Delete: Delete
}

structure Delete {
Objects: ObjectIdentifierList
}

list ObjectIdentifierList {
member: ObjectIdentifier
alextwoods marked this conversation as resolved.
Show resolved Hide resolved
}

structure ObjectIdentifier {
Key: String
alextwoods marked this conversation as resolved.
Show resolved Hide resolved
}

`paths` specified in :ref:`OperationContextParams <smithy.rules#operationContextParams-trait>` are limited
to a subset of JMESPath:

* `Identifiers`_ - the most basic expression and can be used to extract a single element from a JSON document.
The return value for an identifier is the value associated with the identifier. If the identifier does not
exist in the JSON document, than a null value is returned.
* `Sub Expressions`_ - a combination of two expressions separated by the ``.`` char.
Example: ``grandparent.parent.child``
* `Wildcard Expressions`_ - Creates a projection over the values in an array or map.
Remaining expressions are evaluated against each returned element.
* `Keys function`_ - return a list of the keys in a map. This is the only supported function but is required
for binding to key values.


.. smithy-trait:: smithy.rules#contextParam
.. _smithy.rules#contextParam-trait:

Expand Down Expand Up @@ -343,3 +420,7 @@ The rules engine is highly extensible through

.. _Javadocs: https://smithy.io/javadoc/__smithy_version__/software/amazon/smithy/rulesengine/language/EndpointRuleSetExtension.html
.. _service providers: https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html
.. _Identifiers: https://jmespath.org/specification.html#identifiers
.. _Sub expressions: https://jmespath.org/specification.html#subexpressions
.. _Wildcard expressions: https://jmespath.org/specification.html#wildcard-expressions
.. _Keys function: https://jmespath.org/specification.html#keys
1 change: 1 addition & 0 deletions smithy-rules-engine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ ext {
dependencies {
api project(":smithy-model")
api project(":smithy-utils")
api project(":smithy-jmespath")
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ public Optional<StaticContextParamsTrait> getStaticContextParams(Shape operation
return operation.getTrait(StaticContextParamsTrait.class);
}

/**
* Gets the operation context parameter names and their {@link OperationContextParamDefinition} for the given
* operation.
*
* @param operation The operation shape.
* @return The mapping of context parameter names to the JMESPath expression to bind to input elements.
*/
public Optional<OperationContextParamsTrait> getOperationContextParams(Shape operation) {
return operation.getTrait(OperationContextParamsTrait.class);
}

/**
* Gets the mapping of {@link MemberShape} to {@link ContextParamTrait} for the operation.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rulesengine.traits;

import java.util.Objects;
import software.amazon.smithy.utils.SmithyBuilder;
import software.amazon.smithy.utils.SmithyUnstableApi;
import software.amazon.smithy.utils.ToSmithyBuilder;

/**
* An operation context parameter definition.
*/
@SmithyUnstableApi
public final class OperationContextParamDefinition implements ToSmithyBuilder<OperationContextParamDefinition> {
private final String path;

private OperationContextParamDefinition(Builder builder) {
this.path = SmithyBuilder.requiredState("path", builder.path);
}

public static Builder builder() {
return new Builder();
}

public String getPath() {
return path;
}

@Override
public Builder toBuilder() {
return builder().path(path);
}

@Override
public int hashCode() {
return Objects.hash(getPath());
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OperationContextParamDefinition that = (OperationContextParamDefinition) o;
return getPath().equals(that.getPath());
}

public static final class Builder implements SmithyBuilder<OperationContextParamDefinition> {
private String path;

private Builder() {
}

public Builder path(String path) {
this.path = path;
return this;
}

public OperationContextParamDefinition build() {
return new OperationContextParamDefinition(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rulesengine.traits;

import java.util.LinkedHashMap;
import java.util.Map;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NodeMapper;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.AbstractTrait;
import software.amazon.smithy.model.traits.AbstractTraitBuilder;
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.utils.BuilderRef;
import software.amazon.smithy.utils.SmithyUnstableApi;
import software.amazon.smithy.utils.ToSmithyBuilder;

/**
* Binds elements of a target operation's input to rule-set parameters.
*/
@SmithyUnstableApi
public final class OperationContextParamsTrait extends AbstractTrait
implements ToSmithyBuilder<OperationContextParamsTrait> {
public static final ShapeId ID = ShapeId.from("smithy.rules#operationContextParams");

private final Map<String, OperationContextParamDefinition> parameters;

private OperationContextParamsTrait(Builder builder) {
super(ID, builder.getSourceLocation());
this.parameters = builder.parameters.copy();
}

public static Builder builder() {
return new Builder();
}

public Map<String, OperationContextParamDefinition> getParameters() {
return parameters;
}

@Override
protected Node createNode() {
NodeMapper mapper = new NodeMapper();
mapper.setOmitEmptyValues(true);
return mapper.serialize(getParameters()).expectObjectNode();
}

@Override
public Builder toBuilder() {
return new Builder()
.sourceLocation(getSourceLocation())
.parameters(parameters);
}

public static final class Provider extends AbstractTrait.Provider {
public Provider() {
super(ID);
}

@Override
public Trait createTrait(ShapeId target, Node value) {
NodeMapper mapper = new NodeMapper();
Map<String, OperationContextParamDefinition> parameters = new LinkedHashMap<>();
value.expectObjectNode().getMembers().forEach((stringNode, node) -> {
parameters.put(stringNode.getValue(), mapper.deserialize(node, OperationContextParamDefinition.class));
});
OperationContextParamsTrait trait = builder()
.sourceLocation(value)
.parameters(parameters)
.build();
trait.setNodeCache(value);
return trait;
}
}

public static final class Builder extends AbstractTraitBuilder<OperationContextParamsTrait, Builder> {
private final BuilderRef<Map<String, OperationContextParamDefinition>> parameters = BuilderRef.forOrderedMap();

private Builder() {
}

public Builder parameters(Map<String, OperationContextParamDefinition> parameters) {
this.parameters.clear();
this.parameters.get().putAll(parameters);
return this;
}

public Builder putParameter(String name, OperationContextParamDefinition definition) {
this.parameters.get().put(name, definition);
return this;
}

public Builder removeParameter(String name) {
this.parameters.get().remove(name);
return this;
}

public Builder clearParameters() {
this.parameters.clear();
return this;
}

@Override
public OperationContextParamsTrait build() {
return new OperationContextParamsTrait(this);
}
}
}
Loading
Loading