Island: Fix representations.py to encode lists and objects

Previously lists and objects didn't get encoded. This forced explicit encoding on the endpoints, which is not necessary
This commit is contained in:
vakarisz 2022-07-11 15:38:02 +03:00
parent cd839884eb
commit da9f3c97ed
2 changed files with 63 additions and 52 deletions

View File

@ -6,35 +6,39 @@ from bson.json_util import dumps
from flask import make_response from flask import make_response
def normalize_obj(obj): def _normalize_obj(obj):
if ("_id" in obj) and ("id" not in obj): if ("_id" in obj) and ("id" not in obj):
obj["id"] = obj["_id"] obj["id"] = obj["_id"]
del obj["_id"] del obj["_id"]
for key, value in list(obj.items()): for key, value in list(obj.items()):
if isinstance(value, list): obj[key] = _normalize_value(value)
for i in range(0, len(value)):
obj[key][i] = _normalize_value(value[i])
else:
obj[key] = _normalize_value(value)
return obj return obj
def _normalize_value(value): def _normalize_value(value):
if isinstance(value, list):
for i in range(0, len(value)):
value[i] = _normalize_value(value[i])
if type(value) == dict: if type(value) == dict:
return normalize_obj(value) return _normalize_obj(value)
if isinstance(value, bson.objectid.ObjectId): if isinstance(value, bson.objectid.ObjectId):
return str(value) return str(value)
if isinstance(value, datetime): if isinstance(value, datetime):
return str(value) return str(value)
if issubclass(type(value), Enum): if issubclass(type(value), Enum):
return value.name return value.name
else:
return value try:
return value.__dict__
except AttributeError:
pass
return value
def output_json(obj, code, headers=None): def output_json(value, code, headers=None):
obj = normalize_obj(obj) value = _normalize_value(value)
resp = make_response(dumps(obj), code) resp = make_response(dumps(value), code)
resp.headers.extend(headers or {}) resp.headers.extend(headers or {})
return resp return resp

View File

@ -1,55 +1,62 @@
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum
from unittest import TestCase
import bson import bson
from monkey_island.cc.services.representations import normalize_obj from monkey_island.cc.services.representations import _normalize_value
class TestRepresentations(TestCase): @dataclass
def test_normalize_obj(self): class MockClass:
# empty a: int
self.assertEqual({}, normalize_obj({}))
# no special content
self.assertEqual({"a": "a"}, normalize_obj({"a": "a"}))
# _id field -> id field obj_id_str = "123456789012345678901234"
self.assertEqual({"id": 12345}, normalize_obj({"_id": 12345})) mock_object = MockClass(1)
# obj id field -> str
obj_id_str = "123456789012345678901234"
self.assertEqual(
{"id": obj_id_str}, normalize_obj({"_id": bson.objectid.ObjectId(obj_id_str)})
)
# datetime -> str def test_normalize_dicts():
dt = datetime.now() assert {} == _normalize_value({})
expected = {"a": str(dt)}
result = normalize_obj({"a": dt})
self.assertEqual(expected, result)
# dicts and lists assert {"a": "a"} == _normalize_value({"a": "a"})
self.assertEqual(
{"a": [{"ba": obj_id_str, "bb": obj_id_str}], "b": {"id": obj_id_str}},
normalize_obj(
{
"a": [
{
"ba": bson.objectid.ObjectId(obj_id_str),
"bb": bson.objectid.ObjectId(obj_id_str),
}
],
"b": {"_id": bson.objectid.ObjectId(obj_id_str)},
}
),
)
def test_normalize__enum(self): assert {"id": 12345} == _normalize_value({"_id": 12345})
class BogusEnum(Enum):
bogus_val = "Bogus"
my_obj = {"something": "something", "my_enum": BogusEnum.bogus_val} assert {"id": obj_id_str} == _normalize_value({"_id": bson.objectid.ObjectId(obj_id_str)})
assert {"something": "something", "my_enum": "bogus_val"} == normalize_obj(my_obj) dt = datetime.now()
expected = {"a": str(dt)}
result = _normalize_value({"a": dt})
assert expected == result
def test_normalize_complex():
mock_dict = {
"a": [
{
"ba": bson.objectid.ObjectId(obj_id_str),
"bb": bson.objectid.ObjectId(obj_id_str),
}
],
"b": {"_id": bson.objectid.ObjectId(obj_id_str)},
}
expected_dict = {"a": [{"ba": obj_id_str, "bb": obj_id_str}], "b": {"id": obj_id_str}}
assert expected_dict == _normalize_value(mock_dict)
def test_normalize_list():
mock_list = [bson.objectid.ObjectId(obj_id_str), {"a": "b"}, {"object": [mock_object]}]
expected_list = [obj_id_str, {"a": "b"}, {"object": [{"a": 1}]}]
assert expected_list == _normalize_value(mock_list)
def test_normalize__enum():
class BogusEnum(Enum):
bogus_val = "Bogus"
my_obj = {"something": "something", "my_enum": BogusEnum.bogus_val}
assert {"something": "something", "my_enum": "bogus_val"} == _normalize_value(my_obj)