Skip to content

Commit

Permalink
Add support for string array request parameter to endpoint resolution (
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitriyMusatkin committed Aug 2, 2024
1 parent 20ea8a4 commit ac3ac9d
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 88 deletions.
20 changes: 18 additions & 2 deletions include/aws/sdkutils/endpoints_rule_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ struct aws_endpoints_resolved_endpoint;
struct aws_endpoints_request_context;
struct aws_hash_table;

enum aws_endpoints_parameter_type { AWS_ENDPOINTS_PARAMETER_STRING, AWS_ENDPOINTS_PARAMETER_BOOLEAN };
enum aws_endpoints_parameter_type {
AWS_ENDPOINTS_PARAMETER_STRING,
AWS_ENDPOINTS_PARAMETER_BOOLEAN,
AWS_ENDPOINTS_PARAMETER_STRING_ARRAY
};
enum aws_endpoints_resolved_endpoint_type { AWS_ENDPOINTS_RESOLVED_ENDPOINT, AWS_ENDPOINTS_RESOLVED_ERROR };

AWS_EXTERN_C_BEGIN
Expand Down Expand Up @@ -221,7 +225,6 @@ AWS_SDKUTILS_API int aws_endpoints_request_context_add_string(

/*
* Add boolean value to request context.
* Note: this function will make a copy of the memory backing the cursors.
* The function will override any previous value stored in the context with the
* same name.
*/
Expand All @@ -231,6 +234,19 @@ AWS_SDKUTILS_API int aws_endpoints_request_context_add_boolean(
struct aws_byte_cursor name,
bool value);

/*
* Add string array value to request context.
* Note: this function will make a copy of the memory backing the cursors.
* The function will override any previous value stored in the context with the
* same name.
*/
AWS_SDKUTILS_API int aws_endpoints_request_context_add_string_array(
struct aws_allocator *allocator,
struct aws_endpoints_request_context *context,
struct aws_byte_cursor name,
struct aws_byte_cursor *values,
size_t len);

/*
* Resolve an endpoint given request context.
* Resolved endpoint is returned through out_resolved_endpoint.
Expand Down
61 changes: 30 additions & 31 deletions include/aws/sdkutils/private/endpoints_types_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,35 @@ enum aws_endpoints_fn_type {
AWS_ENDPOINTS_FN_LAST,
};

enum aws_endpoints_value_type {
/* Special value to represent that any value type is expected from resolving an expresion.
Note a valid value for a value type. */
AWS_ENDPOINTS_VALUE_ANY,

AWS_ENDPOINTS_VALUE_NONE,
AWS_ENDPOINTS_VALUE_STRING,
AWS_ENDPOINTS_VALUE_BOOLEAN,
AWS_ENDPOINTS_VALUE_OBJECT, /* Generic type returned by some functions. json string under the covers. */
AWS_ENDPOINTS_VALUE_NUMBER,
AWS_ENDPOINTS_VALUE_ARRAY,

AWS_ENDPOINTS_VALUE_SIZE
};

/* concrete type value */
struct aws_endpoints_value {
enum aws_endpoints_value_type type;
union {
struct aws_owning_cursor owning_cursor_string;
bool boolean;
struct aws_owning_cursor owning_cursor_object;
double number;
struct aws_array_list array;
} v;
/* Value is a reference to another value, no need to clean it up. */
bool is_ref;
};

struct aws_endpoints_parameter {
struct aws_allocator *allocator;

Expand All @@ -80,10 +109,7 @@ struct aws_endpoints_parameter {
struct aws_byte_cursor built_in;

bool has_default_value;
union {
struct aws_byte_cursor string;
bool boolean;
} default_value;
struct aws_endpoints_value default_value;

bool is_required;
struct aws_byte_cursor documentation;
Expand Down Expand Up @@ -210,40 +236,13 @@ struct aws_partitions_config {
******************************
*/

enum aws_endpoints_value_type {
/* Special value to represent that any value type is expected from resolving an expresion.
Note a valid value for a value type. */
AWS_ENDPOINTS_VALUE_ANY,

AWS_ENDPOINTS_VALUE_NONE,
AWS_ENDPOINTS_VALUE_STRING,
AWS_ENDPOINTS_VALUE_BOOLEAN,
AWS_ENDPOINTS_VALUE_OBJECT, /* Generic type returned by some functions. json string under the covers. */
AWS_ENDPOINTS_VALUE_NUMBER,
AWS_ENDPOINTS_VALUE_ARRAY,

AWS_ENDPOINTS_VALUE_SIZE
};

struct aws_endpoints_request_context {
struct aws_allocator *allocator;
struct aws_ref_count ref_count;

struct aws_hash_table values;
};

/* concrete type value */
struct aws_endpoints_value {
enum aws_endpoints_value_type type;
union {
struct aws_owning_cursor owning_cursor_string;
bool boolean;
struct aws_owning_cursor owning_cursor_object;
double number;
struct aws_array_list array;
} v;
};

/* wrapper around aws_endpoints_value to store it more easily in hash table*/
struct aws_endpoints_scope_value {
struct aws_allocator *allocator;
Expand Down
87 changes: 60 additions & 27 deletions source/endpoints_rule_engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,11 @@ static int s_init_top_level_scope(

switch (value->type) {
case AWS_ENDPOINTS_PARAMETER_STRING:
val->value.type = AWS_ENDPOINTS_VALUE_STRING;
val->value.v.owning_cursor_string =
aws_endpoints_non_owning_cursor_create(value->default_value.string);
break;
case AWS_ENDPOINTS_PARAMETER_BOOLEAN:
val->value.type = AWS_ENDPOINTS_VALUE_BOOLEAN;
val->value.v.boolean = value->default_value.boolean;
case AWS_ENDPOINTS_PARAMETER_STRING_ARRAY:
val->value = value->default_value;
val->value.is_ref = true;
break;
break;
default:
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_RESOLVE, "Unexpected parameter type.");
Expand Down Expand Up @@ -310,8 +308,21 @@ static int s_resolve_expr(
}
case AWS_ENDPOINTS_EXPR_ARRAY: {
out_value->type = AWS_ENDPOINTS_VALUE_ARRAY;
/* TODO: deep copy */
out_value->v.array = expr->e.array;
{
size_t len = aws_array_list_length(&expr->e.array);
aws_array_list_init_dynamic(&out_value->v.array, allocator, len, sizeof(struct aws_endpoints_value));
for (size_t i = 0; i < len; ++i) {
struct aws_endpoints_expr expr_elem;
aws_array_list_get_at(&expr->e.array, &expr_elem, i);
struct aws_endpoints_value val;
if (s_resolve_expr(allocator, &expr_elem, scope, &val)) {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_RESOLVE, "Failed to resolve array element.");
aws_endpoints_value_clean_up(out_value);
goto on_error;
}
aws_array_list_set_at(&out_value->v.array, &val, i);
}
}
break;
}
case AWS_ENDPOINTS_EXPR_REFERENCE: {
Expand All @@ -325,14 +336,9 @@ static int s_resolve_expr(
out_value->type = AWS_ENDPOINTS_VALUE_NONE;
} else {
struct aws_endpoints_scope_value *aws_endpoints_scope_value = element->value;

*out_value = aws_endpoints_scope_value->value;
if (aws_endpoints_scope_value->value.type == AWS_ENDPOINTS_VALUE_STRING) {
/* Value will not own underlying mem and instead its owned
by the scope, so set it to NULL. */
out_value->v.owning_cursor_string.string = NULL;
} else if (aws_endpoints_scope_value->value.type == AWS_ENDPOINTS_VALUE_OBJECT) {
out_value->v.owning_cursor_object.string = NULL;
}
out_value->is_ref = true;
}
break;
}
Expand Down Expand Up @@ -450,6 +456,8 @@ int aws_endpoints_path_through_array(
struct aws_endpoints_value *value,
struct aws_byte_cursor path_cur,
struct aws_endpoints_value *out_value) {
(void)allocator;
(void)scope;

AWS_PRECONDITION(value->type == AWS_ENDPOINTS_VALUE_ARRAY);

Expand All @@ -461,25 +469,20 @@ int aws_endpoints_path_through_array(
goto on_error;
}

if (index < aws_array_list_length(&value->v.array)) {
if (index >= aws_array_list_length(&value->v.array)) {
out_value->type = AWS_ENDPOINTS_VALUE_NONE;
return AWS_OP_SUCCESS;
}

struct aws_endpoints_expr *expr = NULL;
if (aws_array_list_get_at_ptr(&value->v.array, (void **)&expr, (size_t)index)) {
struct aws_endpoints_value *val = NULL;
if (aws_array_list_get_at_ptr(&value->v.array, (void **)&val, (size_t)index)) {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_RESOLVE, "Failed to index into resolved value");
goto on_error;
}

struct aws_endpoints_value val;
if (s_resolve_expr(allocator, expr, scope, &val)) {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_RESOLVE, "Failed to resolve val.");
aws_endpoints_value_clean_up(&val);
goto on_error;
}
*out_value = *val;
out_value->is_ref = true;

*out_value = val;
return AWS_OP_SUCCESS;

on_error:
Expand Down Expand Up @@ -597,7 +600,10 @@ static int s_resolve_templated_value_with_pathing(
goto on_error;
}
} else {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_RESOLVE, "Invalid value type for pathing through.");
AWS_LOGF_ERROR(
AWS_LS_SDKUTILS_ENDPOINTS_RESOLVE,
"Invalid value type for pathing through. type %d",
scope_value->value.type);
goto on_error;
}

Expand Down Expand Up @@ -737,6 +743,34 @@ int aws_endpoints_request_context_add_boolean(
return AWS_OP_SUCCESS;
}

int aws_endpoints_request_context_add_string_array(
struct aws_allocator *allocator,
struct aws_endpoints_request_context *context,
struct aws_byte_cursor name,
struct aws_byte_cursor *values,
size_t len) {

struct aws_endpoints_scope_value *val = aws_endpoints_scope_value_new(allocator, name);
val->value.type = AWS_ENDPOINTS_VALUE_ARRAY;
aws_array_list_init_dynamic(&val->value.v.array, allocator, len, sizeof(struct aws_endpoints_value));

for (size_t i = 0; i < len; ++i) {
struct aws_endpoints_value elem = {
.is_ref = false,
.type = AWS_ENDPOINTS_VALUE_STRING,
.v.owning_cursor_object = aws_endpoints_owning_cursor_from_cursor(allocator, values[i])};

aws_array_list_set_at(&val->value.v.array, &elem, i);
}

if (aws_hash_table_put(&context->values, &val->name.cur, val, NULL)) {
aws_endpoints_scope_value_destroy(val);
return aws_raise_error(AWS_ERROR_SDKUTILS_ENDPOINTS_RESOLVE_INIT_FAILED);
};

return AWS_OP_SUCCESS;
}

/*
******************************
* Rule engine.
Expand Down Expand Up @@ -1126,7 +1160,6 @@ int aws_endpoints_rule_engine_resolve(
result = aws_raise_error(AWS_ERROR_SDKUTILS_ENDPOINTS_RULESET_EXHAUSTED);

on_done:
AWS_LOGF_DEBUG(AWS_LS_SDKUTILS_ENDPOINTS_RESOLVE, "Resolved endpoint with status %d", result);
s_scope_clean_up(&scope);
return result;
}
54 changes: 42 additions & 12 deletions source/endpoints_ruleset.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
/* parameter types */
static struct aws_byte_cursor s_string_type_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("string");
static struct aws_byte_cursor s_boolean_type_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("boolean");
static struct aws_byte_cursor s_string_array_type_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("stringArray");

/* rule types */
static struct aws_byte_cursor s_endpoint_type_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("endpoint");
Expand Down Expand Up @@ -52,7 +53,7 @@ int aws_endpoints_parameter_get_default_string(
AWS_PRECONDITION(out_cursor);

if (parameter->type == AWS_ENDPOINTS_PARAMETER_STRING) {
*out_cursor = parameter->default_value.string;
*out_cursor = parameter->default_value.v.owning_cursor_string.cur;
return AWS_OP_SUCCESS;
};

Expand All @@ -67,7 +68,7 @@ int aws_endpoints_parameter_get_default_boolean(
AWS_PRECONDITION(out_bool);

if (parameter->type == AWS_ENDPOINTS_PARAMETER_BOOLEAN) {
*out_bool = &parameter->default_value.boolean;
*out_bool = &parameter->default_value.v.boolean;
return AWS_OP_SUCCESS;
};

Expand Down Expand Up @@ -405,6 +406,8 @@ static int s_on_parameter_key(
type = AWS_ENDPOINTS_PARAMETER_STRING;
} else if (aws_byte_cursor_eq_ignore_case(&type_cur, &s_boolean_type_cur)) {
type = AWS_ENDPOINTS_PARAMETER_BOOLEAN;
} else if (aws_byte_cursor_eq_ignore_case(&type_cur, &s_string_array_type_cur)) {
type = AWS_ENDPOINTS_PARAMETER_STRING_ARRAY;
} else {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_PARSING, "Unexpected type for parameter.");
goto on_error;
Expand Down Expand Up @@ -446,13 +449,40 @@ static int s_on_parameter_key(
struct aws_json_value *default_node = aws_json_value_get_from_object(value, aws_byte_cursor_from_c_str("default"));
parameter->has_default_value = default_node != NULL;
if (default_node != NULL) {
if (type == AWS_ENDPOINTS_PARAMETER_STRING &&
aws_json_value_get_string(default_node, &parameter->default_value.string)) {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_PARSING, "Unexpected type for default parameter value.");
goto on_error;
} else if (
type == AWS_ENDPOINTS_PARAMETER_BOOLEAN &&
aws_json_value_get_boolean(default_node, &parameter->default_value.boolean)) {
if (type == AWS_ENDPOINTS_PARAMETER_STRING && aws_json_value_is_string(default_node)) {
struct aws_byte_cursor cur;
aws_json_value_get_string(default_node, &cur);
parameter->default_value.type = AWS_ENDPOINTS_VALUE_STRING;
parameter->default_value.v.owning_cursor_string = aws_endpoints_non_owning_cursor_create(cur);
} else if (type == AWS_ENDPOINTS_PARAMETER_BOOLEAN && aws_json_value_is_boolean(default_node)) {
parameter->default_value.type = AWS_ENDPOINTS_VALUE_BOOLEAN;
aws_json_value_get_boolean(default_node, &parameter->default_value.v.boolean);
} else if (type == AWS_ENDPOINTS_PARAMETER_STRING_ARRAY && aws_json_value_is_array(default_node)) {
parameter->default_value.type = AWS_ENDPOINTS_VALUE_ARRAY;
size_t len = aws_json_get_array_size(default_node);
aws_array_list_init_dynamic(
&parameter->default_value.v.array, wrapper->allocator, len, sizeof(struct aws_endpoints_value));
for (size_t i = 0; i < len; ++i) {
struct aws_json_value *element = aws_json_get_array_element(default_node, i);
if (!aws_json_value_is_string(element)) {
AWS_LOGF_ERROR(
AWS_LS_SDKUTILS_ENDPOINTS_PARSING,
"Unexpected type for default parameter value. String array parameter must have string "
"elements");
goto on_error;
}

struct aws_byte_cursor cur;
aws_json_value_get_string(element, &cur);

struct aws_endpoints_value val = {
.is_ref = false,
.type = AWS_ENDPOINTS_VALUE_STRING,
.v.owning_cursor_string = aws_endpoints_non_owning_cursor_create(cur)};

aws_array_list_set_at(&parameter->default_value.v.array, &val, i);
}
} else {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_PARSING, "Unexpected type for default parameter value.");
goto on_error;
}
Expand Down Expand Up @@ -643,7 +673,7 @@ static int s_parse_endpoints_rule_data_endpoint(
if (headers_node != NULL) {

if (s_init_members_from_json(allocator, headers_node, &data_rule->headers, s_on_headers_key)) {
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_PARSING, "Failed to extract parameters.");
AWS_LOGF_ERROR(AWS_LS_SDKUTILS_ENDPOINTS_PARSING, "Failed to extract headers.");
goto on_error;
}
}
Expand Down Expand Up @@ -914,12 +944,12 @@ static void s_endpoints_ruleset_destroy(void *data) {

struct aws_endpoints_ruleset *ruleset = data;

aws_json_value_destroy(ruleset->json_root);

aws_hash_table_clean_up(&ruleset->parameters);

aws_array_list_deep_clean_up(&ruleset->rules, s_on_rule_array_element_clean_up);

aws_json_value_destroy(ruleset->json_root);

aws_mem_release(ruleset->allocator, ruleset);
}

Expand Down
Loading

0 comments on commit ac3ac9d

Please sign in to comment.