fix: Move rfcdiff-latest-json API call from /doc to /api ()

Old /doc/rfcdiff-latest-json/ URL is changed to a permanent redirect to
/api/doc/rfcdiff-latest-json/
This commit is contained in:
Kesara Rathnayake 2023-02-18 05:39:47 +13:00 committed by GitHub
parent 81ffad9852
commit f70ce3eaab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 345 additions and 335 deletions

View file

@ -23,6 +23,8 @@ import debug # pyflakes:ignore
import ietf
from ietf.doc.utils import get_unicode_document_content
from ietf.doc.models import RelatedDocument, State
from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory
from ietf.group.factories import RoleFactory
from ietf.meeting.factories import MeetingFactory, SessionFactory
from ietf.meeting.test_data import make_meeting_test_data
@ -33,7 +35,7 @@ from ietf.person.models import PersonalApiKey
from ietf.stats.models import MeetingRegistration
from ietf.utils.mail import outbox, get_payload_text
from ietf.utils.models import DumpInfo
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
from ietf.utils.test_utils import TestCase, login_testing_unauthorized, reload_db_objects
OMITTED_APPS = (
'ietf.secr.meetings',
@ -589,3 +591,198 @@ class TastypieApiTestCase(ResourceTestCaseMixin, TestCase):
#print("There doesn't seem to be any resource for model %s.models.%s"%(app.__name__,model.__name__,))
self.assertIn(model._meta.model_name, list(app_resources.keys()),
"There doesn't seem to be any API resource for model %s.models.%s"%(app.__name__,model.__name__,))
class RfcdiffSupportTests(TestCase):
def setUp(self):
super().setUp()
self.target_view = 'ietf.api.views.rfcdiff_latest_json'
self._last_rfc_num = 8000
def getJson(self, view_args):
url = urlreverse(self.target_view, kwargs=view_args)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
return r.json()
def next_rfc_number(self):
self._last_rfc_num += 1
return self._last_rfc_num
def do_draft_test(self, name):
draft = IndividualDraftFactory(name=name, rev='00', create_revisions=range(0,13))
draft = reload_db_objects(draft)
received = self.getJson(dict(name=draft.name))
self.assertEqual(
received,
dict(
name=draft.name,
rev=draft.rev,
content_url=draft.get_href(),
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
),
'Incorrect JSON when draft revision not specified',
)
received = self.getJson(dict(name=draft.name, rev=draft.rev))
self.assertEqual(
received,
dict(
name=draft.name,
rev=draft.rev,
content_url=draft.get_href(),
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
),
'Incorrect JSON when latest revision specified',
)
received = self.getJson(dict(name=draft.name, rev='10'))
self.assertEqual(
received,
dict(
name=draft.name,
rev='10',
content_url=draft.history_set.get(rev='10').get_href(),
previous=f'{draft.name}-09'
),
'Incorrect JSON when historical revision specified',
)
received = self.getJson(dict(name=draft.name, rev='00'))
self.assertNotIn('previous', received, 'Rev 00 has no previous name when not replacing a draft')
replaced = IndividualDraftFactory()
RelatedDocument.objects.create(relationship_id='replaces',source=draft,target=replaced.docalias.first())
received = self.getJson(dict(name=draft.name, rev='00'))
self.assertEqual(received['previous'], f'{replaced.name}-{replaced.rev}',
'Rev 00 has a previous name when replacing a draft')
def test_draft(self):
# test with typical, straightforward names
self.do_draft_test(name='draft-somebody-did-a-thing')
# try with different potentially problematic names
self.do_draft_test(name='draft-someone-did-something-01-02')
self.do_draft_test(name='draft-someone-did-something-else-02')
self.do_draft_test(name='draft-someone-did-something-02-weird-01')
def do_draft_with_broken_history_test(self, name):
draft = IndividualDraftFactory(name=name, rev='10')
received = self.getJson(dict(name=draft.name,rev='09'))
self.assertEqual(received['rev'],'09')
self.assertEqual(received['previous'], f'{draft.name}-08')
self.assertTrue('warning' in received)
def test_draft_with_broken_history(self):
# test with typical, straightforward names
self.do_draft_with_broken_history_test(name='draft-somebody-did-something')
# try with different potentially problematic names
self.do_draft_with_broken_history_test(name='draft-someone-did-something-01-02')
self.do_draft_with_broken_history_test(name='draft-someone-did-something-else-02')
self.do_draft_with_broken_history_test(name='draft-someone-did-something-02-weird-03')
def do_rfc_test(self, draft_name):
draft = WgDraftFactory(name=draft_name, create_revisions=range(0,2))
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
draft = reload_db_objects(draft)
rfc = draft
number = rfc.rfc_number()
received = self.getJson(dict(name=number))
self.assertEqual(
received,
dict(
content_url=rfc.get_href(),
name=rfc.canonical_name(),
previous=f'{draft.name}-{draft.rev}',
),
'Can look up an RFC by number',
)
num_received = received
received = self.getJson(dict(name=rfc.canonical_name()))
self.assertEqual(num_received, received, 'RFC by canonical name gives same result as by number')
received = self.getJson(dict(name=f'RfC {number}'))
self.assertEqual(num_received, received, 'RFC with unusual spacing/caps gives same result as by number')
received = self.getJson(dict(name=draft.name))
self.assertEqual(num_received, received, 'RFC by draft name and no rev gives same result as by number')
received = self.getJson(dict(name=draft.name, rev='01'))
self.assertEqual(
received,
dict(
content_url=draft.history_set.get(rev='01').get_href(),
name=draft.name,
rev='01',
previous=f'{draft.name}-00',
),
'RFC by draft name with rev should give draft name, not canonical name'
)
def test_rfc(self):
# simple draft name
self.do_rfc_test(draft_name='draft-test-ar-ef-see')
# tricky draft names
self.do_rfc_test(draft_name='draft-whatever-02')
self.do_rfc_test(draft_name='draft-test-me-03-04')
def test_rfc_with_tombstone(self):
draft = WgDraftFactory(create_revisions=range(0,2))
draft.docalias.create(name='rfc3261') # See views_doc.HAS_TOMBSTONE
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
draft = reload_db_objects(draft)
rfc = draft
# Some old rfcs had tombstones that shouldn't be used for comparisons
received = self.getJson(dict(name=rfc.canonical_name()))
self.assertTrue(received['previous'].endswith('00'))
def do_rfc_with_broken_history_test(self, draft_name):
draft = WgDraftFactory(rev='10', name=draft_name)
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
draft = reload_db_objects(draft)
rfc = draft
received = self.getJson(dict(name=draft.name))
self.assertEqual(
received,
dict(
content_url=rfc.get_href(),
name=rfc.canonical_name(),
previous=f'{draft.name}-10',
),
'RFC by draft name without rev should return canonical RFC name and no rev',
)
received = self.getJson(dict(name=draft.name, rev='10'))
self.assertEqual(received['name'], draft.name, 'RFC by draft name with rev should return draft name')
self.assertEqual(received['rev'], '10', 'Requested rev should be returned')
self.assertEqual(received['previous'], f'{draft.name}-09', 'Previous rev is one less than requested')
self.assertIn(f'{draft.name}-10', received['content_url'], 'Returned URL should include requested rev')
self.assertNotIn('warning', received, 'No warning when we have the rev requested')
received = self.getJson(dict(name=f'{draft.name}-09'))
self.assertEqual(received['name'], draft.name, 'RFC by draft name with rev should return draft name')
self.assertEqual(received['rev'], '09', 'Requested rev should be returned')
self.assertEqual(received['previous'], f'{draft.name}-08', 'Previous rev is one less than requested')
self.assertIn(f'{draft.name}-09', received['content_url'], 'Returned URL should include requested rev')
self.assertEqual(
received['warning'],
'History for this version not found - these results are speculation',
'Warning should be issued when requested rev is not found'
)
def test_rfc_with_broken_history(self):
# simple draft name
self.do_rfc_with_broken_history_test(draft_name='draft-some-draft')
# tricky draft names
self.do_rfc_with_broken_history_test(draft_name='draft-gizmo-01')
self.do_rfc_with_broken_history_test(draft_name='draft-oh-boy-what-a-draft-02-03')

View file

@ -1,5 +1,6 @@
# Copyright The IETF Trust 2017, All Rights Reserved
from django.conf import settings
from django.conf.urls import include
from django.views.generic import TemplateView
@ -56,6 +57,9 @@ urlpatterns = [
url(r'^version/?$', api_views.version),
# Application authentication API key
url(r'^appauth/[authortools|bibxml]', api_views.app_auth),
# latest versions
url(r'^rfcdiff-latest-json/%(name)s(?:-%(rev)s)?(\.txt|\.html)?/?$' % settings.URL_REGEXPS, api_views.rfcdiff_latest_json),
url(r'^rfcdiff-latest-json/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', api_views.rfcdiff_latest_json),
]
# Additional (standard) Tastypie endpoints

View file

@ -4,6 +4,7 @@
import json
import pytz
import re
from jwcrypto.jwk import JWK
@ -12,7 +13,7 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.http import HttpResponse
from django.http import HttpResponse, Http404
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.utils.decorators import method_decorator
@ -31,10 +32,12 @@ import ietf
from ietf.person.models import Person, Email
from ietf.api import _api_list
from ietf.api.serializer import JsonExportMixin
from ietf.doc.utils import fuzzy_find_documents
from ietf.ietfauth.views import send_account_creation_email
from ietf.ietfauth.utils import role_required
from ietf.meeting.models import Meeting
from ietf.stats.models import MeetingRegistration
from ietf.utils import log
from ietf.utils.decorators import require_api_key
from ietf.utils.models import DumpInfo
@ -225,3 +228,138 @@ def app_auth(request):
return HttpResponse(
json.dumps({'success': True}),
content_type='application/json')
def find_doc_for_rfcdiff(name, rev):
"""rfcdiff lookup heuristics
Returns a tuple with:
[0] - condition string
[1] - document found (or None)
[2] - historic version
[3] - revision actually found (may differ from :rev: input)
"""
found = fuzzy_find_documents(name, rev)
condition = 'no such document'
if found.documents.count() != 1:
return (condition, None, None, rev)
doc = found.documents.get()
if found.matched_rev is None or doc.rev == found.matched_rev:
condition = 'current version'
return (condition, doc, None, found.matched_rev)
else:
candidate = doc.history_set.filter(rev=found.matched_rev).order_by("-time").first()
if candidate:
condition = 'historic version'
return (condition, doc, candidate, found.matched_rev)
else:
condition = 'version dochistory not found'
return (condition, doc, None, found.matched_rev)
# This is a proof of concept of a service that would redirect to the current revision
# def rfcdiff_latest(request, name, rev=None):
# condition, doc, history = find_doc_for_rfcdiff(name, rev)
# if not doc:
# raise Http404
# if history:
# return redirect(history.get_href())
# else:
# return redirect(doc.get_href())
HAS_TOMBSTONE = [
2821, 2822, 2873, 2919, 2961, 3023, 3029, 3031, 3032, 3033, 3034, 3035, 3036,
3037, 3038, 3042, 3044, 3050, 3052, 3054, 3055, 3056, 3057, 3059, 3060, 3061,
3062, 3063, 3064, 3067, 3068, 3069, 3070, 3071, 3072, 3073, 3074, 3075, 3076,
3077, 3078, 3080, 3081, 3082, 3084, 3085, 3086, 3087, 3088, 3089, 3090, 3094,
3095, 3096, 3097, 3098, 3101, 3102, 3103, 3104, 3105, 3106, 3107, 3108, 3109,
3110, 3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, 3121, 3123,
3124, 3126, 3127, 3128, 3130, 3131, 3132, 3133, 3134, 3135, 3136, 3137, 3138,
3139, 3140, 3141, 3142, 3143, 3144, 3145, 3147, 3149, 3150, 3151, 3152, 3153,
3154, 3155, 3156, 3157, 3158, 3159, 3160, 3161, 3162, 3163, 3164, 3165, 3166,
3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 3176, 3179, 3180, 3181, 3182,
3183, 3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, 3192, 3193, 3194, 3197,
3198, 3201, 3202, 3203, 3204, 3205, 3206, 3207, 3208, 3209, 3210, 3211, 3212,
3213, 3214, 3215, 3216, 3217, 3218, 3220, 3221, 3222, 3224, 3225, 3226, 3227,
3228, 3229, 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3240, 3241,
3242, 3243, 3244, 3245, 3246, 3247, 3248, 3249, 3250, 3253, 3254, 3255, 3256,
3257, 3258, 3259, 3260, 3261, 3262, 3263, 3264, 3265, 3266, 3267, 3268, 3269,
3270, 3271, 3272, 3273, 3274, 3275, 3276, 3278, 3279, 3280, 3281, 3282, 3283,
3284, 3285, 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, 3294, 3295, 3296,
3297, 3298, 3301, 3302, 3303, 3304, 3305, 3307, 3308, 3309, 3310, 3311, 3312,
3313, 3315, 3317, 3318, 3319, 3320, 3321, 3322, 3323, 3324, 3325, 3326, 3327,
3329, 3330, 3331, 3332, 3334, 3335, 3336, 3338, 3340, 3341, 3342, 3343, 3346,
3348, 3349, 3351, 3352, 3353, 3354, 3355, 3356, 3360, 3361, 3362, 3363, 3364,
3366, 3367, 3368, 3369, 3370, 3371, 3372, 3374, 3375, 3377, 3378, 3379, 3383,
3384, 3385, 3386, 3387, 3388, 3389, 3390, 3391, 3394, 3395, 3396, 3397, 3398,
3401, 3402, 3403, 3404, 3405, 3406, 3407, 3408, 3409, 3410, 3411, 3412, 3413,
3414, 3415, 3416, 3417, 3418, 3419, 3420, 3421, 3422, 3423, 3424, 3425, 3426,
3427, 3428, 3429, 3430, 3431, 3433, 3434, 3435, 3436, 3437, 3438, 3439, 3440,
3441, 3443, 3444, 3445, 3446, 3447, 3448, 3449, 3450, 3451, 3452, 3453, 3454,
3455, 3458, 3459, 3460, 3461, 3462, 3463, 3464, 3465, 3466, 3467, 3468, 3469,
3470, 3471, 3472, 3473, 3474, 3475, 3476, 3477, 3480, 3481, 3483, 3485, 3488,
3494, 3495, 3496, 3497, 3498, 3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508,
3509, 3511, 3512, 3515, 3516, 3517, 3518, 3520, 3521, 3522, 3523, 3524, 3525,
3527, 3529, 3530, 3532, 3533, 3534, 3536, 3537, 3538, 3539, 3541, 3543, 3544,
3545, 3546, 3547, 3548, 3549, 3550, 3551, 3552, 3555, 3556, 3557, 3558, 3559,
3560, 3562, 3563, 3564, 3565, 3568, 3569, 3570, 3571, 3572, 3573, 3574, 3575,
3576, 3577, 3578, 3579, 3580, 3581, 3582, 3583, 3584, 3588, 3589, 3590, 3591,
3592, 3593, 3594, 3595, 3597, 3598, 3601, 3607, 3609, 3610, 3612, 3614, 3615,
3616, 3625, 3627, 3630, 3635, 3636, 3637, 3638
]
def rfcdiff_latest_json(request, name, rev=None):
response = dict()
condition, document, history, found_rev = find_doc_for_rfcdiff(name, rev)
if condition == 'no such document':
raise Http404
elif condition in ('historic version', 'current version'):
doc = history if history else document
if not found_rev and doc.is_rfc():
response['content_url'] = doc.get_href()
response['name']=doc.canonical_name()
if doc.name != doc.canonical_name():
prev_rev = doc.rev
# not sure what to do if non-numeric values come back, so at least log it
log.assertion('doc.rfc_number().isdigit()') # .rfc_number() is expensive...
log.assertion('doc.rev.isdigit()')
if int(doc.rfc_number()) in HAS_TOMBSTONE and prev_rev != '00':
prev_rev = f'{(int(doc.rev)-1):02d}'
response['previous'] = f'{doc.name}-{prev_rev}'
else:
doc.is_rfc = lambda: False
response['content_url'] = doc.get_href()
response['rev'] = doc.rev
response['name'] = doc.name
if doc.rev == '00':
replaces_docs = (history.doc if condition=='historic version' else doc).related_that_doc('replaces')
if replaces_docs:
replaces = replaces_docs[0].document
response['previous'] = f'{replaces.name}-{replaces.rev}'
else:
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
if match and match.group(2):
response['previous'] = f'rfc{match.group(2)}'
else:
# not sure what to do if non-numeric values come back, so at least log it
log.assertion('doc.rev.isdigit()')
response['previous'] = f'{doc.name}-{(int(doc.rev)-1):02d}'
elif condition == 'version dochistory not found':
response['warning'] = 'History for this version not found - these results are speculation'
response['name'] = document.name
response['rev'] = found_rev
document.rev = found_rev
document.is_rfc = lambda: False
response['content_url'] = document.get_href()
# not sure what to do if non-numeric values come back, so at least log it
log.assertion('found_rev.isdigit()')
if int(found_rev) > 0:
response['previous'] = f'{document.name}-{(int(found_rev)-1):02d}'
else:
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
if match and match.group(2):
response['previous'] = f'rfc{match.group(2)}'
if not response:
raise Http404
return HttpResponse(json.dumps(response), content_type='application/json')

View file

@ -56,7 +56,7 @@ from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName
from ietf.person.models import Person
from ietf.person.factories import PersonFactory, EmailFactory
from ietf.utils.mail import outbox, empty_outbox
from ietf.utils.test_utils import login_testing_unauthorized, unicontent, reload_db_objects
from ietf.utils.test_utils import login_testing_unauthorized, unicontent
from ietf.utils.test_utils import TestCase
from ietf.utils.text import normalize_text
from ietf.utils.timezone import date_today, datetime_today, DEADLINE_TZINFO, RPC_TZINFO
@ -2662,200 +2662,6 @@ class Idnits2SupportTests(TestCase):
self.assertEqual(r.status_code, 200)
self.assertContains(r,'Proposed')
class RfcdiffSupportTests(TestCase):
def setUp(self):
super().setUp()
self.target_view = 'ietf.doc.views_doc.rfcdiff_latest_json'
self._last_rfc_num = 8000
def getJson(self, view_args):
url = urlreverse(self.target_view, kwargs=view_args)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
return r.json()
def next_rfc_number(self):
self._last_rfc_num += 1
return self._last_rfc_num
def do_draft_test(self, name):
draft = IndividualDraftFactory(name=name, rev='00', create_revisions=range(0,13))
draft = reload_db_objects(draft)
received = self.getJson(dict(name=draft.name))
self.assertEqual(
received,
dict(
name=draft.name,
rev=draft.rev,
content_url=draft.get_href(),
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
),
'Incorrect JSON when draft revision not specified',
)
received = self.getJson(dict(name=draft.name, rev=draft.rev))
self.assertEqual(
received,
dict(
name=draft.name,
rev=draft.rev,
content_url=draft.get_href(),
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
),
'Incorrect JSON when latest revision specified',
)
received = self.getJson(dict(name=draft.name, rev='10'))
self.assertEqual(
received,
dict(
name=draft.name,
rev='10',
content_url=draft.history_set.get(rev='10').get_href(),
previous=f'{draft.name}-09'
),
'Incorrect JSON when historical revision specified',
)
received = self.getJson(dict(name=draft.name, rev='00'))
self.assertNotIn('previous', received, 'Rev 00 has no previous name when not replacing a draft')
replaced = IndividualDraftFactory()
RelatedDocument.objects.create(relationship_id='replaces',source=draft,target=replaced.docalias.first())
received = self.getJson(dict(name=draft.name, rev='00'))
self.assertEqual(received['previous'], f'{replaced.name}-{replaced.rev}',
'Rev 00 has a previous name when replacing a draft')
def test_draft(self):
# test with typical, straightforward names
self.do_draft_test(name='draft-somebody-did-a-thing')
# try with different potentially problematic names
self.do_draft_test(name='draft-someone-did-something-01-02')
self.do_draft_test(name='draft-someone-did-something-else-02')
self.do_draft_test(name='draft-someone-did-something-02-weird-01')
def do_draft_with_broken_history_test(self, name):
draft = IndividualDraftFactory(name=name, rev='10')
received = self.getJson(dict(name=draft.name,rev='09'))
self.assertEqual(received['rev'],'09')
self.assertEqual(received['previous'], f'{draft.name}-08')
self.assertTrue('warning' in received)
def test_draft_with_broken_history(self):
# test with typical, straightforward names
self.do_draft_with_broken_history_test(name='draft-somebody-did-something')
# try with different potentially problematic names
self.do_draft_with_broken_history_test(name='draft-someone-did-something-01-02')
self.do_draft_with_broken_history_test(name='draft-someone-did-something-else-02')
self.do_draft_with_broken_history_test(name='draft-someone-did-something-02-weird-03')
def do_rfc_test(self, draft_name):
draft = WgDraftFactory(name=draft_name, create_revisions=range(0,2))
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
draft = reload_db_objects(draft)
rfc = draft
number = rfc.rfc_number()
received = self.getJson(dict(name=number))
self.assertEqual(
received,
dict(
content_url=rfc.get_href(),
name=rfc.canonical_name(),
previous=f'{draft.name}-{draft.rev}',
),
'Can look up an RFC by number',
)
num_received = received
received = self.getJson(dict(name=rfc.canonical_name()))
self.assertEqual(num_received, received, 'RFC by canonical name gives same result as by number')
received = self.getJson(dict(name=f'RfC {number}'))
self.assertEqual(num_received, received, 'RFC with unusual spacing/caps gives same result as by number')
received = self.getJson(dict(name=draft.name))
self.assertEqual(num_received, received, 'RFC by draft name and no rev gives same result as by number')
received = self.getJson(dict(name=draft.name, rev='01'))
self.assertEqual(
received,
dict(
content_url=draft.history_set.get(rev='01').get_href(),
name=draft.name,
rev='01',
previous=f'{draft.name}-00',
),
'RFC by draft name with rev should give draft name, not canonical name'
)
def test_rfc(self):
# simple draft name
self.do_rfc_test(draft_name='draft-test-ar-ef-see')
# tricky draft names
self.do_rfc_test(draft_name='draft-whatever-02')
self.do_rfc_test(draft_name='draft-test-me-03-04')
def test_rfc_with_tombstone(self):
draft = WgDraftFactory(create_revisions=range(0,2))
draft.docalias.create(name='rfc3261') # See views_doc.HAS_TOMBSTONE
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
draft = reload_db_objects(draft)
rfc = draft
# Some old rfcs had tombstones that shouldn't be used for comparisons
received = self.getJson(dict(name=rfc.canonical_name()))
self.assertTrue(received['previous'].endswith('00'))
def do_rfc_with_broken_history_test(self, draft_name):
draft = WgDraftFactory(rev='10', name=draft_name)
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
draft = reload_db_objects(draft)
rfc = draft
received = self.getJson(dict(name=draft.name))
self.assertEqual(
received,
dict(
content_url=rfc.get_href(),
name=rfc.canonical_name(),
previous=f'{draft.name}-10',
),
'RFC by draft name without rev should return canonical RFC name and no rev',
)
received = self.getJson(dict(name=draft.name, rev='10'))
self.assertEqual(received['name'], draft.name, 'RFC by draft name with rev should return draft name')
self.assertEqual(received['rev'], '10', 'Requested rev should be returned')
self.assertEqual(received['previous'], f'{draft.name}-09', 'Previous rev is one less than requested')
self.assertIn(f'{draft.name}-10', received['content_url'], 'Returned URL should include requested rev')
self.assertNotIn('warning', received, 'No warning when we have the rev requested')
received = self.getJson(dict(name=f'{draft.name}-09'))
self.assertEqual(received['name'], draft.name, 'RFC by draft name with rev should return draft name')
self.assertEqual(received['rev'], '09', 'Requested rev should be returned')
self.assertEqual(received['previous'], f'{draft.name}-08', 'Previous rev is one less than requested')
self.assertIn(f'{draft.name}-09', received['content_url'], 'Returned URL should include requested rev')
self.assertEqual(
received['warning'],
'History for this version not found - these results are speculation',
'Warning should be issued when requested rev is not found'
)
def test_rfc_with_broken_history(self):
# simple draft name
self.do_rfc_with_broken_history_test(draft_name='draft-some-draft')
# tricky draft names
self.do_rfc_with_broken_history_test(draft_name='draft-gizmo-01')
self.do_rfc_with_broken_history_test(draft_name='draft-oh-boy-what-a-draft-02-03')
class RawIdTests(TestCase):

View file

@ -85,11 +85,6 @@ urlpatterns = [
url(r'^html/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', views_doc.document_html),
url(r'^idnits2-rfcs-obsoleted/?$', views_doc.idnits2_rfcs_obsoleted),
url(r'^idnits2-rfc-status/?$', views_doc.idnits2_rfc_status),
# These two are proof-of-concept of a service that would redirect to the latest version
# url(r'^rfcdiff-latest/%(name)s(?:-%(rev)s)?(\.txt|\.html)?/?$' % settings.URL_REGEXPS, views_doc.rfcdiff_latest),
# url(r'^rfcdiff-latest/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', views_doc.rfcdiff_latest),
url(r'^rfcdiff-latest-json/%(name)s(?:-%(rev)s)?(\.txt|\.html)?/?$' % settings.URL_REGEXPS, views_doc.rfcdiff_latest_json),
url(r'^rfcdiff-latest-json/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', views_doc.rfcdiff_latest_json),
url(r'^all/?$', views_search.index_all_drafts),
url(r'^active/?$', views_search.index_active_drafts),
@ -180,4 +175,7 @@ urlpatterns = [
url(r'^%(name)s/session/' % settings.URL_REGEXPS, include('ietf.doc.urls_material')),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/session/', include(session_patterns)),
url(r'^(?P<name>[A-Za-z0-9\._\+\-]+)$', views_search.search_for_name),
# latest versions - keep old URLs alive during migration period
url(r'^rfcdiff-latest-json/%(name)s(?:-%(rev)s)?(\.txt|\.html)?/?$' % settings.URL_REGEXPS, RedirectView.as_view(pattern_name='ietf.api.views.rfcdiff_latest_json', permanent=True)),
url(r'^rfcdiff-latest-json/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', RedirectView.as_view(pattern_name='ietf.api.views.rfcdiff_latest_json', permanent=True)),
]

View file

@ -1943,136 +1943,3 @@ def idnits2_state(request, name, rev=None):
else:
doc.deststatus="Unknown"
return render(request, 'doc/idnits2-state.txt', context={'doc':doc}, content_type='text/plain;charset=utf-8')
def find_doc_for_rfcdiff(name, rev):
"""rfcdiff lookup heuristics
Returns a tuple with:
[0] - condition string
[1] - document found (or None)
[2] - historic version
[3] - revision actually found (may differ from :rev: input)
"""
found = fuzzy_find_documents(name, rev)
condition = 'no such document'
if found.documents.count() != 1:
return (condition, None, None, rev)
doc = found.documents.get()
if found.matched_rev is None or doc.rev == found.matched_rev:
condition = 'current version'
return (condition, doc, None, found.matched_rev)
else:
candidate = doc.history_set.filter(rev=found.matched_rev).order_by("-time").first()
if candidate:
condition = 'historic version'
return (condition, doc, candidate, found.matched_rev)
else:
condition = 'version dochistory not found'
return (condition, doc, None, found.matched_rev)
# This is a proof of concept of a service that would redirect to the current revision
# def rfcdiff_latest(request, name, rev=None):
# condition, doc, history = find_doc_for_rfcdiff(name, rev)
# if not doc:
# raise Http404
# if history:
# return redirect(history.get_href())
# else:
# return redirect(doc.get_href())
HAS_TOMBSTONE = [
2821, 2822, 2873, 2919, 2961, 3023, 3029, 3031, 3032, 3033, 3034, 3035, 3036,
3037, 3038, 3042, 3044, 3050, 3052, 3054, 3055, 3056, 3057, 3059, 3060, 3061,
3062, 3063, 3064, 3067, 3068, 3069, 3070, 3071, 3072, 3073, 3074, 3075, 3076,
3077, 3078, 3080, 3081, 3082, 3084, 3085, 3086, 3087, 3088, 3089, 3090, 3094,
3095, 3096, 3097, 3098, 3101, 3102, 3103, 3104, 3105, 3106, 3107, 3108, 3109,
3110, 3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, 3121, 3123,
3124, 3126, 3127, 3128, 3130, 3131, 3132, 3133, 3134, 3135, 3136, 3137, 3138,
3139, 3140, 3141, 3142, 3143, 3144, 3145, 3147, 3149, 3150, 3151, 3152, 3153,
3154, 3155, 3156, 3157, 3158, 3159, 3160, 3161, 3162, 3163, 3164, 3165, 3166,
3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 3176, 3179, 3180, 3181, 3182,
3183, 3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, 3192, 3193, 3194, 3197,
3198, 3201, 3202, 3203, 3204, 3205, 3206, 3207, 3208, 3209, 3210, 3211, 3212,
3213, 3214, 3215, 3216, 3217, 3218, 3220, 3221, 3222, 3224, 3225, 3226, 3227,
3228, 3229, 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3240, 3241,
3242, 3243, 3244, 3245, 3246, 3247, 3248, 3249, 3250, 3253, 3254, 3255, 3256,
3257, 3258, 3259, 3260, 3261, 3262, 3263, 3264, 3265, 3266, 3267, 3268, 3269,
3270, 3271, 3272, 3273, 3274, 3275, 3276, 3278, 3279, 3280, 3281, 3282, 3283,
3284, 3285, 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, 3294, 3295, 3296,
3297, 3298, 3301, 3302, 3303, 3304, 3305, 3307, 3308, 3309, 3310, 3311, 3312,
3313, 3315, 3317, 3318, 3319, 3320, 3321, 3322, 3323, 3324, 3325, 3326, 3327,
3329, 3330, 3331, 3332, 3334, 3335, 3336, 3338, 3340, 3341, 3342, 3343, 3346,
3348, 3349, 3351, 3352, 3353, 3354, 3355, 3356, 3360, 3361, 3362, 3363, 3364,
3366, 3367, 3368, 3369, 3370, 3371, 3372, 3374, 3375, 3377, 3378, 3379, 3383,
3384, 3385, 3386, 3387, 3388, 3389, 3390, 3391, 3394, 3395, 3396, 3397, 3398,
3401, 3402, 3403, 3404, 3405, 3406, 3407, 3408, 3409, 3410, 3411, 3412, 3413,
3414, 3415, 3416, 3417, 3418, 3419, 3420, 3421, 3422, 3423, 3424, 3425, 3426,
3427, 3428, 3429, 3430, 3431, 3433, 3434, 3435, 3436, 3437, 3438, 3439, 3440,
3441, 3443, 3444, 3445, 3446, 3447, 3448, 3449, 3450, 3451, 3452, 3453, 3454,
3455, 3458, 3459, 3460, 3461, 3462, 3463, 3464, 3465, 3466, 3467, 3468, 3469,
3470, 3471, 3472, 3473, 3474, 3475, 3476, 3477, 3480, 3481, 3483, 3485, 3488,
3494, 3495, 3496, 3497, 3498, 3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508,
3509, 3511, 3512, 3515, 3516, 3517, 3518, 3520, 3521, 3522, 3523, 3524, 3525,
3527, 3529, 3530, 3532, 3533, 3534, 3536, 3537, 3538, 3539, 3541, 3543, 3544,
3545, 3546, 3547, 3548, 3549, 3550, 3551, 3552, 3555, 3556, 3557, 3558, 3559,
3560, 3562, 3563, 3564, 3565, 3568, 3569, 3570, 3571, 3572, 3573, 3574, 3575,
3576, 3577, 3578, 3579, 3580, 3581, 3582, 3583, 3584, 3588, 3589, 3590, 3591,
3592, 3593, 3594, 3595, 3597, 3598, 3601, 3607, 3609, 3610, 3612, 3614, 3615,
3616, 3625, 3627, 3630, 3635, 3636, 3637, 3638
]
def rfcdiff_latest_json(request, name, rev=None):
response = dict()
condition, document, history, found_rev = find_doc_for_rfcdiff(name, rev)
if condition == 'no such document':
raise Http404
elif condition in ('historic version', 'current version'):
doc = history if history else document
if not found_rev and doc.is_rfc():
response['content_url'] = doc.get_href()
response['name']=doc.canonical_name()
if doc.name != doc.canonical_name():
prev_rev = doc.rev
# not sure what to do if non-numeric values come back, so at least log it
log.assertion('doc.rfc_number().isdigit()') # .rfc_number() is expensive...
log.assertion('doc.rev.isdigit()')
if int(doc.rfc_number()) in HAS_TOMBSTONE and prev_rev != '00':
prev_rev = f'{(int(doc.rev)-1):02d}'
response['previous'] = f'{doc.name}-{prev_rev}'
else:
doc.is_rfc = lambda: False
response['content_url'] = doc.get_href()
response['rev'] = doc.rev
response['name'] = doc.name
if doc.rev == '00':
replaces_docs = (history.doc if condition=='historic version' else doc).related_that_doc('replaces')
if replaces_docs:
replaces = replaces_docs[0].document
response['previous'] = f'{replaces.name}-{replaces.rev}'
else:
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
if match and match.group(2):
response['previous'] = f'rfc{match.group(2)}'
else:
# not sure what to do if non-numeric values come back, so at least log it
log.assertion('doc.rev.isdigit()')
response['previous'] = f'{doc.name}-{(int(doc.rev)-1):02d}'
elif condition == 'version dochistory not found':
response['warning'] = 'History for this version not found - these results are speculation'
response['name'] = document.name
response['rev'] = found_rev
document.rev = found_rev
document.is_rfc = lambda: False
response['content_url'] = document.get_href()
# not sure what to do if non-numeric values come back, so at least log it
log.assertion('found_rev.isdigit()')
if int(found_rev) > 0:
response['previous'] = f'{document.name}-{(int(found_rev)-1):02d}'
else:
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
if match and match.group(2):
response['previous'] = f'rfc{match.group(2)}'
if not response:
raise Http404
return HttpResponse(json.dumps(response), content_type='application/json')