Skip to content
Merged
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
51 changes: 44 additions & 7 deletions docker/scripts/odoo_website_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,42 @@ def _print_bootstrap_readback(
print(f"website_bootstrap_logo_present={_marker_bool(not logo_expected or logo_present)}")


def _select_website(website_model: Any, *, canonical_url: str) -> Any:
def _select_website(
website_model: Any,
*,
canonical_url: str,
primary_page: Any | None = None,
default_website: Any | None = None,
) -> Any:
if primary_page and "website_id" in primary_page._fields:
page_website = _field_value(primary_page, "website_id")
if page_website:
return page_website.sudo()
canonical_host = _canonical_host(canonical_url)
if canonical_host and "domain" in website_model._fields:
website = website_model.search([("domain", "in", (canonical_host, canonical_url))], order="id", limit=1)
if website:
return website
if default_website:
return default_website.sudo()
return website_model.search([], order="id", limit=1)


def _clear_duplicate_canonical_domains(website_model: Any, *, website: Any, canonical_url: str) -> None:
canonical_host = _canonical_host(canonical_url)
if not canonical_host or "domain" not in website_model._fields:
return
duplicates = website_model.search(
[
("id", "!=", website.id),
("domain", "in", (canonical_host, canonical_url)),
],
order="id",
)
if duplicates:
duplicates.sudo().write({"domain": ""})


def _homepage_values(website: Any, *, homepage_url: str, homepage_page: Any | None) -> dict[str, object]:
values: dict[str, object] = {}
if homepage_page and "homepage_id" in website._fields:
Expand Down Expand Up @@ -307,8 +334,21 @@ def apply_website_bootstrap(env: Any, parsed_payload: dict[str, object] | None)

canonical_url = str(website_payload.get("canonical_url") or _payload_web_base_url(parsed_payload) or "").strip()

homepage_url = str(website_payload.get("homepage_url") or "").strip()
primary_page_xmlid = str(website_payload.get("primary_page_xmlid") or "").strip()
primary_page = _find_website_page_by_xmlid(env, xmlid=primary_page_xmlid)
primary_page_xmlid_found = bool(primary_page)
if primary_page_xmlid and not primary_page:
raise RuntimeError(f"Website bootstrap primary page XML ID not found: {primary_page_xmlid}")
default_website = env.ref("website.default_website", raise_if_not_found=False)

website_model = env["website"].sudo()
website = _select_website(website_model, canonical_url=canonical_url)
website = _select_website(
website_model,
canonical_url=canonical_url,
primary_page=primary_page,
default_website=default_website,
)
if not website:
default_name = str(website_payload.get("name") or "Website").strip() or "Website"
create_values = _field_values(website_model, {"name": default_name})
Expand All @@ -323,6 +363,7 @@ def apply_website_bootstrap(env: Any, parsed_payload: dict[str, object] | None)
_require_existing_fields(website, ("domain",), label="canonical domain")
_set_config_parameter(env, "web.base.url", canonical_url)
_set_config_parameter(env, "web.base.url.freeze", "True")
_clear_duplicate_canonical_domains(website_model, website=website, canonical_url=canonical_url)
website_values["domain"] = _canonical_host(canonical_url)
default_lang = str(website_payload.get("default_lang") or "").strip()
if default_lang and "default_lang_id" in website._fields:
Expand All @@ -344,16 +385,12 @@ def apply_website_bootstrap(env: Any, parsed_payload: dict[str, object] | None)
if logo_expected:
_assert_binary_field_value(website, "logo", logo_value, label="website logo")

homepage_url = str(website_payload.get("homepage_url") or "").strip()
primary_page_xmlid = str(website_payload.get("primary_page_xmlid") or "").strip()
homepage_page, primary_page_xmlid_found = _find_website_page(env, website, xmlid=primary_page_xmlid, url=homepage_url)
homepage_page = primary_page or _find_website_page_by_url(env, website, url=homepage_url)
if homepage_page:
page_values: dict[str, object] = {"is_published": True, "website_published": True}
if "website_id" in homepage_page._fields:
page_values["website_id"] = website.id
_write_existing_fields(homepage_page, page_values)
elif primary_page_xmlid:
raise RuntimeError(f"Website bootstrap primary page XML ID not found: {primary_page_xmlid}")
_write_existing_fields(website, _homepage_values(website, homepage_url=homepage_url, homepage_page=homepage_page))
final_homepage_url = homepage_url
final_homepage_page = homepage_page
Expand Down
68 changes: 67 additions & 1 deletion tests/test_odoo_website_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ def __init__(
self.record = record if record is not None else FakeRecord(truthy=False)
self._fields = set(fields)
self.records = records
self.searches: list[tuple[tuple[object, ...], dict[str, object]]] = []

def sudo(self) -> FakeModel:
return self

def search(self, *unused_args: object, **unused_kwargs: object) -> FakeRecord:
self.searches.append((unused_args, unused_kwargs))
if self.records is not None:
return self.records[0] if self.records else FakeRecord(truthy=False)
return self.record
Expand Down Expand Up @@ -106,16 +108,19 @@ def get_param(self, key: str) -> str | None:
class FakeEnv:
def __init__(self) -> None:
self.website = FakeRecord(fields=("name", "domain", "homepage_id", "homepage_url", "logo"))
self.default_website = self.website
self.config_parameter = FakeConfigParameter()
self.modules = FakeModel(record=FakeRecord(fields=(), truthy=True))
self.pages = FakeModel(record=FakeRecord(fields=(), truthy=False))
self.langs = FakeModel(record=FakeRecord(fields=(), truthy=False))
self.refs: dict[str, FakeRecord] = {}
self.registry = {"website": object()}
self.website_model: FakeModel | None = None

def __getitem__(self, model_name: str) -> Any:
return {
"website": FakeModel(record=self.website, fields=("name", "domain", "homepage_id", "homepage_url", "logo")),
"website": self.website_model
or FakeModel(record=self.website, fields=("name", "domain", "homepage_id", "homepage_url", "logo")),
"ir.config_parameter": self.config_parameter,
"ir.module.module": self.modules,
"website.page": self.pages,
Expand All @@ -124,6 +129,8 @@ def __getitem__(self, model_name: str) -> Any:
}[model_name]

def ref(self, xmlid: str, *unused_args: object, **unused_kwargs: object) -> FakeRecord | None:
if xmlid == "website.default_website":
return self.default_website
return self.refs.get(xmlid)


Expand Down Expand Up @@ -206,6 +213,61 @@ def test_page_backed_homepage_requires_primary_page_and_persists_it(self) -> Non
self.assertEqual(env.website.homepage_url, "/cell-mechanic")
self.assertIn({"is_published": True, "website_published": True, "website_id": 1}, page.writes)

def test_primary_page_website_wins_over_existing_canonical_domain_match(self) -> None:
env = FakeEnv()
page_bound_website = FakeRecord(
record_id=1,
fields=("name", "domain", "homepage_id", "homepage_url", "logo"),
values={"name": "My Website", "domain": ""},
)
canonical_website = FakeRecord(
record_id=2,
fields=("name", "domain", "homepage_id", "homepage_url", "logo"),
values={"name": "Old Target", "domain": "cm-website-testing.example.com"},
)
env.website = page_bound_website
env.default_website = page_bound_website
env.website_model = FakeModel(
record=page_bound_website,
fields=("name", "domain", "homepage_id", "homepage_url", "logo"),
records=[canonical_website],
)
page = FakeRecord(
record_id=42,
fields=("is_published", "website_published", "website_id"),
values={"model_name": "website.page", "website_id": page_bound_website},
)
env.refs["cm_website.website_page_cell_mechanic"] = page
payload = {
"website_bootstrap": {
"name": "Cell Mechanic",
"canonical_url": "https://cm-website-testing.example.com",
"homepage_url": "/cell-mechanic",
"primary_page_xmlid": "cm_website.website_page_cell_mechanic",
}
}

website_bootstrap.apply_website_bootstrap(env, payload)

self.assertEqual(page_bound_website.name, "Cell Mechanic")
self.assertEqual(page_bound_website.domain, "cm-website-testing.example.com")
self.assertEqual(page_bound_website.homepage_id, page.id)
self.assertEqual(canonical_website.name, "Old Target")
self.assertEqual(canonical_website.domain, "")
self.assertIn({"is_published": True, "website_published": True, "website_id": 1}, page.writes)
self.assertIn(
(
(
[
("id", "!=", page_bound_website.id),
("domain", "in", ("cm-website-testing.example.com", "https://cm-website-testing.example.com")),
],
),
{"order": "id"},
),
env.website_model.searches,
)

def test_missing_primary_page_fails_before_delegating_to_installed_module(self) -> None:
env = FakeEnv()
payload = {
Expand All @@ -225,6 +287,8 @@ def test_missing_primary_page_fails_before_delegating_to_installed_module(self)

with self.assertRaisesRegex(RuntimeError, "primary page XML ID not found"):
website_bootstrap.apply_website_bootstrap(env, payload)
self.assertEqual(env.config_parameter.values, {})
self.assertEqual(env.website.writes, [])

def test_bad_primary_page_xmlid_fails_even_when_url_fallback_page_exists(self) -> None:
env = FakeEnv()
Expand All @@ -247,6 +311,8 @@ def test_bad_primary_page_xmlid_fails_even_when_url_fallback_page_exists(self) -

with self.assertRaisesRegex(RuntimeError, "primary page XML ID not found"):
website_bootstrap.apply_website_bootstrap(env, payload)
self.assertEqual(env.config_parameter.values, {})
self.assertEqual(env.website.writes, [])

def test_route_homepage_readback_reports_final_route_homepage(self) -> None:
env = FakeEnv()
Expand Down