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

feat: add support for annotated python dicts as avro map type #433

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
11 changes: 11 additions & 0 deletions openedx_events/event_bus/avro/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@
# check whether list items type is in basic types.
if arg_data_type[0] in SIMPLE_PYTHON_TYPE_TO_AVRO_MAPPING:
return data
elif data_type_origin == dict:
# returns types of dict contents
# if data_type == Dict[str, int], arg_data_type = (str, int)
arg_data_type = get_args(data_type)
if not arg_data_type:
raise TypeError(

Check warning on line 59 in openedx_events/event_bus/avro/deserializer.py

View check run for this annotation

Codecov / codecov/patch

openedx_events/event_bus/avro/deserializer.py#L59

Added line #L59 was not covered by tests
"Dict without annotation type is not supported. The argument should be a type, for eg., Dict[str, int]"
)
# check whether dict items type is in basic types.
if arg_data_type[1] in SIMPLE_PYTHON_TYPE_TO_AVRO_MAPPING:
return data
elif hasattr(data_type, "__attrs_attrs__"):
transformed = {}
for attribute in data_type.__attrs_attrs__:
Expand Down
17 changes: 16 additions & 1 deletion openedx_events/event_bus/avro/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
field["type"] = field_type
# Case 2: data_type is a simple type that can be converted directly to an Avro type
elif data_type in PYTHON_TYPE_TO_AVRO_MAPPING:
if PYTHON_TYPE_TO_AVRO_MAPPING[data_type] in ["record", "array"]:
if PYTHON_TYPE_TO_AVRO_MAPPING[data_type] in ["map", "array"]:
# pylint: disable-next=broad-exception-raised
raise Exception("Unable to generate Avro schema for dict or array fields without annotation types.")
avro_type = PYTHON_TYPE_TO_AVRO_MAPPING[data_type]
Expand All @@ -83,6 +83,21 @@
f" {set(SIMPLE_PYTHON_TYPE_TO_AVRO_MAPPING.keys())}"
)
field["type"] = {"type": PYTHON_TYPE_TO_AVRO_MAPPING[data_type_origin], "items": avro_type}
elif data_type_origin == dict:
# returns types of dict contents
# if data_type == Dict[str, int], arg_data_type = (str, int)
Comment on lines +87 to +88
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove code comments

arg_data_type = get_args(data_type)
if not arg_data_type:
raise TypeError(

Check warning on line 91 in openedx_events/event_bus/avro/schema.py

View check run for this annotation

Codecov / codecov/patch

openedx_events/event_bus/avro/schema.py#L91

Added line #L91 was not covered by tests
Comment on lines +90 to +91
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a failure test case?

"Dict without annotation type is not supported. The argument should be a type, for eg., Dict[str, int]"
)
avro_type = SIMPLE_PYTHON_TYPE_TO_AVRO_MAPPING.get(arg_data_type[1])
if avro_type is None:
raise TypeError(

Check warning on line 96 in openedx_events/event_bus/avro/schema.py

View check run for this annotation

Codecov / codecov/patch

openedx_events/event_bus/avro/schema.py#L96

Added line #L96 was not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

"Only following types are supported for dict arguments:"
f" {set(SIMPLE_PYTHON_TYPE_TO_AVRO_MAPPING.keys())}"
)
field["type"] = {"type": PYTHON_TYPE_TO_AVRO_MAPPING[data_type_origin], "values": avro_type}
# Case 3: data_type is an attrs class
elif hasattr(data_type, "__attrs_attrs__"):
# Inner Attrs Class
Expand Down
1 change: 1 addition & 0 deletions openedx_events/event_bus/avro/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def _event_data_to_avro_record_dict(event_data, serializers=None):
def value_to_dict(value):
# Case 1: Value is an instance of an attrs-decorated class
if hasattr(value, "__attrs_attrs__"):
print("\n\n MY VALUE IN VALUE TO DICT", value, "\n\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove unnecessary code

return attr.asdict(value, value_serializer=_get_non_attrs_serializer(serializers))
return _get_non_attrs_serializer(serializers)(None, None, value)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
{
"name": "CloudEvent",
"type": "record",
"doc": "Avro Event Format for CloudEvents created with openedx_events/schema",
"fields": [
{
"name": "thread",
"type": {
"name": "DiscussionThreadData",
"type": "record",
"fields": [
{
"name": "body",
"type": "string"
},
{
"name": "commentable_id",
"type": "string"
},
{
"name": "id",
"type": "string"
},
{
"name": "truncated",
"type": "boolean"
},
{
"name": "url",
"type": "string"
},
{
"name": "user",
"type": {
"name": "UserData",
"type": "record",
"fields": [
{
"name": "id",
"type": "long"
},
{
"name": "is_active",
"type": "boolean"
},
{
"name": "pii",
"type": {
"name": "UserPersonalData",
"type": "record",
"fields": [
{
"name": "username",
"type": "string"
},
{
"name": "email",
"type": "string"
},
{
"name": "name",
"type": "string"
}
]
}
}
]
}
},
{
"name": "course_id",
"type": "string"
},
{
"name": "thread_type",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "anonymous",
"type": [
"null",
"boolean"
],
"default": null
},
{
"name": "anonymous_to_peers",
"type": [
"null",
"boolean"
],
"default": null
},
{
"name": "title",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "title_truncated",
"type": [
"null",
"boolean"
],
"default": null
},
{
"name": "group_id",
"type": [
"null",
"long"
],
"default": null
},
{
"name": "team_id",
"type": [
"null",
"long"
],
"default": null
},
{
"name": "category_id",
"type": [
"null",
"long"
],
"default": null
},
{
"name": "category_name",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "discussion",
"type": [
"null",
{
"type": "map",
"values": "string"
}
],
"default": null
},
{
"name": "user_course_roles",
"type": {
"type": "array",
"items": "string"
}
},
{
"name": "user_forums_roles",
"type": {
"type": "array",
"items": "string"
}
},
{
"name": "options",
"type": {
"type": "map",
"values": "boolean"
}
}
]
}
}
],
"namespace": "org.openedx.learning.forum.thread.created.v1"
}
Loading
Loading