Projet

Général

Profil

0005-misc-use-tokens-to-store-autocompletion-context-6066.patch

Frédéric Péters, 18 janvier 2022 17:51

Télécharger (11,2 ko)

Voir les différences:

Subject: [PATCH 5/5] misc: use tokens to store autocompletion context (#60665)

 wcs/api.py           | 36 ++++++++++++------
 wcs/data_sources.py  | 91 +++++++++++++++++++++++---------------------
 wcs/qommon/tokens.py | 13 ++++++-
 wcs/sessions.py      | 25 ------------
 4 files changed, 83 insertions(+), 82 deletions(-)
wcs/api.py
1147 1147

  
1148 1148
class AutocompleteDirectory(Directory):
1149 1149
    def _q_lookup(self, component):
1150
        info = get_session().get_data_source_query_info_from_token(component)
1151
        if not info:
1150
        try:
1151
            token = get_publisher().token_class.get(component)
1152
            if token.type != 'autocomplete':
1153
                raise KeyError()
1154
            if token.context['session_id'] != get_session().id:
1155
                raise KeyError()
1156
            if token.context.get('url') == '':
1157
                # this is a datasource without a json url
1158
                # (typically a Python source)
1159
                raise KeyError()
1160
        except KeyError:
1152 1161
            raise AccessForbiddenError()
1153 1162
        get_response().set_content_type('application/json')
1154 1163

  
1155
        if isinstance(info, str) and not info.startswith('carddef:'):
1156
            # legacy json source
1157
            info = {'url': info}
1158
        elif isinstance(info, str):
1159
            # legacy carddef source
1160
            info = {'carddef_ref': info}
1164
        info = token.context
1161 1165

  
1162 1166
        if 'url' in info:
1163 1167
            url = info['url']
......
1180 1184

  
1181 1185
        # carddef_ref in info
1182 1186
        carddef_ref = info['carddef_ref']
1187
        custom_view = None
1188
        if 'dynamic_custom_view' in info:
1189
            custom_view = get_publisher().custom_view_class.get(info['dynamic_custom_view'])
1190
            custom_view.filters = info['dynamic_custom_view_filters']
1183 1191
        values = CardDef.get_data_source_items(
1184 1192
            carddef_ref,
1185
            custom_view=info.get('dynamic_custom_view'),
1193
            custom_view=custom_view,
1186 1194
            query=get_request().form.get('q', ''),
1187 1195
            limit=get_request().form.get('page_limit'),
1188 1196
        )
......
1195 1203
        try:
1196 1204
            data_source = get_data_source_object({'type': component}, ignore_errors=False)
1197 1205
        except KeyError:
1198
            info = get_session().get_data_source_query_info_from_token(component)
1199
            if not info:
1206
            try:
1207
                token = get_publisher().token_class.get(component)
1208
                if token.type != 'autocomplete':
1209
                    raise KeyError()
1210
                if token.context['session_id'] != get_session().id:
1211
                    raise KeyError()
1212
            except KeyError:
1200 1213
                raise TraversalError()
1214
            info = token.context
1201 1215
            try:
1202 1216
                data_source = get_data_source_object({'type': info['slug']}, ignore_errors=False)
1203 1217
            except KeyError:
wcs/data_sources.py
599 599
        if self.type == 'jsonp':
600 600
            return self.data_source.get('value')
601 601

  
602
        token_context = {}
602 603
        if self.type == 'json' and self.query_parameter:
603 604
            json_url = self.get_json_query_url()
604
            info = None
605
            if json_url:
606
                info = {'url': json_url, 'data_source': self.id}
607
            return '/api/autocomplete/%s' % (get_session().get_data_source_query_info_token(info))
605
            token_context = {'url': json_url, 'data_source': self.id}
608 606

  
609
        if self.type and self.type.startswith('carddef:'):
610
            api_url = '/api/autocomplete/%s' % (
611
                get_session().get_data_source_query_info_token(
612
                    {
613
                        'carddef_ref': self.type,
614
                    }
615
                )
616
            )
617
            parts = self.type.split(':')
618
            if len(parts) <= 2:
619
                return api_url
620

  
621
            # custom view, check if it's dynamic
622
            from wcs.carddef import CardDef
623
            from wcs.workflows import WorkflowStatusItem
607
        elif self.type and self.type.startswith('carddef:'):
608
            token_context = {'carddef_ref': self.type}
624 609

  
625
            custom_view = CardDef.get_data_source_custom_view(self.type)
626
            if custom_view is None:
627
                get_publisher().record_error(
628
                    _('Unknown custom view "%s" for CardDef "%s"') % (parts[2], parts[1]),
629
                    context='[DATASOURCE]',
630
                    notify=True,
631
                    record=True,
632
                )
633
                return api_url
634

  
635
            had_template = False
636
            for filter_key, filter_value in custom_view.filters.items():
637
                if not Template.is_template_string(filter_value):
638
                    continue
639
                custom_view.filters[filter_key] = WorkflowStatusItem.compute(filter_value)
640
                had_template = True
641
            if had_template:
642
                # keep altered custom view in session
643
                api_url = '/api/autocomplete/%s' % (
644
                    get_session().get_data_source_query_info_token(
645
                        {'carddef_ref': self.type, 'dynamic_custom_view': custom_view}
610
            parts = self.type.split(':')
611
            if len(parts) > 2:
612
                # custom view, check if it's dynamic
613
                from wcs.carddef import CardDef
614
                from wcs.workflows import WorkflowStatusItem
615

  
616
                custom_view = CardDef.get_data_source_custom_view(self.type)
617
                if custom_view is None:
618
                    get_publisher().record_error(
619
                        _('Unknown custom view "%s" for CardDef "%s"') % (parts[2], parts[1]),
620
                        context='[DATASOURCE]',
621
                        notify=True,
622
                        record=True,
646 623
                    )
647
                )
648
            return api_url
624
                else:
625
                    had_template = False
626
                    for filter_key, filter_value in custom_view.filters.items():
627
                        if not Template.is_template_string(filter_value):
628
                            continue
629
                        custom_view.filters[filter_key] = WorkflowStatusItem.compute(filter_value)
630
                        had_template = True
631
                    if had_template:
632
                        # keep altered custom view in token
633
                        token_context = {
634
                            'carddef_ref': self.type,
635
                            'dynamic_custom_view': custom_view.id,
636
                            'dynamic_custom_view_filters': custom_view.filters,
637
                        }
638

  
639
        if token_context:
640
            token_context['session_id'] = get_session().id
641
            token, dummy = get_publisher().token_class.get_or_create(
642
                type='autocomplete', context=token_context
643
            )
644
            token.set_expiration_delay(3600)
645
            token.store()
646
            return '/api/autocomplete/%s' % token.id
649 647

  
650 648
        return None
651 649

  
......
656 654
            context = get_publisher().substitutions.get_context_variables(mode='lazy')
657 655
            new_url = get_variadic_url(url, context)
658 656
            if new_url != url:
659
                info = {'url': new_url, 'slug': self.slug}
660
                return '/api/geojson/%s' % get_session().get_data_source_query_info_token(info)
657
                token_context = {'session_id': get_session().id, 'url': new_url, 'slug': self.slug}
658
                token, dummy = get_publisher().token_class.get_or_create(
659
                    type='autocomplete', context=token_context
660
                )
661
                token.set_expiration_delay(3600)
662
                token.store()
663
                return '/api/geojson/%s' % token.id
661 664
        return '/api/geojson/%s' % self.slug
662 665

  
663 666
    def get_geojson_data(self, force_url=None):
wcs/qommon/tokens.py
19 19
import string
20 20

  
21 21
from django.utils.timezone import make_aware, now
22

  
23 22
from quixote import get_publisher
24 23

  
25
from .storage import Less, StorableObject
24
from .storage import Equal, Less, StorableObject
26 25

  
27 26

  
28 27
class Token(StorableObject):
......
49 48
            if not cls.has_key(id):
50 49
                return id
51 50

  
51
    @classmethod
52
    def get_or_create(cls, type, context):
53
        try:
54
            return (cls.select(clause=[Equal('type', type), Equal('context', context)])[0], False)
55
        except IndexError:
56
            token = cls()
57
            token.type = type
58
            token.context = context
59
            return (token, True)
60

  
52 61
    def migrate(self):
53 62
        if isinstance(self.expiration, (float, int)):
54 63
            self.expiration = make_aware(datetime.datetime.fromtimestamp(self.expiration))
wcs/sessions.py
16 16

  
17 17
import os
18 18
import time
19
import uuid
20 19

  
21 20
from .qommon import sessions
22 21
from .qommon.sessions import Session
......
27 26
    magictokens = None
28 27
    anonymous_formdata_keys = None
29 28
    visiting_objects = None
30
    data_source_query_url_tokens = None
31 29

  
32 30
    def has_info(self):
33 31
        return (
34 32
            self.anonymous_formdata_keys
35 33
            or self.magictokens
36 34
            or self.visiting_objects
37
            or self.data_source_query_url_tokens
38 35
            or Session.has_info(self)
39 36
        )
40 37

  
......
149 146
                del session.visiting_objects[object_key]
150 147
                session.store()
151 148

  
152
    def get_data_source_query_info_token(self, info):
153
        # info can be an URL in case of remote JSON source or a cards
154
        # reference, like carddef:foobar. It is stored in a dictionary
155
        # with _url_ in its name for historical reasons.
156
        if not self.data_source_query_url_tokens:
157
            self.data_source_query_url_tokens = {}
158
        for key, value in self.data_source_query_url_tokens.items():
159
            if value == info:
160
                return key
161
        key = str(uuid.uuid4())
162
        self.data_source_query_url_tokens[key] = info
163
        if self.id:
164
            # do not save if the session has not been created yet, this happens
165
            # for API calls to create card/form data.
166
            self.store()
167
        return key
168

  
169
    def get_data_source_query_info_from_token(self, token):
170
        if not self.data_source_query_url_tokens:
171
            return None
172
        return self.data_source_query_url_tokens.get(token)
173

  
174 149

  
175 150
sessions.BasicSession = BasicSession
176 151
StorageSessionManager = sessions.StorageSessionManager
177
-