29 from Pyro.errors import PyroError, ConnectionClosedError |
29 from Pyro.errors import PyroError, ConnectionClosedError |
30 |
30 |
31 from logilab.common.configuration import REQUIRED |
31 from logilab.common.configuration import REQUIRED |
32 from logilab.common.optik_ext import check_yn |
32 from logilab.common.optik_ext import check_yn |
33 |
33 |
|
34 from yams.schema import role_name |
|
35 |
34 from rql.nodes import Constant |
36 from rql.nodes import Constant |
35 from rql.utils import rqlvar_maker |
37 from rql.utils import rqlvar_maker |
36 |
38 |
37 from cubicweb import dbapi, server |
39 from cubicweb import dbapi, server |
38 from cubicweb import BadConnectionId, UnknownEid, ConnectionError |
40 from cubicweb import ValidationError, BadConnectionId, UnknownEid, ConnectionError |
|
41 from cubicweb.schema import VIRTUAL_RTYPES |
39 from cubicweb.cwconfig import register_persistent_options |
42 from cubicweb.cwconfig import register_persistent_options |
40 from cubicweb.server.sources import (AbstractSource, ConnectionWrapper, |
43 from cubicweb.server.sources import (AbstractSource, ConnectionWrapper, |
41 TimedCache, dbg_st_search, dbg_results) |
44 TimedCache, dbg_st_search, dbg_results) |
42 from cubicweb.server.msplanner import neged_relation |
45 from cubicweb.server.msplanner import neged_relation |
43 |
46 |
174 self.support_entities = {} |
177 self.support_entities = {} |
175 self.support_relations = {} |
178 self.support_relations = {} |
176 self.dont_cross_relations = set(('owned_by', 'created_by')) |
179 self.dont_cross_relations = set(('owned_by', 'created_by')) |
177 self.cross_relations = set() |
180 self.cross_relations = set() |
178 assert self.eid is not None |
181 assert self.eid is not None |
|
182 self._schemacfg_idx = {} |
179 if session is None: |
183 if session is None: |
180 _session = self.repo.internal_session() |
184 _session = self.repo.internal_session() |
181 else: |
185 else: |
182 _session = session |
186 _session = session |
183 try: |
187 try: |
184 for rql, struct in [('Any ETN WHERE S cw_support ET, ET name ETN, ET is CWEType, S eid %(s)s', |
188 for schemacfg in _session.execute( |
185 self.support_entities), |
189 'Any CFG,CFGO,SN,S WHERE ' |
186 ('Any RTN WHERE S cw_support RT, RT name RTN, RT is CWRType, S eid %(s)s', |
190 'CFG options CFGO, CFG cw_schema S, S name SN, ' |
187 self.support_relations)]: |
191 'CFG cw_for_source X, X eid %(x)s', {'x': self.eid}).entities(): |
188 for ertype, in _session.execute(rql, {'s': self.eid}): |
192 self.add_schema_config(schemacfg) |
189 struct[ertype] = True # XXX write support |
|
190 for rql, struct in [('Any RTN WHERE S cw_may_cross RT, RT name RTN, S eid %(s)s', |
|
191 self.cross_relations), |
|
192 ('Any RTN WHERE S cw_dont_cross RT, RT name RTN, S eid %(s)s', |
|
193 self.dont_cross_relations)]: |
|
194 for rtype, in _session.execute(rql, {'s': self.eid}): |
|
195 struct.add(rtype) |
|
196 finally: |
193 finally: |
197 if session is None: |
194 if session is None: |
198 _session.close() |
195 _session.close() |
199 # XXX move in hooks or schema constraints |
196 |
200 for rtype in ('is', 'is_instance_of', 'cw_source'): |
197 etype_options = set(('write',)) |
201 assert rtype not in self.dont_cross_relations, \ |
198 rtype_options = set(('maycross', 'dontcross', 'write',)) |
202 '%s relation should not be in dont_cross_relations' % rtype |
199 |
203 assert rtype not in self.support_relations, \ |
200 def _check_options(self, schemacfg, allowedoptions): |
204 '%s relation should not be in support_relations' % rtype |
201 if schemacfg.options: |
|
202 options = set(w.strip() for w in schemacfg.options.split(':')) |
|
203 else: |
|
204 options = set() |
|
205 if options - allowedoptions: |
|
206 options = ', '.join(sorted(options - allowedoptions)) |
|
207 msg = _('unknown option(s): %s' % options) |
|
208 raise ValidationError(schemacfg.eid, {role_name('options', 'subject'): msg}) |
|
209 return options |
|
210 |
|
211 def add_schema_config(self, schemacfg, checkonly=False): |
|
212 """added CWSourceSchemaConfig, modify mapping accordingly""" |
|
213 try: |
|
214 ertype = schemacfg.schema.name |
|
215 except AttributeError: |
|
216 msg = schemacfg._cw._("attribute/relation can't be mapped, only " |
|
217 "entity and relation types") |
|
218 raise ValidationError(schemacfg.eid, {role_name('cw_for_schema', 'subject'): msg}) |
|
219 if schemacfg.schema.__regid__ == 'CWEType': |
|
220 options = self._check_options(schemacfg, self.etype_options) |
|
221 if not checkonly: |
|
222 self.support_entities[ertype] = 'write' in options |
|
223 else: # CWRType |
|
224 if ertype in ('is', 'is_instance_of', 'cw_source') or ertype in VIRTUAL_RTYPES: |
|
225 msg = schemacfg._cw._('%s relation should not be in mapped') % rtype |
|
226 raise ValidationError(schemacfg.eid, {role_name('cw_for_schema', 'subject'): msg}) |
|
227 options = self._check_options(schemacfg, self.rtype_options) |
|
228 if 'dontcross' in options: |
|
229 if 'maycross' in options: |
|
230 msg = schemacfg._("can't mix dontcross and maycross options") |
|
231 raise ValidationError(schemacfg.eid, {role_name('options', 'subject'): msg}) |
|
232 if 'write' in options: |
|
233 msg = schemacfg._("can't mix dontcross and write options") |
|
234 raise ValidationError(schemacfg.eid, {role_name('options', 'subject'): msg}) |
|
235 if not checkonly: |
|
236 self.dont_cross_relations.add(ertype) |
|
237 elif not checkonly: |
|
238 self.support_relations[ertype] = 'write' in options |
|
239 if 'maycross' in options: |
|
240 self.cross_relations.add(ertype) |
|
241 if not checkonly: |
|
242 # add to an index to ease deletion handling |
|
243 self._schemacfg_idx[schemacfg.eid] = ertype |
|
244 |
|
245 def del_schema_config(self, schemacfg, checkonly=False): |
|
246 """deleted CWSourceSchemaConfig, modify mapping accordingly""" |
|
247 if checkonly: |
|
248 return |
|
249 try: |
|
250 ertype = self._schemacfg_idx[schemacfg.eid] |
|
251 if ertype[0].isupper(): |
|
252 del self.support_entities[ertype] |
|
253 else: |
|
254 if ertype in self.support_relations: |
|
255 del self.support_relations[ertype] |
|
256 if ertype in self.cross_relations: |
|
257 self.cross_relations.remove(ertype) |
|
258 else: |
|
259 self.dont_cross_relations.remove(ertype) |
|
260 except: |
|
261 self.error('while updating mapping consequently to removal of %s', |
|
262 schemacfg) |
205 |
263 |
206 def local_eid(self, cnx, extid, session): |
264 def local_eid(self, cnx, extid, session): |
207 etype, dexturi, dextid = cnx.describe(extid) |
265 etype, dexturi, dextid = cnx.describe(extid) |
208 if dexturi == 'system' or not ( |
266 if dexturi == 'system' or not ( |
209 dexturi in self.repo.sources_by_uri or self._skip_externals): |
267 dexturi in self.repo.sources_by_uri or self._skip_externals): |