diff --git a/ara/api/fields.py b/ara/api/fields.py new file mode 100644 index 00000000..df080f21 --- /dev/null +++ b/ara/api/fields.py @@ -0,0 +1,75 @@ +# Copyright (c) 2019 Red Hat, Inc. +# +# This file is part of ARA Records Ansible. +# +# ARA Records Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ARA Records Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ARA Records Ansible. If not, see . + +import hashlib +import json +import zlib + +from rest_framework import serializers + +from ara.api import models + +# Constants used for defaults which rely on compression so we don't need to +# reproduce this code elsewhere. +EMPTY_DICT = zlib.compress(json.dumps({}).encode("utf8")) +EMPTY_LIST = zlib.compress(json.dumps([]).encode("utf8")) +EMPTY_STRING = zlib.compress(json.dumps("").encode("utf8")) + + +class CompressedTextField(serializers.CharField): + """ + Compresses text before storing it in the database. + Decompresses text from the database before serving it. + """ + + def to_representation(self, obj): + return zlib.decompress(obj).decode("utf8") + + def to_internal_value(self, data): + return zlib.compress(data.encode("utf8")) + + +class CompressedObjectField(serializers.JSONField): + """ + Serializes/compresses an object (i.e, list, dict) before storing it in the + database. + Decompresses/deserializes an object before serving it. + """ + + def to_representation(self, obj): + return json.loads(zlib.decompress(obj).decode("utf8")) + + def to_internal_value(self, data): + return zlib.compress(json.dumps(data).encode("utf8")) + + +class FileContentField(serializers.CharField): + """ + Compresses text before storing it in the database. + Decompresses text from the database before serving it. + """ + + def to_representation(self, obj): + return zlib.decompress(obj.contents).decode("utf8") + + def to_internal_value(self, data): + contents = data.encode("utf8") + sha1 = hashlib.sha1(contents).hexdigest() + content_file, created = models.FileContent.objects.get_or_create( + sha1=sha1, defaults={"sha1": sha1, "contents": zlib.compress(contents)} + ) + return content_file diff --git a/ara/api/serializers.py b/ara/api/serializers.py index a442e917..a86403c7 100644 --- a/ara/api/serializers.py +++ b/ara/api/serializers.py @@ -15,47 +15,17 @@ # You should have received a copy of the GNU General Public License # along with ARA. If not, see . -import hashlib -import json import logging -import zlib from rest_framework import serializers -from ara.api import models +from ara.api import fields as ara_fields, models DATE_FORMAT = "(iso-8601: 2016-05-06T17:20:25.749489-04:00)" DURATION_FORMAT = "([DD] [HH:[MM:]]ss[.uuuuuu])" logger = logging.getLogger("ara.api.serializers") -class CompressedTextField(serializers.CharField): - """ - Compresses text before storing it in the database. - Decompresses text from the database before serving it. - """ - - def to_representation(self, obj): - return zlib.decompress(obj).decode("utf8") - - def to_internal_value(self, data): - return zlib.compress(data.encode("utf8")) - - -class CompressedObjectField(serializers.JSONField): - """ - Serializes/compresses an object (i.e, list, dict) before storing it in the - database. - Decompresses/deserializes an object before serving it. - """ - - def to_representation(self, obj): - return json.loads(zlib.decompress(obj).decode("utf8")) - - def to_internal_value(self, data): - return zlib.compress(json.dumps(data).encode("utf8")) - - class DurationSerializer(serializers.ModelSerializer): """ Serializer for duration-based fields @@ -79,31 +49,13 @@ class FileContentSerializer(serializers.ModelSerializer): fields = "__all__" -class FileContentField(serializers.CharField): - """ - Compresses text before storing it in the database. - Decompresses text from the database before serving it. - """ - - def to_representation(self, obj): - return zlib.decompress(obj.contents).decode("utf8") - - def to_internal_value(self, data): - contents = data.encode("utf8") - sha1 = hashlib.sha1(contents).hexdigest() - content_file, created = models.FileContent.objects.get_or_create( - sha1=sha1, defaults={"sha1": sha1, "contents": zlib.compress(contents)} - ) - return content_file - - class FileSerializer(serializers.ModelSerializer): class Meta: model = models.File fields = "__all__" sha1 = serializers.SerializerMethodField() - content = FileContentField() + content = ara_fields.FileContentField() @staticmethod def get_sha1(obj): @@ -115,7 +67,7 @@ class HostSerializer(serializers.ModelSerializer): model = models.Host fields = "__all__" - facts = CompressedObjectField(default=zlib.compress(json.dumps({}).encode("utf8"))) + facts = ara_fields.CompressedObjectField(default=ara_fields.EMPTY_DICT) def get_unique_together_validators(self): """ @@ -138,7 +90,7 @@ class ResultSerializer(DurationSerializer): model = models.Result fields = "__all__" - content = CompressedObjectField(default=zlib.compress(json.dumps({}).encode("utf8"))) + content = ara_fields.CompressedObjectField(default=ara_fields.EMPTY_DICT) class LabelSerializer(serializers.ModelSerializer): @@ -146,8 +98,8 @@ class LabelSerializer(serializers.ModelSerializer): model = models.Label fields = "__all__" - description = CompressedTextField( - default=zlib.compress(json.dumps("").encode("utf8")), help_text="A textual description of the label" + description = ara_fields.CompressedTextField( + default=ara_fields.EMPTY_STRING, help_text="A text description of the label" ) @@ -156,9 +108,7 @@ class TaskSerializer(DurationSerializer): model = models.Task fields = "__all__" - tags = CompressedObjectField( - default=zlib.compress(json.dumps([]).encode("utf8")), help_text="A JSON list containing Ansible tags" - ) + tags = ara_fields.CompressedObjectField(default=ara_fields.EMPTY_LIST, help_text="A list containing Ansible tags") class SimpleTaskSerializer(serializers.ModelSerializer): @@ -172,9 +122,8 @@ class RecordSerializer(serializers.ModelSerializer): model = models.Record fields = "__all__" - value = CompressedObjectField( - default=zlib.compress(json.dumps("").encode("utf8")), - help_text="A string, list, dict, json or other formatted data", + value = ara_fields.CompressedObjectField( + default=ara_fields.EMPTY_STRING, help_text="A string, list, dict, json or other formatted data" ) @@ -204,7 +153,7 @@ class PlaybookSerializer(DurationSerializer): model = models.Playbook fields = "__all__" - arguments = CompressedObjectField(default=zlib.compress(json.dumps({}).encode("utf8"))) + arguments = ara_fields.CompressedObjectField(default=ara_fields.EMPTY_DICT) files = FileSerializer(many=True, default=[]) hosts = HostSerializer(many=True, default=[]) labels = LabelSerializer(many=True, default=[])