Source code for odoo_tools.services.objects

import giturlparse

from . import fields
from . import models


[docs]class ValidationError(Exception): pass
[docs]class Version(object): def __init__(self, since, until=None): self.since = since self.until = until
[docs]class KeyValue(models.Extendable, models.ParentedObject, models.BaseModel):
[docs] def extend(self, other): options = self._data.copy() options.update(other._data if other and other._data else {}) return KeyValue(options)
[docs] def to_dict(self): return self._data.copy()
[docs]class RepoConfig(models.ParentedObject, models.Extendable, models.BaseModel): #: (str): Url of the repository. The url can point #: to a repository using the ssh format or https format. url = fields.Url( "Url of repository", availability=Version(since=1), default=None, ) #: (str): the commit id to use commit = fields.String( "Commit of the object", availability=Version(since=1), default=None, ) #: (str): the branch name to use branch = fields.String( "Branch of the object", availability=Version(since=1), default=None, ) #: A private key if provided, the private key can #: be raw or it can be encrypted using Fernet encryption. private_key = fields.String( default=None, ) #: Tell if the repo require authentication. In some cases, #: you could have https repositories that require extra #: credentials. If auth is False, then the url will not #: get altered to use credentials like access token provided #: in a credentials store. auth = fields.Boolean( default=None ) @property def ref(self): """ Returns the proper ref to use. By default it will try to use the following values in that order: - commit - branch - resolved odoo version or None Returns: str | None: the default ref to use. """ service = self.get_parent(ServiceManifest) if ( service and service.resolved.odoo and service.resolved.odoo.version ): default = service.resolved.odoo.version else: default = None return self.commit or self.branch or default @property def repo_path(self): """ Converts the url of the repo in a unique path. The main reason is to provide a path that can be used as a unique identifier for the repositories. When inheriting services from an other manifests, all projects related to the same url will be inherited accordingly. When fetching repositories, it ensure that a project a/web and b/web will not be fetched into a web folder. Or to some extent, a github.com/a/web and gitlab.com/a/web are still considered as two different projects. Returns: str: the path of the repo """ url = giturlparse.parse(self.url.lower()) if url.valid: path = "_".join([ url.host.replace('.', '_'), url.owner.replace('/', '_'), url.repo.replace('/', '_') ]) else: path = self.url return path
[docs] def extend(self, other): if not other: return self return RepoConfig({ "url": other.url or self.url, "commit": other.commit or self.commit, "branch": other.branch or self.branch, "private_key": other.private_key or self.private_key, })
[docs] def to_dict(self): return { "url": self.url, "commit": self.commit, "branch": self.branch, "ref": self.ref, "private_key": self.private_key }
[docs]class OdooConfig(models.ParentedObject, models.Extendable, models.BaseModel): version = fields.String() repo = fields.Object( "Repository Configuration", object_class=RepoConfig, availability=Version(since=1) ) options = fields.Object( "Odoo Custom Options", object_class=KeyValue, availability=Version(since=1) )
[docs] def extend(self, other): if not other: return self return OdooConfig({ "version": ( other.version if other.version else self.version ), "repo": ( other.repo.extend(self.repo) if other.repo else self.repo ), "options": ( other.options.extend(self.options) if other.options else self.options ) })
[docs] def to_dict(self): return { "version": self.version, "repo": self.repo.to_dict() if self.repo else {}, "options": self.options.to_dict() if self.options else {} }
[docs]class ManifestProxy(models.ParentedObject, models.BaseModel): reference = fields.String()
[docs] @classmethod def parse(klass, data): return ManifestProxy({"reference": data})
[docs]class ServiceManifest( models.ParentedObject, models.Extendable, models.BaseModel ): """ This object represent all properties that can be set on a manifest for odoo services. """ #: (String): A string representing the name of the #: service configuration. For example, you may want to define #: a service staging and production with different settings. name = fields.String( "name", availability=Version(since="1") ) #: (ServiceManifest): A reference to an other service manifest. #: It's possible to reference manifests by name. In that case, a #: service will be able to merge its own properties with a parent #: manifest configuration. #: #: For example, you may want to have a production server using the #: production branch of certain repositories. While a staging #: environment tracking the staging branch. To make things simpler, #: you could have all addons defined in the staging environment. But #: you could also define custom branches for production and keep the #: rest as the staging environment is using. inherit = fields.ProxyObject( "Reference to parent manifest", getter="get_inherited_object", availability=Version(since="1") ) #: (OdooConfig): A field referencing an odoo configuration. odoo = fields.Object( "Odoo Configuration", object_class=OdooConfig, availability=Version(since="1") ) #: (List<RepoConfig>): A list of repository configuration. addons = fields.Dict( "List of addons", object_class=RepoConfig, key="repo_path", ) #: (KeyValue): A key value store of labels. labels = fields.Object( "Odoo Custom Options", object_class=KeyValue, availability=Version(since=1) ) #: (KeyValue): A key value store of environment variables. env = fields.Object( "Odoo Custom Options", object_class=KeyValue, availability=Version(since=1) )
[docs] def get_inherited_object(self, key): """ Returns: ServiceManifest: Related service manifest given by its name. """ parent = self.get_parent(ServiceManifests) return parent.services[key]
# TODO refactor to make auto extandable objects # Guess how they can be extended without specifying # properties.
[docs] def resolved_prop(self, prop_name): if not self.inherit: return getattr(self, prop_name) other_val = self.inherit.resolved_prop(prop_name) self_val = getattr(self, prop_name) if not other_val: return self_val if not self_val: return other_val # TODO return Extendable Collections in field with list/dict if isinstance(other_val, models.Extendable): return other_val.extend(self_val) elif isinstance(other_val, dict): ret_val = other_val.copy() for key, value in self_val.items(): if ( key in ret_val and isinstance(ret_val[key], models.Extendable) ): ret_val[key] = ret_val[key].extend(value) else: ret_val[key] = value return ret_val elif isinstance(other_val, list): return other_val + ret_val else: print("whoa")
@property def resolved(self): """ This service manifest with all of its properties resolved against the inherit property. For example, if you had a service that inherits from an other one and each of them had different addons configured. It would let you combine all configurations together. The value of this property would be a new ServiceManifest that has all addons of self and of inherit (recursively). Returns: ServiceManifest: ServiceManifest that is an extension of inherit """ return self.extend(self.inherit)
[docs] def extend(self, other): """ Creates a new ServiceManifest that extend the other one. Args: other (ServiceManifest): The other manifest to inherit from. Returns: (ServiceManifest): A new ServiceManifest """ if not other: return self return ServiceManifest({ "name": self.name, "odoo": self.resolved_prop('odoo'), "addons": self.resolved_prop('addons'), "labels": self.resolved_prop('labels'), "env": self.resolved_prop('env'), })
[docs] def to_dict(self): """ Returns a dict representing the data in this object. Returns: dict: Dict containing all properties of this object. """ inherit = ( self._data['inherit']['ref'] if 'inherit' in self._data else None ) addons = self.addons or {} vals = { "name": self.name, "addons": { key: val.to_dict() for key, val in addons.items() } } if inherit: vals['inherit'] = inherit for attr in ['odoo', 'env', 'labels']: val = getattr(self, attr) if val: vals[attr] = val.to_dict() return vals
[docs]class ServiceManifests(models.BaseModel): #: (dict): Dictionary of service manifest by their name services = fields.Dict( "List of services", object_class=ServiceManifest, key="name" )
[docs] def to_dict(self): """ Returns a dict representing the data in this object. Returns: dict: Dict containing all properties of this object. """ return { "services": { key: val.to_dict() for key, val in self.services.items() } }