From 19e01fe6b737f2091e96a6dc7dbcd2b3267ed097 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 22 Nov 2024 14:39:10 -0400 Subject: [PATCH 1/5] Revert "fix: ensure csrf cookie for searches (#8260)" This reverts commit 622ded5d2b4db9d4bf948af0e1d0890edaa8bb33. --- ietf/doc/views_search.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index f4ad247ff0d..0f1937efb37 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -56,7 +56,6 @@ from django.utils.html import strip_tags from django.utils.cache import _generate_cache_key # type: ignore from django.utils.text import slugify -from django.views.decorators.csrf import ensure_csrf_cookie from django_stubs_ext import QuerySetAny import debug # pyflakes:ignore @@ -284,7 +283,6 @@ def retrieve_search_results(form, all_types=False): return docs -@ensure_csrf_cookie def search(request): """Search for a draft""" # defaults for results / meta @@ -337,7 +335,6 @@ def search(request): ) -@ensure_csrf_cookie def frontpage(request): form = SearchForm() return render(request, 'doc/frontpage.html', {'form':form}) From a1376da33b00056abba09c0bae995f4b3de51095 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 22 Nov 2024 14:39:30 -0400 Subject: [PATCH 2/5] Revert "refactor: eliminate single-use helper (#8226)" This reverts commit 6608c9d530b62d10c88d637f949a00fb5fea4526. --- ietf/doc/utils.py | 4 ++++ ietf/doc/views_search.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index a30430829a5..9b2570d8ba6 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -1046,6 +1046,10 @@ def get_replaces_tree(doc): return sorted(history, key=lambda x: x['published']) +def get_search_cache_key(key_fragment): + return f"doc:document:search:{key_fragment}" + + def build_file_urls(doc: Union[Document, DocHistory]): if doc.type_id == "rfc": base_path = os.path.join(settings.RFC_PATH, doc.name + ".") diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index 0f1937efb37..7b71dd77bd9 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -65,7 +65,7 @@ IESG_BALLOT_ACTIVE_STATES, IESG_STATCHG_CONFLREV_ACTIVE_STATES, IESG_CHARTER_ACTIVE_STATES ) from ietf.doc.fields import select2_id_doc_name_json -from ietf.doc.utils import augment_events_with_revision, needed_ballot_positions +from ietf.doc.utils import get_search_cache_key, augment_events_with_revision, needed_ballot_positions from ietf.group.models import Group from ietf.idindex.index import active_drafts_index_by_group from ietf.name.models import DocTagName, DocTypeName, StreamName @@ -292,7 +292,7 @@ def search(request): if request.method == "POST": form = SearchForm(data=request.POST) if form.is_valid(): - cache_key = f"doc:document:search:{form.cache_key_fragment()}" + cache_key = get_search_cache_key(form.cache_key_fragment()) cached_val = cache.get(cache_key) if cached_val: [results, meta] = cached_val From 797e329dc4fd48599989f87e92a1931a76952fd9 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 22 Nov 2024 14:39:38 -0400 Subject: [PATCH 3/5] Revert "feat: POST for document search requests (#8206)" This reverts commit b65a37b6e8f6cfe105cd89c61c7046fff9d21523. --- ietf/api/serializer.py | 1 - ietf/doc/tests.py | 155 ++++++--------------- ietf/doc/tests_js.py | 4 +- ietf/doc/utils.py | 11 +- ietf/doc/views_search.py | 109 +++++---------- ietf/liaisons/forms.py | 1 - ietf/liaisons/widgets.py | 1 - ietf/templates/doc/search/search_form.html | 2 - 8 files changed, 87 insertions(+), 197 deletions(-) diff --git a/ietf/api/serializer.py b/ietf/api/serializer.py index ca34ea649e5..27f194c5b52 100644 --- a/ietf/api/serializer.py +++ b/ietf/api/serializer.py @@ -146,7 +146,6 @@ def end_object(self, obj): field_value = None else: field_value = field - # Need QuerySetAny instead of QuerySet until django-stubs 5.0.1 if isinstance(field_value, QuerySetAny) or isinstance(field_value, list): self._current[name] = dict([ (rel.pk, self.expand_related(rel, name)) for rel in field_value ]) else: diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index abac10a5e96..f0c8e30626f 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -73,163 +73,96 @@ class SearchTests(TestCase): - def test_search_handles_querystring_parameters(self): - """Search parameters via querystring should not actually search""" - url = urlreverse("ietf.doc.views_search.search") - r = self.client.get(url + "?name=some-document-name&oldDrafts=on") - # Check that we got a valid response and that the warning about query string parameters is shown. - self.assertContains( - r, - "Searching via the URL query string is no longer supported.", - status_code=200, - ) - # Check that the form was filled in correctly (not an exhaustive check, but different from the - # form defaults) - pq = PyQuery(r.content) - self.assertEqual( - pq("form#search_form input#id_name").attr("value"), - "some-document-name", - "The name field should be set in the SearchForm", - ) - self.assertEqual( - pq("form#search_form input#id_olddrafts").attr("checked"), - "checked", - "The old drafts checkbox should be selected in the SearchForm", - ) - self.assertIsNone( - pq("form#search_form input#id_rfcs").attr("checked"), - "The RFCs checkbox should not be selected in the SearchForm", - ) - self.assertIsNone( - pq("form#search_form input#id_activedrafts").attr("checked"), - "The active drafts checkbox should not be selected in the SearchForm", - ) - def test_search(self): - draft = WgDraftFactory( - name="draft-ietf-mars-test", - group=GroupFactory(acronym="mars", parent=Group.objects.get(acronym="farfut")), - authors=[PersonFactory()], - ad=PersonFactory(), - ) + + draft = WgDraftFactory(name='draft-ietf-mars-test',group=GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut')),authors=[PersonFactory()],ad=PersonFactory()) rfc = WgRfcFactory() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="pub-req")) - old_draft = IndividualDraftFactory( - name="draft-foo-mars-test", - authors=[PersonFactory()], - title="Optimizing Martian Network Topologies", - ) + old_draft = IndividualDraftFactory(name='draft-foo-mars-test',authors=[PersonFactory()],title="Optimizing Martian Network Topologies") old_draft.set_state(State.objects.get(used=True, type="draft", slug="expired")) - - url = urlreverse("ietf.doc.views_search.search") - + + base_url = urlreverse('ietf.doc.views_search.search') + # only show form, no search yet - r = self.client.get(url) + r = self.client.get(base_url) self.assertEqual(r.status_code, 200) - + # no match - r = self.client.post(url, {"activedrafts": "on", "name": "thisisnotadocumentname"}) + r = self.client.get(base_url + "?activedrafts=on&name=thisisnotadocumentname") self.assertEqual(r.status_code, 200) self.assertContains(r, "No documents match") - - r = self.client.post(url, {"rfcs": "on", "name": "xyzzy"}) + + r = self.client.get(base_url + "?rfcs=on&name=xyzzy") self.assertEqual(r.status_code, 200) self.assertContains(r, "No documents match") - - r = self.client.post(url, {"olddrafts": "on", "name": "bar"}) + + r = self.client.get(base_url + "?olddrafts=on&name=bar") self.assertEqual(r.status_code, 200) self.assertContains(r, "No documents match") - - r = self.client.post(url, {"olddrafts": "on", "name": "foo"}) + + r = self.client.get(base_url + "?olddrafts=on&name=foo") self.assertEqual(r.status_code, 200) self.assertContains(r, "draft-foo-mars-test") - - r = self.client.post(url, {"olddrafts": "on", "name": "FoO"}) # mixed case + + r = self.client.get(base_url + "?olddrafts=on&name=FoO") # mixed case self.assertEqual(r.status_code, 200) self.assertContains(r, "draft-foo-mars-test") - + # find by RFC - r = self.client.post(url, {"rfcs": "on", "name": rfc.name}) + r = self.client.get(base_url + "?rfcs=on&name=%s" % rfc.name) self.assertEqual(r.status_code, 200) self.assertContains(r, rfc.title) - + # find by active/inactive - + draft.set_state(State.objects.get(type="draft", slug="active")) - r = self.client.post(url, {"activedrafts": "on", "name": draft.name}) + r = self.client.get(base_url + "?activedrafts=on&name=%s" % draft.name) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + draft.set_state(State.objects.get(type="draft", slug="expired")) - r = self.client.post(url, {"olddrafts": "on", "name": draft.name}) + r = self.client.get(base_url + "?olddrafts=on&name=%s" % draft.name) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + draft.set_state(State.objects.get(type="draft", slug="active")) - + # find by title - r = self.client.post(url, {"activedrafts": "on", "name": draft.title.split()[0]}) + r = self.client.get(base_url + "?activedrafts=on&name=%s" % draft.title.split()[0]) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + # find by author - r = self.client.post( - url, - { - "activedrafts": "on", - "by": "author", - "author": draft.documentauthor_set.first().person.name_parts()[1], - }, - ) + r = self.client.get(base_url + "?activedrafts=on&by=author&author=%s" % draft.documentauthor_set.first().person.name_parts()[1]) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + # find by group - r = self.client.post( - url, - {"activedrafts": "on", "by": "group", "group": draft.group.acronym}, - ) + r = self.client.get(base_url + "?activedrafts=on&by=group&group=%s" % draft.group.acronym) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - - r = self.client.post( - url, - {"activedrafts": "on", "by": "group", "group": draft.group.acronym.swapcase()}, - ) + + r = self.client.get(base_url + "?activedrafts=on&by=group&group=%s" % draft.group.acronym.swapcase()) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + # find by area - r = self.client.post( - url, - {"activedrafts": "on", "by": "area", "area": draft.group.parent_id}, - ) + r = self.client.get(base_url + "?activedrafts=on&by=area&area=%s" % draft.group.parent_id) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + # find by area - r = self.client.post( - url, - {"activedrafts": "on", "by": "area", "area": draft.group.parent_id}, - ) + r = self.client.get(base_url + "?activedrafts=on&by=area&area=%s" % draft.group.parent_id) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + # find by AD - r = self.client.post(url, {"activedrafts": "on", "by": "ad", "ad": draft.ad_id}) + r = self.client.get(base_url + "?activedrafts=on&by=ad&ad=%s" % draft.ad_id) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) - + # find by IESG state - r = self.client.post( - url, - { - "activedrafts": "on", - "by": "state", - "state": draft.get_state("draft-iesg").pk, - "substate": "", - }, - ) + r = self.client.get(base_url + "?activedrafts=on&by=state&state=%s&substate=" % draft.get_state("draft-iesg").pk) self.assertEqual(r.status_code, 200) self.assertContains(r, draft.title) @@ -238,15 +171,15 @@ def test_search_became_rfc(self): rfc = WgRfcFactory() draft.set_state(State.objects.get(type="draft", slug="rfc")) draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) - url = urlreverse("ietf.doc.views_search.search") + base_url = urlreverse('ietf.doc.views_search.search') # find by RFC - r = self.client.post(url, {"rfcs": "on", "name": rfc.name}) + r = self.client.get(base_url + f"?rfcs=on&name={rfc.name}") self.assertEqual(r.status_code, 200) self.assertContains(r, rfc.title) # find by draft - r = self.client.post(url, {"activedrafts": "on", "rfcs": "on", "name": draft.name}) + r = self.client.get(base_url + f"?activedrafts=on&rfcs=on&name={draft.name}") self.assertEqual(r.status_code, 200) self.assertContains(r, rfc.title) diff --git a/ietf/doc/tests_js.py b/ietf/doc/tests_js.py index 9a5aad13b9c..acd74c4a0b1 100644 --- a/ietf/doc/tests_js.py +++ b/ietf/doc/tests_js.py @@ -92,8 +92,10 @@ def _read_author_form(form_elt): self.assertEqual(len(author_forms), 1) # get the "add author" button so we can add blank author forms + add_author_button = self.driver.find_element(By.ID, 'add-author-button') for index, auth in enumerate(authors): - self.scroll_and_click((By.ID, 'add-author-button')) # Create new form. Automatically scrolls to it. + self.scroll_to_element(add_author_button) # Can only click if it's in view! + add_author_button.click() # Create a new form. Automatically scrolls to it. author_forms = authors_list.find_elements(By.CLASS_NAME, 'author-panel') authors_added = index + 1 self.assertEqual(len(author_forms), authors_added + 1) # Started with 1 author, hence +1 diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index 9b2570d8ba6..97243a20d64 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -3,7 +3,9 @@ import datetime +import hashlib import io +import json import math import os import re @@ -346,7 +348,6 @@ def augment_events_with_revision(doc, events): """Take a set of events for doc and add a .rev attribute with the revision they refer to by checking NewRevisionDocEvents.""" - # Need QuerySetAny instead of QuerySet until django-stubs 5.0.1 if isinstance(events, QuerySetAny): qs = events.filter(newrevisiondocevent__isnull=False) else: @@ -1046,8 +1047,12 @@ def get_replaces_tree(doc): return sorted(history, key=lambda x: x['published']) -def get_search_cache_key(key_fragment): - return f"doc:document:search:{key_fragment}" +def get_search_cache_key(params): + from ietf.doc.views_search import SearchForm + fields = set(SearchForm.base_fields) - set(['sort',]) + kwargs = dict([ (k,v) for (k,v) in list(params.items()) if k in fields ]) + key = "doc:document:search:" + hashlib.sha512(json.dumps(kwargs, sort_keys=True).encode('utf-8')).hexdigest() + return key def build_file_urls(doc: Union[Document, DocHistory]): diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index 7b71dd77bd9..4fa3b2560c5 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -37,8 +37,6 @@ import re import datetime import copy -import hashlib -import json import operator from collections import defaultdict @@ -46,17 +44,16 @@ from django import forms from django.conf import settings -from django.contrib import messages from django.core.cache import cache, caches from django.urls import reverse as urlreverse from django.db.models import Q -from django.http import Http404, HttpResponseBadRequest, HttpResponse, HttpResponseRedirect +from django.http import Http404, HttpResponseBadRequest, HttpResponse, HttpResponseRedirect, QueryDict from django.shortcuts import render from django.utils import timezone from django.utils.html import strip_tags from django.utils.cache import _generate_cache_key # type: ignore from django.utils.text import slugify -from django_stubs_ext import QuerySetAny + import debug # pyflakes:ignore @@ -148,29 +145,6 @@ def clean(self): q['irtfstate'] = None return q - def cache_key_fragment(self): - """Hash a bound form to get a value for use in a cache key - - Raises a ValueError if the form is not valid. - """ - def _serialize_value(val): - # Need QuerySetAny instead of QuerySet until django-stubs 5.0.1 - if isinstance(val, QuerySetAny): - return [item.pk for item in val] - else: - return getattr(val, "pk", val) # use pk if present, else value - - if not self.is_valid(): - raise ValueError(f"SearchForm invalid: {self.errors}") - contents = { - field_name: _serialize_value(field_value) - for field_name, field_value in self.cleaned_data.items() - if field_name != "sort" and field_value is not None - } - contents_json = json.dumps(contents, sort_keys=True) - return hashlib.sha512(contents_json.encode("utf-8")).hexdigest() - - def retrieve_search_results(form, all_types=False): """Takes a validated SearchForm and return the results.""" @@ -282,64 +256,45 @@ def retrieve_search_results(form, all_types=False): return docs - def search(request): - """Search for a draft""" - # defaults for results / meta - results = [] - meta = {"by": None, "searching": False} - - if request.method == "POST": - form = SearchForm(data=request.POST) - if form.is_valid(): - cache_key = get_search_cache_key(form.cache_key_fragment()) - cached_val = cache.get(cache_key) - if cached_val: - [results, meta] = cached_val - else: - results = retrieve_search_results(form) - results, meta = prepare_document_table( - request, results, form.cleaned_data - ) - cache.set( - cache_key, [results, meta] - ) # for settings.CACHE_MIDDLEWARE_SECONDS - log(f"Search results computed for {form.cleaned_data}") - meta["searching"] = True - else: - if request.GET: - # backwards compatibility - fill in the form - get_params = request.GET.copy() - if "activeDrafts" in request.GET: - get_params["activedrafts"] = request.GET["activeDrafts"] - if "oldDrafts" in request.GET: - get_params["olddrafts"] = request.GET["oldDrafts"] - if "subState" in request.GET: - get_params["substate"] = request.GET["subState"] - form = SearchForm(data=get_params) - messages.error( - request, - ( - "Searching via the URL query string is no longer supported. " - "The form below has been filled in with the parameters from your request. " - 'To execute your search, please click "Search".' - ), - ) + if request.GET: + # backwards compatibility + get_params = request.GET.copy() + if 'activeDrafts' in request.GET: + get_params['activedrafts'] = request.GET['activeDrafts'] + if 'oldDrafts' in request.GET: + get_params['olddrafts'] = request.GET['oldDrafts'] + if 'subState' in request.GET: + get_params['substate'] = request.GET['subState'] + + form = SearchForm(get_params) + if not form.is_valid(): + return HttpResponseBadRequest("form not valid: %s" % form.errors) + + cache_key = get_search_cache_key(get_params) + cached_val = cache.get(cache_key) + if cached_val: + [results, meta] = cached_val else: - form = SearchForm() + results = retrieve_search_results(form) + results, meta = prepare_document_table(request, results, get_params) + cache.set(cache_key, [results, meta]) # for settings.CACHE_MIDDLEWARE_SECONDS + log(f"Search results computed for {get_params}") + meta['searching'] = True + else: + form = SearchForm() + results = [] + meta = { 'by': None, 'searching': False } + get_params = QueryDict('') - return render( - request, - "doc/search/search.html", - context={"form": form, "docs": results, "meta": meta}, + return render(request, 'doc/search/search.html', { + 'form':form, 'docs':results, 'meta':meta, 'queryargs':get_params.urlencode() }, ) - def frontpage(request): form = SearchForm() return render(request, 'doc/frontpage.html', {'form':form}) - def search_for_name(request, name): def find_unique(n): exact = Document.objects.filter(name__iexact=n).first() diff --git a/ietf/liaisons/forms.py b/ietf/liaisons/forms.py index a75028bf798..1d91041b253 100644 --- a/ietf/liaisons/forms.py +++ b/ietf/liaisons/forms.py @@ -203,7 +203,6 @@ def get_results(self): class CustomModelMultipleChoiceField(ModelMultipleChoiceField): '''If value is a QuerySet, return it as is (for use in widget.render)''' def prepare_value(self, value): - # Need QuerySetAny instead of QuerySet until django-stubs 5.0.1 if isinstance(value, QuerySetAny): return value if (hasattr(value, '__iter__') and diff --git a/ietf/liaisons/widgets.py b/ietf/liaisons/widgets.py index 3d4f2d13a5d..74368e83f2b 100644 --- a/ietf/liaisons/widgets.py +++ b/ietf/liaisons/widgets.py @@ -35,7 +35,6 @@ def render(self, name, value, **kwargs): html = '
' % name html += 'No files attached' html += '
' - # Need QuerySetAny instead of QuerySet until django-stubs 5.0.1 if value and isinstance(value, QuerySetAny): for attachment in value: html += '%s ' % (conditional_escape(attachment.document.get_href()), conditional_escape(attachment.document.title)) diff --git a/ietf/templates/doc/search/search_form.html b/ietf/templates/doc/search/search_form.html index 6c91894c8c1..d4f463ec663 100644 --- a/ietf/templates/doc/search/search_form.html +++ b/ietf/templates/doc/search/search_form.html @@ -4,10 +4,8 @@ {% load widget_tweaks %} {% load ietf_filters %}
- {% csrf_token %}
{{ form.name|add_class:"form-control"|attr:"placeholder:Document name/title/RFC number"|attr:"aria-label:Document name/title/RFC number" }} From ce8f61b3ea931ffec9a315e667fc5b6b7cea0fa3 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 22 Nov 2024 15:05:50 -0400 Subject: [PATCH 4/5] test: add back test fix --- ietf/doc/tests_js.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ietf/doc/tests_js.py b/ietf/doc/tests_js.py index acd74c4a0b1..9a5aad13b9c 100644 --- a/ietf/doc/tests_js.py +++ b/ietf/doc/tests_js.py @@ -92,10 +92,8 @@ def _read_author_form(form_elt): self.assertEqual(len(author_forms), 1) # get the "add author" button so we can add blank author forms - add_author_button = self.driver.find_element(By.ID, 'add-author-button') for index, auth in enumerate(authors): - self.scroll_to_element(add_author_button) # Can only click if it's in view! - add_author_button.click() # Create a new form. Automatically scrolls to it. + self.scroll_and_click((By.ID, 'add-author-button')) # Create new form. Automatically scrolls to it. author_forms = authors_list.find_elements(By.CLASS_NAME, 'author-panel') authors_added = index + 1 self.assertEqual(len(author_forms), authors_added + 1) # Started with 1 author, hence +1 From 933733ec889488393ab4cf380dd10c3fee5d0f26 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 22 Nov 2024 15:06:39 -0400 Subject: [PATCH 5/5] refactor: eliminate single-use helper --- ietf/doc/utils.py | 11 +---------- ietf/doc/views_search.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index 97243a20d64..a30430829a5 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -3,9 +3,7 @@ import datetime -import hashlib import io -import json import math import os import re @@ -348,6 +346,7 @@ def augment_events_with_revision(doc, events): """Take a set of events for doc and add a .rev attribute with the revision they refer to by checking NewRevisionDocEvents.""" + # Need QuerySetAny instead of QuerySet until django-stubs 5.0.1 if isinstance(events, QuerySetAny): qs = events.filter(newrevisiondocevent__isnull=False) else: @@ -1047,14 +1046,6 @@ def get_replaces_tree(doc): return sorted(history, key=lambda x: x['published']) -def get_search_cache_key(params): - from ietf.doc.views_search import SearchForm - fields = set(SearchForm.base_fields) - set(['sort',]) - kwargs = dict([ (k,v) for (k,v) in list(params.items()) if k in fields ]) - key = "doc:document:search:" + hashlib.sha512(json.dumps(kwargs, sort_keys=True).encode('utf-8')).hexdigest() - return key - - def build_file_urls(doc: Union[Document, DocHistory]): if doc.type_id == "rfc": base_path = os.path.join(settings.RFC_PATH, doc.name + ".") diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index 4fa3b2560c5..9e9b5e88dd3 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -33,7 +33,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +import hashlib +import json import re import datetime import copy @@ -62,7 +63,7 @@ IESG_BALLOT_ACTIVE_STATES, IESG_STATCHG_CONFLREV_ACTIVE_STATES, IESG_CHARTER_ACTIVE_STATES ) from ietf.doc.fields import select2_id_doc_name_json -from ietf.doc.utils import get_search_cache_key, augment_events_with_revision, needed_ballot_positions +from ietf.doc.utils import augment_events_with_revision, needed_ballot_positions from ietf.group.models import Group from ietf.idindex.index import active_drafts_index_by_group from ietf.name.models import DocTagName, DocTypeName, StreamName @@ -256,7 +257,14 @@ def retrieve_search_results(form, all_types=False): return docs + def search(request): + def _get_cache_key(params): + fields = set(SearchForm.base_fields) - {'sort'} + kwargs = dict([(k, v) for (k, v) in list(params.items()) if k in fields]) + key = "doc:document:search:" + hashlib.sha512(json.dumps(kwargs, sort_keys=True).encode('utf-8')).hexdigest() + return key + if request.GET: # backwards compatibility get_params = request.GET.copy() @@ -271,7 +279,7 @@ def search(request): if not form.is_valid(): return HttpResponseBadRequest("form not valid: %s" % form.errors) - cache_key = get_search_cache_key(get_params) + cache_key = _get_cache_key(get_params) cached_val = cache.get(cache_key) if cached_val: [results, meta] = cached_val