Skip to content

Commit

Permalink
Implement config constraints (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
jterapin committed Aug 9, 2023
1 parent ab0863d commit f5c5592
Show file tree
Hide file tree
Showing 16 changed files with 231 additions and 37 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion codegen/projections/rails_json/lib/rails_json/config.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion codegen/projections/weather/lib/weather/config.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion codegen/projections/white_label/lib/white_label/config.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion codegen/projections/white_label/spec/compression_spec.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ module WhiteLabel

context 'request_min_compression_size_bytes' do
it 'raises error when given invalid integer' do
# TODO: after we implement constraints on configs
expect { Config.new(request_min_compression_size_bytes: -1) }
.to raise_error(
ArgumentError,
'Expected config[:request_min_compression_size_bytes] ' \
'to be between 0 to 10485760, got -1.'
)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

package software.amazon.smithy.ruby.codegen.config;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import software.amazon.smithy.ruby.codegen.ClientFragment;
Expand All @@ -29,20 +31,19 @@
public class ClientConfig {

private final String name;
private final String type;
private final String documentation;
private final String documentationType;
private final ConfigDefaults defaults;
private final boolean allowOperationOverride;
private final List<ConfigConstraint> constraints;

/**
* @param builder builder to construct from.
*/
public ClientConfig(Builder builder) {
this.name = builder.name;
this.type = builder.type;
this.documentation = builder.documentation;
this.documentationType = builder.documentationType;
this.documentationType = builder.documentationType != null ? builder.documentationType : builder.type;
if (builder.defaults != null) {
this.defaults = builder.defaults;
} else {
Expand All @@ -52,6 +53,7 @@ public ClientConfig(Builder builder) {
this.defaults.setDocumentationDefault(builder.documentationDefaultValue);
}
this.allowOperationOverride = builder.allowOperationOverride;
this.constraints = List.copyOf(builder.constraints);
}

public static Builder builder() {
Expand All @@ -66,13 +68,6 @@ public String getName() {
return name;
}

/**
* @return The Ruby type of the config (eg String, Integer, Boolean, ect).
*/
public String getType() {
return type;
}

/**
* @return Documentation string to be added to the initialize method.
*/
Expand All @@ -94,10 +89,7 @@ public String getDocumentationDefaultValue() {
* @return Documented type
*/
public String getDocumentationType() {
if (documentationType != null) {
return documentationType;
}
return type;
return documentationType;
}

/**
Expand All @@ -109,6 +101,13 @@ public String renderDefaults(GenerationContext context) {
return defaults.renderDefault(context);
}

/**
* @return a list of ConfigConstraint objects.
*/
public List<ConfigConstraint> getConstraints() {
return constraints;
}

/**
* If true, this config can be overridden
* per operation.
Expand Down Expand Up @@ -147,12 +146,12 @@ public boolean equals(Object o) {
}
ClientConfig that = (ClientConfig) o;
return Objects.equals(getName(), that.getName())
&& Objects.equals(getType(), that.getType());
&& Objects.equals(getDocumentationType(), that.getDocumentationType());
}

@Override
public int hashCode() {
return Objects.hash(getName(), getType());
return Objects.hash(getName(), getDocumentationType());
}

/**
Expand All @@ -166,9 +165,10 @@ public static class Builder implements SmithyBuilder<ClientConfig> {
private String documentationType;
private ConfigDefaults defaults;
private boolean allowOperationOverride = false;
private final List<ConfigConstraint> constraints;

protected Builder() {

constraints = new ArrayList<>();
}

/**
Expand All @@ -186,6 +186,7 @@ public Builder name(String name) {
*/
public Builder type(String type) {
this.type = type;
this.constraints.add(0, new TypeConstraint(type));
return this;
}

Expand Down Expand Up @@ -282,6 +283,15 @@ public Builder defaultLiteral(String defaultLiteral) {
return this;
}

/**
* @param constraint a ConfigConstraint object.
* @return this builder.
*/
public Builder constraint(ConfigConstraint constraint) {
this.constraints.add(constraint);
return this;
}

@Override
public ClientConfig build() {
return new ClientConfig(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.smithy.ruby.codegen.config;

/**
* Represents the config value constraints for ClientConfig.
*/

public interface ConfigConstraint {
/**
* Render the constraint validator for the config value.
*
* @param configName the name of the config.
* @return the string to be rendered into Ruby code.
*/
String render(String configName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.smithy.ruby.codegen.config;

/**
* Range Constraint for config value.
*/
public class RangeConstraint implements ConfigConstraint {

private final long minValue;
private final long maxValue;

/**
* @param minValue the minimum value
* @param maxValue the maximum value
*/
public RangeConstraint(long minValue, long maxValue) {
this.maxValue = maxValue;
this.minValue = minValue;
}

@Override
public String render(String configName) {
return String.format(
"Hearth::Validator.validate_range!(%s, min: %d, max: %d, context: 'config[:%s]')",
configName, minValue, maxValue, configName);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.smithy.ruby.codegen.config;

/**
* Type Constraint for config value.
*/
public class TypeConstraint implements ConfigConstraint {

private final String type;

/**
* @param type ruby type for the config. Used for validation, must be a valid Ruby class or Boolean.
*/
public TypeConstraint(String type) {
if (type.equals("Boolean")) {
this.type = "TrueClass, FalseClass";
} else {
this.type = type;
}
}

@Override
public String render(String configName) {
return String.format(
"Hearth::Validator.validate_types!(%s, %s, context: 'config[:%s]')",
configName, type, configName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private void renderConfigDocumentation(RubyCodeWriter writer) {
writer.writeYardMethod("initialize(*options)", () -> {
clientConfigList.forEach((clientConfig) -> {
String member = RubyFormatter.asSymbol(RubySymbolProvider.toMemberName(clientConfig.getName()));
String returnType = clientConfig.getType();
String returnType = clientConfig.getDocumentationType();
String defaultValue = clientConfig.getDocumentationDefaultValue();
String documentation = clientConfig.getDocumentation();
writer.writeYardOption("args", returnType, member, defaultValue, documentation);
Expand All @@ -115,16 +115,11 @@ private void renderConfigDocumentation(RubyCodeWriter writer) {
}

private void renderValidateMethod(RubyCodeWriter writer) {
writer.openBlock("def validate_types!");
clientConfigList.stream().forEach(clientConfig -> {
writer.openBlock("def validate!");
clientConfigList.forEach(clientConfig -> {
String member = RubySymbolProvider.toMemberName(clientConfig.getName());
String type = clientConfig.getType();
if (type.equals("Boolean")) {
type = "TrueClass, FalseClass";
}
writer.write("$3T.validate_types!($1L, $2L, context: 'config[:$1L]')",
member, type, Hearth.VALIDATOR);
// TODO - add constraints here
clientConfig.getConstraints().forEach(c ->
writer.write(c.render(member)));
});
writer.closeBlock("end");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import software.amazon.smithy.ruby.codegen.GenerationContext;
import software.amazon.smithy.ruby.codegen.RubyIntegration;
import software.amazon.smithy.ruby.codegen.config.ClientConfig;
import software.amazon.smithy.ruby.codegen.config.RangeConstraint;
import software.amazon.smithy.ruby.codegen.middleware.Middleware;
import software.amazon.smithy.ruby.codegen.middleware.MiddlewareBuilder;
import software.amazon.smithy.ruby.codegen.middleware.MiddlewareStackStep;
Expand Down Expand Up @@ -62,6 +63,7 @@ public void modifyClientMiddleware(MiddlewareBuilder middlewareBuilder, Generati
.documentation(minCompressionDocumentation)
.allowOperationOverride()
.defaultPrimitiveValue("10240")
.constraint(new RangeConstraint(0, 10485760))
.build();

Middleware compression = Middleware.builder()
Expand Down
2 changes: 1 addition & 1 deletion hearth/lib/hearth/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Configuration
def initialize(**options)
Hearth::Validator.validate_unknown!(self, options, context: 'config')
Hearth::Config::Resolver.resolve(self, options, self.class.defaults)
validate_types!
validate!
end

def dup
Expand Down
Loading

0 comments on commit f5c5592

Please sign in to comment.