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

[WIP] 14.0 shopinvader cache invalidation #1384

Open
wants to merge 7 commits into
base: 14.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
6 changes: 6 additions & 0 deletions setup/shopinvader_cache_invalidation/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
1 change: 1 addition & 0 deletions shopinvader_cache_invalidation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
28 changes: 28 additions & 0 deletions shopinvader_cache_invalidation/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Matthieu SAISON <[email protected]>
# License AGPL-3.0 or later (https: //www.gnu.org/licenses/agpl).

{
"name": "Custom Shopinvader Cache Purge",
"summary": "List url to purge and manage purge",
"version": "14.0.1.0.0",
"category": "Uncategorized",
"website": "https://github.com/shopinvader/odoo-shopinvader",
"author": " Akretion",
"license": "AGPL-3",
"application": False,
"installable": True,
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": ["shopinvader_search_engine"],
"data": [
"data/ir_cron.xml",
"security/ir.model.access.csv",
"views/shopinvader_url_purge.xml",
"views/shopinvader_backend.xml",
"views/shopinvader_url_purge_type_view.xml",
],
"demo": [],
}
17 changes: 17 additions & 0 deletions shopinvader_cache_invalidation/data/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record forcecreate="True" id="ir_cron_cache_purge" model="ir.cron">
<field name="name">Purge search engine cache from listed urls.</field>
<field name="active" eval="True" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="model_id" ref="model_shopinvader_url_purge" />
<field name="state">code</field>
<field
name="code"
>model.search([("state", "=", "to_purge")]).purge_url()</field>
</record>
</odoo>
3 changes: 3 additions & 0 deletions shopinvader_cache_invalidation/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import se_binding
from . import shopinvader_url_purge
from . import shopinvader_backend
33 changes: 33 additions & 0 deletions shopinvader_cache_invalidation/models/se_binding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Matthieu SAISON <[email protected]>
# License AGPL-3.0 or later (https: //www.gnu.org/licenses/agpl).
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Matthieu SAISON <[email protected]>
# License AGPL-3.0 or later (https: //www.gnu.org/licenses/agpl).

import logging

from odoo import models

_logger = logging.getLogger(__name__)


class SeBinding(models.AbstractModel):
_inherit = "se.binding"

def synchronize(self):
res = super().synchronize()
for backend in self.mapped("backend_id"):
url_keys = {
binding.data.get("url_key")
for binding in self
if binding.backend_id == backend and binding.data.get("url_key")
}
for url_key in url_keys:
self.env["shopinvader.url.purge"]._request_purge(
url_key,
self._name,
self._description,
backend.id,
)
return res
27 changes: 27 additions & 0 deletions shopinvader_cache_invalidation/models/shopinvader_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Matthieu SAISON <[email protected]>
# License AGPL-3.0 or later (https: //www.gnu.org/licenses/agpl).

from odoo import api, fields, models


class ShopinvaderBackend(models.Model):
_inherit = "shopinvader.backend"

purge_ids = fields.One2many(
"shopinvader.url.purge", "backend_id", string="Urls to purge"
)
purge_nbr = fields.Integer(compute="_compute_purge_nbr", store=True, readonly=True)

cache_refresh_secret = fields.Char()

@property
def _server_env_fields(self):
env_fields = super()._server_env_fields
env_fields.update({"cache_refresh_secret": {}})
return env_fields

@api.depends("purge_ids")
def _compute_purge_nbr(self):
for record in self:
record.purge_nbr = len(record.purge_ids)
98 changes: 98 additions & 0 deletions shopinvader_cache_invalidation/models/shopinvader_url_purge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Matthieu SAISON <[email protected]>
# License AGPL-3.0 or later (https: //www.gnu.org/licenses/agpl).
import logging

import requests
from urllib.parse import urljoin

from odoo import fields, models

_logger = logging.getLogger(__name__)


class ShopinvaderUrlPurge(models.Model):
_name = "shopinvader.url.purge"
_description = "List of url to purge from cache"

url = fields.Char(required=True)
purge_type_id = fields.Many2one(
"shopinvader.url.purge.type", string="Purge Type", required=True
)
backend_id = fields.Many2one("shopinvader.backend", string="Backend", required=True)
date_last_purge = fields.Datetime("Last Purge Date", readonly=True)
manual = fields.Boolean(default=True, readonly=True, required=True)
state = fields.Selection(
[
("to_purge", "To purge"),
("done", "Done"),
("failed", "Failed"),
],
readonly=True,
)
error = fields.Char(readonly=True)

def _request_purge(self, url_key, model_name, model_description, backend_id):
record = self.search(
[
("url", "=", url_key),
("backend_id", "=", backend_id),
]
)
if record:
record.state = "to_purge"
else:
purge_type = self.env["shopinvader.url.purge.type"].get_or_create(
model_name, model_description
)
record = self.create(
{
"url": url_key,
"state": "to_purge",
"purge_type_id": purge_type.id,
"backend_id": backend_id,
"manual": False,
}
)

# use by cron
def purge_url(self):
for backend in self.backend_id:
s = requests.Session()
s.headers.update({"X-force-cache-refresh": backend.cache_refresh_secret})
for record in self:
if record.backend_id == backend:
url = urljoin(record.backend_id.location, record.url)
try:
response = s.get(url)
if response.status_code != 200:
raise Exception(
"Response Error code %s %s",
response.status_code,
response.text,
)
self.write(
{
"date_last_purge": fields.Datetime.now(),
"state": "done",
"error": None,
}
)
except Exception as e:
_logger.error("Fail to purge %s", e)
self.error = str(e)
self.state = "failed"
continue


class ShopinvaderUrlPurgeType(models.Model):
_name = "shopinvader.url.purge.type"

code = fields.Char(readonly=True)
name = fields.Char(required=True)

def get_or_create(self, model_name, model_description):
res = self.search([("code", "=", model_name)])
if not res:
res = self.create({"code": model_name, "name": model_description})
return res
1 change: 1 addition & 0 deletions shopinvader_cache_invalidation/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Matthieu SAISON <[email protected]>
1 change: 1 addition & 0 deletions shopinvader_cache_invalidation/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Schedules tasks to purge urls's cache on shopinvader website
3 changes: 3 additions & 0 deletions shopinvader_cache_invalidation/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_custom_shopinvader_cache_purge,full access cache_purge,model_shopinvader_url_purge,base.group_user,1,1,1,1
access_custom_shopinvader_cache_purge_type,full access cache_purge_type,model_shopinvader_url_purge_type,base.group_user,1,1,1,1
26 changes: 26 additions & 0 deletions shopinvader_cache_invalidation/views/shopinvader_backend.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2013 Akretion (http://www.akretion.com)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>

<record model="ir.ui.view" id="shopinvader_backend_form_view">
<field name="model">shopinvader.backend</field>
<field name="inherit_id" ref="shopinvader.shopinvader_backend_view_form" />
<field name="arch" type="xml">
<div class="oe_button_box" position="inside">
<button
type="action"
class="oe_stat_button"
name="%(action_view_url_purge)d"
icon="fa-th-list"
>
<field name="purge_nbr" string="purge lines" widget="statinfo" />
</button>
</div>
<field name="se_backend_id" position="after">
<field name="cache_refresh_secret" />
</field>
</field>
</record>

</odoo>
129 changes: 129 additions & 0 deletions shopinvader_cache_invalidation/views/shopinvader_url_purge.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="action_view_url_purge" model="ir.actions.act_window">
<field name="type">ir.actions.act_window</field>
<field name="name">Liste des URL</field>
<field name="res_model">shopinvader.url.purge</field>
<field name="view_mode">tree</field>
<field name="domain">[('backend_id', '=', active_id)]</field>
</record>

<record model="ir.ui.view" id="shopinvader_url_purge_search_view">
<field name="model">shopinvader.url.purge</field>
<field name="arch" type="xml">
<search>
<field name="url" />
<field name="purge_type_id" />
<field name="backend_id" />
<field name="state" />
<filter
name="manual"
string="Manual"
domain="[('manual', '=', True)]"
/>
<filter name="auto" string="Auto" domain="[('manual', '=', False)]" />
<group expand="0" string="Group By">
<filter
string="Backend"
name="groupby_backend"
domain="[]"
context="{'group_by': 'backend_id'}"
/>
<filter
string="Type"
name="groupby_type"
domain="[]"
context="{'group_by': 'purge_type_id'}"
/>
<filter
string="State"
name="groupby_state"
domain="[]"
context="{'group_by': 'state'}"
/>
</group>
</search>
</field>
</record>

<record model="ir.ui.view" id="shopinvader_url_purge_tree_view">
<field name="model">shopinvader.url.purge</field>
<field name="arch" type="xml">
<tree>
<field name="url" />
<field name="purge_type_id" />
<field name="backend_id" />
<field name="state" />
<field name="date_last_purge" />
<field name="manual" />
</tree>
</field>
</record>

<record model="ir.ui.view" id="shopinvader_url_purge_form_view">
<field name="model">shopinvader.url.purge</field>
<field name="arch" type="xml">
<form>
<header>
<button name="purge_url" type="object" string="Purge" />
</header>
<group>
<field name="url" attrs="{'readonly': [('manual', '=', False)]}" />
<field
name="purge_type_id"
attrs="{'readonly': [('manual', '=', False)]}"
/>
<field
name="backend_id"
attrs="{'readonly': [('manual', '=', False)]}"
/>
<field name="state" />
<field name="date_last_purge" />
<field name="manual" />
<field
name="error"
attrs="{'invisible': [('error', '=', False)]}"
/>
</group>
</form>
</field>
</record>


<record model="ir.actions.act_window" id="shopinvader_url_purge_act_window">
<field name="res_model">shopinvader.url.purge</field>
<field name="view_mode">tree,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
</record>

<record model="ir.actions.server" id="action_purge">
<field name="name">Set to purge</field>
<field name="model_id" ref="model_shopinvader_url_purge" />
<field name="binding_type">action</field>
<field name="state">code</field>
<field name="code">
records.write({"state": "to_purge"})
</field>
<field name="groups_id" eval="[(4, ref('base.group_user'))]" />
</record>

<menuitem
id="shopinvader_url_purge_root_menu"
parent="shopinvader.menu_shopinvader_root"
name="Url Purge List"
sequence="90"
action="shopinvader_url_purge_act_window"
/>

<record model="ir.actions.server" id="action_purge_url">
<field name="name">Purge Url</field>
<field name="model_id" ref="model_shopinvader_url_purge" />
<field name="binding_model_id" ref="model_shopinvader_url_purge" />
<field name="state">code</field>
<field name="code">
records.purge_url()
</field>
</record>

</odoo>
Loading
Loading