Skip to content

Commit

Permalink
[IMP] server_environment: add possibility of auto creating records
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent-hatakeyama committed Jun 21, 2024
1 parent 40c12a6 commit 3c8a812
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 7 deletions.
54 changes: 48 additions & 6 deletions server_environment/models/server_env_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)

import logging
from ast import literal_eval
from functools import partialmethod

from lxml import etree
Expand All @@ -10,7 +11,7 @@

from odoo.addons.base_sparse_field.models.fields import Serialized

from ..server_env import serv_config
from .. import server_env

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -211,8 +212,11 @@ def _server_env_read_from_config(self, field_name, config_getter):
# at this point we should have checked that we have a key with
# _server_env_has_key_defined so we are sure that the value is
# either in the global or the record config
getter = getattr(serv_config, config_getter)
if section_name in serv_config and field_name in serv_config[section_name]:
getter = getattr(server_env.serv_config, config_getter)
if (
section_name in server_env.serv_config
and field_name in server_env.serv_config[section_name]
):
value = getter(section_name, field_name)
else:
value = getter(global_section_name, field_name)
Expand All @@ -228,11 +232,12 @@ def _server_env_has_key_defined(self, field_name):
global_section_name = self._server_env_global_section_name()
section_name = self._server_env_section_name()
has_global_config = (
global_section_name in serv_config
and field_name in serv_config[global_section_name]
global_section_name in server_env.serv_config
and field_name in server_env.serv_config[global_section_name]
)
has_config = (
section_name in serv_config and field_name in serv_config[section_name]
section_name in server_env.serv_config
and field_name in server_env.serv_config[section_name]
)
return has_global_config or has_config

Expand Down Expand Up @@ -424,3 +429,40 @@ def _setup_base(self):
self._server_env_transform_field_to_read_from_env(field)
self._server_env_add_is_editable_field(field)
return

def _register_hook(self):
super()._register_hook()
for model_name in self.env:
model = self.env[model_name]
if hasattr(model, "_server_env_global_section_name"):
global_section_name = model._server_env_global_section_name()
for section in server_env.serv_config:
if section.startswith(f"{global_section_name}."):
if server_env.serv_config[section].get("__autocreate", None):
name_value = section[len(global_section_name) + 1 :]
domain = [
(model._server_env_section_name_field, "=", name_value)
]
count = model.with_context(active_test=False).search_count(
domain
)
if count == 0:
_logger.debug("Automatic creation of %s", name_value)
values = literal_eval(
server_env.serv_config[section].get("__autocreate")
)
values[
model._server_env_section_name_field
] = name_value
records = model.create([values])
self.env["ir.model.data"].create(
[
{
"name": section,
"model": model_name,
"module": "__server_environment__",
"res_id": record.id,
}
for record in records
]
)
17 changes: 17 additions & 0 deletions server_environment/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,20 @@ Note: empty environment keys always take precedence over default fields

Read the documentation of the class
[models/server_env_mixin.py](models/server_env_mixin.py).

## Auto creation of records

It is possible to indicate that records must be created automatically if not found in the database.

When specifying a section in the configuration file or environment variable, also define ``__autocreate = {}``.
The value is a dictionary that will be passed when the record is created. This allows setting some non environment variables that are required.
For example, when using fs_storage module, the name of the storage is required so the configuration would look like:

```ini
[fs_storage.my_sftp]
__autocreate = {"name": "My SFTP"}
protocol=sftp
options={"host": "10.10.10.10", "username": "foo", "password": "xxxxxxxxx"}
```

When the module creates such a record, it will add an xml id in the form `__server_enironment__.<section name>`.
1 change: 1 addition & 0 deletions server_environment/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
- Thomas Binfeld \<<[email protected]>\>
- Stéphane Bidoul \<<[email protected]>\>
- Simone Orsi \<<[email protected]>\>
- Vincent Hatakeyama \<<[email protected]>\>
2 changes: 1 addition & 1 deletion server_environment/readme/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
- it is not possible to set the environment from the command line. A
configuration file must be used.
configuration file or environment variables must be used.
- the module does not allow to set low level attributes such as database
server, etc.
- server.env.techname.mixin's tech_name field could leverage the new
Expand Down
1 change: 1 addition & 0 deletions server_environment/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import test_server_environment_autocreate
from . import test_server_environment
from . import test_environment_variable
24 changes: 24 additions & 0 deletions server_environment/tests/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2024 XCG Consulting
# @author Vincent Hatakeyama
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models


class ExternalService(models.Model):
_name = "external_service"
_description = "External Service"
_inherit = "server.env.mixin"

name = fields.Char(required=True)
description = fields.Char(required=True)
host = fields.Char()
user = fields.Char()
password = fields.Char()

@property
def _server_env_fields(self):
return {
"host": {},
"user": {},
"password": {},
}
76 changes: 76 additions & 0 deletions server_environment/tests/test_server_environment_autocreate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright 2018 Camptocamp (https://www.camptocamp.com).
# Copyright 2024 XCG Consulting (https://xcg-consulting.fr).
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
import configparser

from odoo_test_helper import FakeModelLoader

from odoo.tests import tagged

from .. import server_env
from . import common


# Test need to be run post install otherwise the _register_hook is not called yet
@tagged("post_install", "-at_install")
class TestEnv(common.ServerEnvironmentCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Load fake models ->/
cls.loader = FakeModelLoader(cls.env, cls.__module__)
cls.loader.backup_registry()
from .models import ExternalService

cls.loader.update_registry((ExternalService,))
cls.env["external_service"].create([{"name": "ftp2", "description": "another"}])

# Add our sections
cls.old_serv_config = server_env.serv_config
server_env.serv_config = configparser.ConfigParser(cls.old_serv_config)
# update configuration with extra sections and values
server_env.serv_config.add_section("external_service.ftp")
server_env.serv_config.set(
"external_service.ftp", "__autocreate", "{'description': 'ftp server'}"
)
server_env.serv_config.set("external_service.ftp", "host", "sftp.example.com")
server_env.serv_config.set("external_service.ftp", "user", "foo")
server_env.serv_config.set("external_service.ftp", "password", "bar")
server_env.serv_config.add_section("external_service.ftp2")
server_env.serv_config.set(
"external_service.ftp2", "__autocreate", "{'description': 'ftp2'}"
)
server_env.serv_config.set("external_service.ftp2", "host", "sftp2.example.com")
server_env.serv_config.set("external_service.ftp2", "user", "monty")
server_env.serv_config.set("external_service.ftp2", "password", "python")
# used to force _register_hook with auto creation
cls.loader.update_registry(tuple())

@classmethod
def tearDownClass(cls):
cls.loader.restore_registry()
super().tearDownClass()
server_env.serv_config = cls.old_serv_config

def test_autocreate(self):
# create record in setupClass
# Test it has no xmlid
record = self.env.ref("__server_environment__.external_service.ftp2", False)
self.assertFalse(record)
# look for it
record = self.env["external_service"].search([("name", "=", "ftp2")])
self.assertEqual(len(record), 1)
self.assertEqual(record.name, "ftp2")
# different from __autocreate dict as it is created in setUpClass
self.assertEqual(record.description, "another")
self.assertEqual(record.host, "sftp2.example.com")
self.assertEqual(record.user, "monty")
self.assertEqual(record.password, "python")

# auto created record
record = self.env.ref("__server_environment__.external_service.ftp")
self.assertEqual(record.name, "ftp")
self.assertEqual(record.description, "ftp server")
self.assertEqual(record.host, "sftp.example.com")
self.assertEqual(record.user, "foo")
self.assertEqual(record.password, "bar")
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
odoo-test-helper

0 comments on commit 3c8a812

Please sign in to comment.