Add support for stringArray rules engine Parameters (#2266)

This commit is contained in:
Alex Woods 2024-05-02 11:17:46 -07:00 committed by GitHub
parent e52e625d0b
commit a6f1d1ce12
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 130 additions and 20 deletions

View File

@ -23,9 +23,9 @@ This following is the :rfc:`ABNF <5234>` grammar for rule set parameter names:
.. productionlist:: smithy
identifier = ALPHA *(ALPHA / DIGIT)
Parameters declare their respective type using the ``type`` key. There are two
supported rule set parameter types: ``string`` and ``boolean``. The following
table provides the description of these types, and their Smithy compatible
Parameters declare their respective type using the ``type`` key. The following
parameter types are supported: ``string``, ``boolean``, and ``stringArray``.
The following table provides the description of these types, and their Smithy compatible
types whose values can be bound to these parameters. Rule set parameters are
always considered nullable and have no default value associated with them.
@ -42,6 +42,9 @@ always considered nullable and have no default value associated with them.
* - ``boolean``
- ``boolean``
- Boolean value type.
* - ``stringArray``
- ``list``
- A list with ``string`` members.
.. _rules-engine-parameters-implementation:
@ -219,13 +222,14 @@ The ``staticContextParam`` structure has the following properties:
* - value
- ``document``
- **Required**. The static value to be set for the parameter. The type
of the value MUST be either a ``string`` or ``boolean``.
of the value MUST be either a ``string``, ``boolean`` or an
array of ``string``.
Each parameter is identified using its name as specified in the rule set. The
type of a ``staticContextParam`` MUST be compatible with the parameter type
specified in the rule set.
The following example specifies two parameters to statically set for an
The following example specifies three parameters to statically set for an
operation:
.. code-block:: smithy
@ -236,6 +240,9 @@ operation:
}
previewEndpoint: {
value: true
},
supportedPrefixes: {
value: ["host", "id", "resourceId"]
}
)
operation GetThing {}

View File

@ -91,13 +91,13 @@ A parameter object contains the following properties:
- Description
* - type
- ``string``
- **Required**. MUST be one of ``string`` or ``boolean``.
- **Required**. MUST be one of ``string``, ``boolean``, or ``stringArray``.
* - builtIn
- ``string``
- Specifies a named built-in value that is sourced and provided to the
endpoint provider by a caller.
* - default
- ``string`` or ``boolean``
- ``string``, ``boolean`` or an array of ``string``.
- Specifies the default value for the parameter if not set. Parameters
with defaults MUST also be marked as ``required``. The type of the
provided default MUST match ``type``.

View File

@ -45,6 +45,9 @@ public interface Type {
if (parameterType == ParameterType.BOOLEAN) {
return booleanType();
}
if (parameterType == ParameterType.STRING_ARRAY) {
return arrayType(stringType());
}
throw new IllegalArgumentException("Unexpected parameter type: " + parameterType);
}

View File

@ -55,7 +55,7 @@ public final class ArrayValue extends Value {
} else {
Type first = values.get(0).getType();
for (Value value : values) {
if (value.getType() != first) {
if (!value.getType().isA(first)) {
throw new SourceException("An array cannot contain different types", this);
}
}

View File

@ -105,7 +105,9 @@ public final class GetAttr extends LibraryFunction {
throw new InvalidRulesException("Invalid path component: slice index must be >= 0",
sourceLocation);
}
result.add(Part.Key.of(component.substring(0, slicePartIndex)));
if (slicePartIndex > 0) {
result.add(Part.Key.of(component.substring(0, slicePartIndex)));
}
result.add(new Part.Index(slice));
} catch (NumberFormatException ex) {
throw new InvalidRulesException(String.format("%s could not be parsed as a number", slicePart),

View File

@ -10,6 +10,7 @@ import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.StringNode;
import software.amazon.smithy.model.shapes.ShapeType;
import software.amazon.smithy.rulesengine.language.error.RuleError;
import software.amazon.smithy.rulesengine.language.evaluation.type.ArrayType;
import software.amazon.smithy.rulesengine.language.evaluation.type.BooleanType;
import software.amazon.smithy.rulesengine.language.evaluation.type.StringType;
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
@ -28,7 +29,12 @@ public enum ParameterType {
/**
* A "boolean" parameter type.
*/
BOOLEAN;
BOOLEAN,
/**
* An array (list) of strings parameter type.
*/
STRING_ARRAY;
/**
* Creates a {@link ParameterType} of a specific type from the given Node information.
@ -39,14 +45,18 @@ public enum ParameterType {
*/
public static ParameterType fromNode(StringNode node) throws RuleError {
String value = node.getValue();
if (value.equalsIgnoreCase("String")) {
if (value.equalsIgnoreCase("string")) {
return STRING;
}
if (value.equalsIgnoreCase("Boolean")) {
if (value.equalsIgnoreCase("boolean")) {
return BOOLEAN;
}
if (value.equals("stringArray")) {
return STRING_ARRAY;
}
throw new RuleError(new SourceException(
String.format("Unexpected parameter type `%s`. Expected `String` or `Boolean`.", value), node));
String.format("Unexpected parameter type `%s`. Expected `string`, `boolean`, or `stringArray`.",
value), node));
}
/**
@ -63,6 +73,17 @@ public enum ParameterType {
if (node.isBooleanNode()) {
return BOOLEAN;
}
if (node.isArrayNode()) {
// confirm all elements are Strings
node.expectArrayNode().getElements().forEach(memberNode -> {
if (!memberNode.isStringNode()) {
throw new RuleError(new SourceException(
String.format("Unexpected array member parameter type `%s`. Expected a string.",
memberNode.getType()), memberNode));
}
});
return STRING_ARRAY;
}
throw new RuleError(new SourceException(
String.format("Unexpected parameter type `%s`. Expected a string or boolean.", node.getType()), node));
}
@ -81,8 +102,14 @@ public enum ParameterType {
if (type instanceof BooleanType) {
return BOOLEAN;
}
if (type instanceof ArrayType) {
ArrayType arrayType = (ArrayType) type;
if (arrayType.getMember().isA(Type.stringType()) || arrayType.getMember().isA(Type.emptyType())) {
return STRING_ARRAY;
}
}
throw new RuntimeException(
String.format("Unexpected parameter type `%s`. Expected a string or boolean.", type));
String.format("Unexpected parameter type `%s`. Expected a string, boolean, or array<string>.", type));
}
/**
@ -105,6 +132,16 @@ public enum ParameterType {
@Override
public String toString() {
return this == STRING ? "String" : "Boolean";
// Inconsistent casing on string/boolean to preserve backwards compatibility in serialization
switch (this) {
case STRING:
return "String";
case BOOLEAN:
return "Boolean";
case STRING_ARRAY:
return "stringArray";
default:
return "Unknown Type";
}
}
}

View File

@ -31,7 +31,7 @@ public final class StaticContextParamsTraitValidator extends AbstractValidator {
.orElse(Collections.emptyMap());
for (Map.Entry<String, StaticContextParamDefinition> entry : definitionMap.entrySet()) {
Node node = entry.getValue().getValue();
if (node.isStringNode() || node.isBooleanNode()) {
if (supportedType(node)) {
continue;
}
events.add(error(operationShape,
@ -45,4 +45,17 @@ public final class StaticContextParamsTraitValidator extends AbstractValidator {
}
return events;
}
private static boolean supportedType(Node node) {
if (node.isStringNode() || node.isBooleanNode()) {
return true;
}
if (node.isArrayNode()) {
// all elements must be strings
return node.expectArrayNode().getElements().stream().allMatch(e -> e.isStringNode());
}
return false;
}
}

View File

@ -1,3 +1,4 @@
[WARNING] example#FizzBuzz: This shape applies a trait that is unstable: smithy.rules#clientContextParams | UnstableTrait
[WARNING] example#FizzBuzz: This shape applies a trait that is unstable: smithy.rules#endpointRuleSet | UnstableTrait
[WARNING] example#FizzBuzz: This shape applies a trait that is unstable: smithy.rules#endpointTests | UnstableTrait
[WARNING] example#GetThing: This shape applies a trait that is unstable: smithy.rules#staticContextParams | UnstableTrait.smithy.rules#staticContextParams

View File

@ -5,6 +5,7 @@ namespace example
use smithy.rules#clientContextParams
use smithy.rules#endpointRuleSet
use smithy.rules#endpointTests
use smithy.rules#staticContextParams
@clientContextParams(
bar: {type: "string", documentation: "a client string parameter"}
@ -30,10 +31,16 @@ use smithy.rules#endpointTests
default: "asdf"
documentation: "docs"
},
stringArrayParam: {
type: "stringArray",
required: true,
default: ["a", "b", "c"],
documentation: "docs"
}
},
rules: [
{
"documentation": "Template the region into the URI when FIPS is enabled",
"documentation": "Template baz into URI when bar is set",
"conditions": [
{
"fn": "isSet",
@ -49,6 +56,25 @@ use smithy.rules#endpointTests
},
"type": "endpoint"
},
{
"documentation": "Template first array value into URI",
"conditions": [
{
"fn": "getAttr",
"argv": [
{
"ref": "stringArrayParam"
},
"[0]"
],
"assign": "arrayValue"
}
],
"endpoint": {
"url": "https://example.com/{arrayValue}"
},
"type": "endpoint"
},
{
"conditions": [],
"documentation": "error fallthrough",
@ -94,6 +120,19 @@ use smithy.rules#endpointTests
}
},
{
"documentation": "Default array values used"
"params": {
}
"expect": {
"endpoint": {
"url": "https://example.com/a"
}
}
},
{
"params": {
"stringArrayParam": []
}
"documentation": "a documentation string",
"expect": {
"error": "endpoint error"
@ -106,6 +145,9 @@ service FizzBuzz {
operations: [GetThing]
}
@staticContextParams(
"stringArrayParam": {value: []}
)
operation GetThing {
input := {}
}

View File

@ -25,6 +25,10 @@ use smithy.rules#staticContextParams
"ExtraParameter": {
"type": "string",
"documentation": "docs"
},
"StringArrayParameter": {
"type": "stringArray",
documentation: "docs"
}
},
"rules": []
@ -38,7 +42,8 @@ service FizzBuzz {
@staticContextParams(
"ParameterFoo": {value: "foo"},
"ExtraParameter": {value: "someValue"}
"ExtraParameter": {value: "someValue"},
"StringArrayParameter": {value: ["a", "b", "c"]}
)
operation GetResource {
input: GetResourceInput

View File

@ -2,7 +2,7 @@
// while parsing the parameter `RegionName`
// at invalid-rules/invalid-param-type.json5:10
// while parsing the parameter type
// Unexpected parameter type `notastring`. Expected `String` or `Boolean`.
// Unexpected parameter type `notastring`. Expected `string`, `boolean`, or `stringArray`.
// at invalid-rules/invalid-param-type.json5:11
{
"version": "1.2",

View File

@ -4,5 +4,5 @@ namespace smithy.example
use smithy.rules#staticContextParams
@staticContextParams(arrayParam: {value: ["foo", "bar"]})
@staticContextParams(arrayParam: {value: ["foo", 3]})
operation OperationArray {}