diff --git a/runbot/fields.py b/runbot/fields.py new file mode 100644 index 00000000..56b5ee6a --- /dev/null +++ b/runbot/fields.py @@ -0,0 +1,50 @@ +from odoo.fields import Field, pycompat +import json +from collections import MutableMapping +from psycopg2.extras import Json + + +class JsonDictField(Field): + type = 'jsonb' + column_type = ('jsonb', 'jsonb') + column_cast_from = ('varchar',) + + def convert_to_write(self, value, record): + return value + + def convert_to_column(self, value, record, values=None, validate=True): + val = self.convert_to_cache(value, record, validate=validate) + return Json(value) if val else val + + def convert_to_cache(self, value, record, validate=True): + return value.dict if isinstance(value, FieldDict) else value if isinstance(value, dict) else None + + def convert_to_record(self, value, record): + return FieldDict(value or {}, self, record) + + +class FieldDict(MutableMapping): + + def __init__(self, init_dict, field, record): + self.field = field + self.record = record + self.dict = init_dict + + def __setitem__(self, key, value): + new = self.dict.copy() + new[key] = value + self.record[self.field.name] = new + + def __getitem__(self, key): + return self.dict[key] + + def __delitem__(self, key): + new = self.dict.copy() + del new[key] + self.record[self.field.name] = new + + def __iter__(self): + return iter(self.dict) + + def __len__(self): + return len(self.dict) diff --git a/runbot/models/build.py b/runbot/models/build.py index 80b59a7f..3ef032d9 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -10,6 +10,7 @@ import time import datetime from ..common import dt2time, fqdn, now, grep, uniq_list, local_pgadmin_cursor, s2human, Commit, dest_reg, os from ..container import docker_build, docker_stop, docker_state, Command +from ..fields import JsonDictField from odoo.addons.runbot.models.repo import RunbotException from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError @@ -54,6 +55,7 @@ class runbot_build(models.Model): sequence = fields.Integer('Sequence') log_ids = fields.One2many('ir.logging', 'build_id', string='Logs') error_log_ids = fields.One2many('ir.logging', 'build_id', domain=[('level', 'in', ['WARNING', 'ERROR', 'CRITICAL'])], string='Error Logs') + config_data = JsonDictField('Json Data') # state machine diff --git a/runbot/tests/test_build.py b/runbot/tests/test_build.py index 1d6974a8..8b497975 100644 --- a/runbot/tests/test_build.py +++ b/runbot/tests/test_build.py @@ -53,6 +53,14 @@ class Test_Build(RunbotCase): build._compute_domain() self.assertEqual(build.domain, 'runbot99.example.org:1234') + # test json stored _data field and data property + self.assertEqual(build.config_data, {}) + build.config_data = {'restore_url': 'foobar'} + self.assertEqual(build.config_data, {'restore_url': 'foobar'}) + build.config_data['test_info'] = 'dummy' + self.assertEqual(build.config_data, {"restore_url": "foobar", "test_info": "dummy"}) + del build.config_data['restore_url'] + self.assertEqual(build.config_data, {"test_info": "dummy"}) other = self.create_build({ 'branch_id': self.branch.id, 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', @@ -69,6 +77,18 @@ class Test_Build(RunbotCase): with self.assertRaises(AssertionError): builds.write({'local_state': 'duplicate'}) + # test non initialized json stored _data field and data property + other.config_data['test_info'] = 'foo' + self.assertEqual(other.config_data, {'test_info': 'foo'}) + (build|other).write({'config_data': {'test_write': 'written'}}) + build.config_data['test_build'] = 'foo' + other.config_data['test_other'] = 'bar' + self.assertEqual(build.config_data, {'test_write': 'written', 'test_build': 'foo'}) + self.assertEqual(other.config_data, {'test_write': 'written', 'test_other': 'bar'}) + build.flush() + build.env.cr.execute("SELECT config_data, config_data->'test_write' AS written, config_data->'test_build' AS test_build FROM runbot_build WHERE id = %s", [build.id]) + self.assertEqual([({'test_write': 'written', 'test_build': 'foo'}, 'written', 'foo')], self.env.cr.fetchall()) + @patch('odoo.addons.runbot.models.build.runbot_build._get_repo_available_modules') def test_filter_modules(self, mock_get_repo_mods): """ test module filtering """