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

[17.0][IMP] server_environment: add possibility of auto creating records #188

Open
wants to merge 1 commit into
base: 17.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 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 Down Expand Up @@ -424,3 +425,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 serv_config:
if section.startswith(f"{global_section_name}."):
if 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", section)
values = literal_eval(
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
]
)
Comment on lines +431 to +464
Copy link
Contributor

Choose a reason for hiding this comment

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

Why would you loop on all the models? This mixin will be inherited by a specific model and that model only should be set up...

Suggested change
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 serv_config:
if section.startswith(f"{global_section_name}."):
if 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", section)
values = literal_eval(
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
]
)
global_section_name = self._server_env_global_section_name()
for section in serv_config:
if not section.startswith(f"{global_section_name}."):
continue
if not serv_config[section].get("__autocreate", None):
continue
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 not count:
_logger.info("Automatic creation of %s", section)
values = literal_eval(
serv_config[section].get("__autocreate")
)
values[
model._server_env_section_name_field
] = name_value
# TODO: handle failure
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
]
)

Something like this would be better.

17 changes: 17 additions & 0 deletions server_environment/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,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": {},
}
3 changes: 1 addition & 2 deletions server_environment/tests/test_environment_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

from odoo.tools.config import config as odoo_config

from odoo.addons.server_environment import server_env

from .. import server_env
from .common import ServerEnvironmentCase


Expand Down
69 changes: 69 additions & 0 deletions server_environment/tests/test_server_environment_autocreate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# 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)
from unittest.mock import patch

from odoo_test_helper import FakeModelLoader

from odoo.tests import tagged
from odoo.tools.config import config as odoo_config

from .. import server_env
from ..models import server_env_mixin
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"}])

@classmethod
def tearDownClass(cls):
cls.loader.restore_registry()
super().tearDownClass()

@patch.dict(odoo_config.options, {"running_env": "autocreate"})
def test_autocreate(self):
original_serv_config = server_env_mixin.serv_config
try:
with self.set_config_dir("testfiles"):
parser = server_env._load_config()
server_env_mixin.serv_config = parser
# Needed to force _register_hook with auto creation
self.loader.update_registry(tuple())

# 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")

# 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")
finally:
server_env_mixin.serv_config = original_serv_config
11 changes: 11 additions & 0 deletions server_environment/tests/testfiles/autocreate/base.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[external_service.ftp]
__autocreate={"description": "ftp server"}
; host=sftp.example.com
; user=foo
; password=bar

[external_service.ftp2]
__autocreate={"description": "ftp2"}
host=sftp2.example.com
user=monty
password=python
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
Loading