Skip to content

Commit 036966d

Browse files
committed
Provide a metadata endpoint to assist rfcdiff.pyht. Commit ready for merge.
- Legacy-Id: 19396
1 parent 58fa321 commit 036966d

File tree

3 files changed

+264
-5
lines changed

3 files changed

+264
-5
lines changed

ietf/doc/tests.py

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from ietf.person.models import Person
4949
from ietf.person.factories import PersonFactory, EmailFactory
5050
from ietf.utils.mail import outbox
51-
from ietf.utils.test_utils import login_testing_unauthorized, unicontent
51+
from ietf.utils.test_utils import login_testing_unauthorized, unicontent, reload_db_objects
5252
from ietf.utils.test_utils import TestCase
5353
from ietf.utils.text import normalize_text
5454

@@ -2502,3 +2502,123 @@ def test_idnits2_state(self):
25022502
r = self.client.get(url)
25032503
self.assertEqual(r.status_code, 200)
25042504
self.assertContains(r,'Proposed')
2505+
2506+
class RfcdiffSupportTests(TestCase):
2507+
2508+
def setUp(self):
2509+
self.target_view = 'ietf.doc.views_doc.rfcdiff_latest_json'
2510+
2511+
def getJson(self, view_args):
2512+
url = urlreverse(self.target_view, kwargs=view_args)
2513+
r = self.client.get(url)
2514+
self.assertEqual(r.status_code, 200)
2515+
return r.json()
2516+
2517+
def test_draft(self):
2518+
draft = IndividualDraftFactory(name='draft-somebody-did-something',rev='00')
2519+
for r in range(0,13):
2520+
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
2521+
draft.rev = f'{r:02d}'
2522+
draft.save_with_history([e])
2523+
draft = reload_db_objects(draft)
2524+
2525+
received = self.getJson(dict(name=draft.name))
2526+
self.assertEqual(received, dict(
2527+
name=draft.name,
2528+
rev=draft.rev,
2529+
content_url=draft.get_href(),
2530+
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
2531+
))
2532+
2533+
received = self.getJson(dict(name=draft.name, rev=draft.rev))
2534+
self.assertEqual(received, dict(
2535+
name=draft.name,
2536+
rev=draft.rev,
2537+
content_url=draft.get_href(),
2538+
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
2539+
))
2540+
2541+
received = self.getJson(dict(name=draft.name, rev='10'))
2542+
self.assertEqual(received, dict(
2543+
name=draft.name,
2544+
rev='10',
2545+
content_url=draft.history_set.get(rev='10').get_href(),
2546+
previous=f'{draft.name}-09'
2547+
))
2548+
2549+
received = self.getJson(dict(name=draft.name, rev='00'))
2550+
self.assertNotIn('previous', received)
2551+
2552+
replaced = IndividualDraftFactory()
2553+
RelatedDocument.objects.create(relationship_id='replaces',source=draft,target=replaced.docalias.first())
2554+
received = self.getJson(dict(name=draft.name, rev='00'))
2555+
self.assertEqual(received['previous'], f'{replaced.name}-{replaced.rev}')
2556+
2557+
2558+
def test_draft_with_broken_history(self):
2559+
draft = IndividualDraftFactory(rev='10')
2560+
received = self.getJson(dict(name=draft.name,rev='09'))
2561+
self.assertEqual(received['rev'],'09')
2562+
self.assertEqual(received['previous'], f'{draft.name}-09')
2563+
self.assertTrue('warning' in received)
2564+
2565+
2566+
def test_draftname_with_numeric_suffix(self):
2567+
draft = IndividualDraftFactory(name='draft-someone-did-something-01-02',rev='00')
2568+
for r in range(0,4):
2569+
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
2570+
draft.rev = f'{r:02d}'
2571+
draft.save_with_history([e])
2572+
2573+
received = self.getJson(dict(name=draft.name))
2574+
self.assertEqual(received['rev'],'03')
2575+
self.assertIn('01-02-03',received['content_url'])
2576+
self.assertIn('01-02-02',received['previous'])
2577+
2578+
received = self.getJson(dict(name=draft.name,rev='02'))
2579+
self.assertEqual(received['rev'],'02')
2580+
self.assertIn('01-02-02',received['content_url'])
2581+
2582+
def test_rfc(self):
2583+
draft = WgDraftFactory()
2584+
for r in range(0,2):
2585+
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
2586+
draft.rev = f'{r:02d}'
2587+
draft.save_with_history([e])
2588+
2589+
draft.docalias.create(name='rfc8000')
2590+
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
2591+
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
2592+
draft = reload_db_objects(draft)
2593+
rfc = draft
2594+
2595+
number = rfc.rfc_number()
2596+
received = self.getJson(dict(name=number))
2597+
self.assertEqual(received, dict(
2598+
content_url=rfc.get_href(),
2599+
name=rfc.canonical_name(),
2600+
previous=f'{draft.name}-{draft.rev}',
2601+
))
2602+
2603+
num_received = received
2604+
received = self.getJson(dict(name=rfc.canonical_name()))
2605+
self.assertEqual(num_received, received)
2606+
2607+
def test_rfc_with_tombstone(self):
2608+
draft = WgDraftFactory()
2609+
for r in range(0,2):
2610+
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
2611+
draft.rev = f'{r:02d}'
2612+
draft.save_with_history([e])
2613+
2614+
draft.docalias.create(name='rfc3261') # See views_doc.HAS_TOMBSTONE
2615+
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
2616+
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
2617+
draft = reload_db_objects(draft)
2618+
rfc = draft
2619+
2620+
# Some old rfcs had tombstones that shouldn't be used for comparisons
2621+
received = self.getJson(dict(name=rfc.canonical_name()))
2622+
self.assertTrue(received['previous'].endswith('00'))
2623+
2624+

ietf/doc/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
url(r'^html/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', views_doc.document_html),
7272
url(r'^idnits2-rfcs-obsoleted/?$', views_doc.idnits2_rfcs_obsoleted),
7373
url(r'^idnits2-rfc-status/?$', views_doc.idnits2_rfc_status),
74+
# These two are proof-of-concept of a service that would redirect to the latest version
75+
# url(r'^rfcdiff-latest/%(name)s(?:-%(rev)s)?(\.txt|\.html)?/?$' % settings.URL_REGEXPS, views_doc.rfcdiff_latest),
76+
# url(r'^rfcdiff-latest/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', views_doc.rfcdiff_latest),
77+
url(r'^rfcdiff-latest-json/%(name)s(?:-%(rev)s)?(\.txt|\.html)?/?$' % settings.URL_REGEXPS, views_doc.rfcdiff_latest_json),
78+
url(r'^rfcdiff-latest-json/(?P<name>[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', views_doc.rfcdiff_latest_json),
7479

7580
url(r'^all/?$', views_search.index_all_drafts),
7681
url(r'^active/?$', views_search.index_active_drafts),

ietf/doc/views_doc.py

Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,8 +1772,142 @@ def idnits2_state(request, name, rev=None):
17721772
doc.deststatus="Unknown"
17731773
return render(request, 'doc/idnits2-state.txt', context={'doc':doc}, content_type='text/plain;charset=utf-8')
17741774

1775+
def find_doc_for_rfcdiff(name, rev):
1776+
if name.startswith('rfc0'):
1777+
name = "rfc" + name[3:].lstrip('0')
1778+
if name.startswith('review-') and re.search(r'-\d\d\d\d-\d\d$', name):
1779+
name = "%s-%s" % (name, rev)
1780+
if rev and not name.startswith('charter-') and re.search('[0-9]{1,2}-[0-9]{2}', rev):
1781+
name = "%s-%s" % (name, rev[:-3])
1782+
rev = rev[-2:]
1783+
if re.match("^[0-9]+$", name):
1784+
name = f'rfc{name}'
1785+
if re.match("^[Rr][Ff][Cc] [0-9]+$",name):
1786+
name = f'rfc{name[4:]}'
1787+
1788+
docs = Document.objects.filter(docalias__name=name, type_id='draft')
1789+
if rev and not docs.exists():
1790+
# handle some special cases, like draft-ietf-tsvwg-ieee-802-11
1791+
name = '%s-%s' % (name, rev)
1792+
rev=None
1793+
docs = Document.objects.filter(docalias__name=name, type_id='draft')
17751794

1776-
1777-
1778-
1779-
1795+
condition = 'no such document'
1796+
if not docs.exists() or docs.count() > 1:
1797+
return (condition, None, None)
1798+
doc = docs.get()
1799+
if not rev or (rev and doc.rev==rev):
1800+
condition = 'current version'
1801+
return condition, doc, None
1802+
else:
1803+
candidate = None
1804+
for h in doc.history_set.order_by("-time"):
1805+
if rev == h.rev:
1806+
candidate = h
1807+
break
1808+
if candidate:
1809+
condition = 'historic version'
1810+
return condition, doc, candidate
1811+
else:
1812+
condition = 'version dochistory not found'
1813+
return condition, doc, None
1814+
1815+
# This is a proof of concept of a service that would redirect to the current revision
1816+
# def rfcdiff_latest(request, name, rev=None):
1817+
# condition, doc, history = find_doc_for_rfcdiff(name, rev)
1818+
# if not doc:
1819+
# raise Http404
1820+
# if history:
1821+
# return redirect(history.get_href())
1822+
# else:
1823+
# return redirect(doc.get_href())
1824+
1825+
HAS_TOMBSTONE = [
1826+
2821, 2822, 2873, 2919, 2961, 3023, 3029, 3031, 3032, 3033, 3034, 3035, 3036,
1827+
3037, 3038, 3042, 3044, 3050, 3052, 3054, 3055, 3056, 3057, 3059, 3060, 3061,
1828+
3062, 3063, 3064, 3067, 3068, 3069, 3070, 3071, 3072, 3073, 3074, 3075, 3076,
1829+
3077, 3078, 3080, 3081, 3082, 3084, 3085, 3086, 3087, 3088, 3089, 3090, 3094,
1830+
3095, 3096, 3097, 3098, 3101, 3102, 3103, 3104, 3105, 3106, 3107, 3108, 3109,
1831+
3110, 3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, 3121, 3123,
1832+
3124, 3126, 3127, 3128, 3130, 3131, 3132, 3133, 3134, 3135, 3136, 3137, 3138,
1833+
3139, 3140, 3141, 3142, 3143, 3144, 3145, 3147, 3149, 3150, 3151, 3152, 3153,
1834+
3154, 3155, 3156, 3157, 3158, 3159, 3160, 3161, 3162, 3163, 3164, 3165, 3166,
1835+
3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 3176, 3179, 3180, 3181, 3182,
1836+
3183, 3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, 3192, 3193, 3194, 3197,
1837+
3198, 3201, 3202, 3203, 3204, 3205, 3206, 3207, 3208, 3209, 3210, 3211, 3212,
1838+
3213, 3214, 3215, 3216, 3217, 3218, 3220, 3221, 3222, 3224, 3225, 3226, 3227,
1839+
3228, 3229, 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3240, 3241,
1840+
3242, 3243, 3244, 3245, 3246, 3247, 3248, 3249, 3250, 3253, 3254, 3255, 3256,
1841+
3257, 3258, 3259, 3260, 3261, 3262, 3263, 3264, 3265, 3266, 3267, 3268, 3269,
1842+
3270, 3271, 3272, 3273, 3274, 3275, 3276, 3278, 3279, 3280, 3281, 3282, 3283,
1843+
3284, 3285, 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, 3294, 3295, 3296,
1844+
3297, 3298, 3301, 3302, 3303, 3304, 3305, 3307, 3308, 3309, 3310, 3311, 3312,
1845+
3313, 3315, 3317, 3318, 3319, 3320, 3321, 3322, 3323, 3324, 3325, 3326, 3327,
1846+
3329, 3330, 3331, 3332, 3334, 3335, 3336, 3338, 3340, 3341, 3342, 3343, 3346,
1847+
3348, 3349, 3351, 3352, 3353, 3354, 3355, 3356, 3360, 3361, 3362, 3363, 3364,
1848+
3366, 3367, 3368, 3369, 3370, 3371, 3372, 3374, 3375, 3377, 3378, 3379, 3383,
1849+
3384, 3385, 3386, 3387, 3388, 3389, 3390, 3391, 3394, 3395, 3396, 3397, 3398,
1850+
3401, 3402, 3403, 3404, 3405, 3406, 3407, 3408, 3409, 3410, 3411, 3412, 3413,
1851+
3414, 3415, 3416, 3417, 3418, 3419, 3420, 3421, 3422, 3423, 3424, 3425, 3426,
1852+
3427, 3428, 3429, 3430, 3431, 3433, 3434, 3435, 3436, 3437, 3438, 3439, 3440,
1853+
3441, 3443, 3444, 3445, 3446, 3447, 3448, 3449, 3450, 3451, 3452, 3453, 3454,
1854+
3455, 3458, 3459, 3460, 3461, 3462, 3463, 3464, 3465, 3466, 3467, 3468, 3469,
1855+
3470, 3471, 3472, 3473, 3474, 3475, 3476, 3477, 3480, 3481, 3483, 3485, 3488,
1856+
3494, 3495, 3496, 3497, 3498, 3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508,
1857+
3509, 3511, 3512, 3515, 3516, 3517, 3518, 3520, 3521, 3522, 3523, 3524, 3525,
1858+
3527, 3529, 3530, 3532, 3533, 3534, 3536, 3537, 3538, 3539, 3541, 3543, 3544,
1859+
3545, 3546, 3547, 3548, 3549, 3550, 3551, 3552, 3555, 3556, 3557, 3558, 3559,
1860+
3560, 3562, 3563, 3564, 3565, 3568, 3569, 3570, 3571, 3572, 3573, 3574, 3575,
1861+
3576, 3577, 3578, 3579, 3580, 3581, 3582, 3583, 3584, 3588, 3589, 3590, 3591,
1862+
3592, 3593, 3594, 3595, 3597, 3598, 3601, 3607, 3609, 3610, 3612, 3614, 3615,
1863+
3616, 3625, 3627, 3630, 3635, 3636, 3637, 3638
1864+
]
1865+
1866+
def rfcdiff_latest_json(request, name, rev=None):
1867+
response = dict()
1868+
condition, document, history = find_doc_for_rfcdiff(name, rev)
1869+
1870+
if condition == 'no such document':
1871+
raise Http404
1872+
elif condition in ('historic version', 'current version'):
1873+
doc = history if history else document
1874+
if not rev and doc.is_rfc():
1875+
response['content_url'] = doc.get_href()
1876+
response['name']=doc.canonical_name()
1877+
if doc.name != doc.canonical_name():
1878+
prev_rev = doc.rev
1879+
if int(doc.rfc_number()) in HAS_TOMBSTONE and prev_rev != '00':
1880+
prev_rev = f'{(int(doc.rev)-1):02d}'
1881+
response['previous'] = f'{doc.name}-{prev_rev}'
1882+
else:
1883+
doc.is_rfc = lambda: False
1884+
response['content_url'] = doc.get_href()
1885+
response['rev'] = doc.rev
1886+
response['name'] = doc.name
1887+
if doc.rev == '00':
1888+
replaces_docs = (history.doc if condition=='historic version' else doc).related_that_doc('replaces')
1889+
if replaces_docs:
1890+
replaces = replaces_docs[0].document
1891+
response['previous'] = f'{replaces.name}-{replaces.rev}'
1892+
else:
1893+
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
1894+
if match and match.group(2):
1895+
response['previous'] = f'rfc{match.group(2)}'
1896+
else:
1897+
response['previous'] = f'{doc.name}-{(int(doc.rev)-1):02d}'
1898+
elif condition == 'version dochistory not found':
1899+
response['warning'] = 'History for this version not found - these results are speculation'
1900+
response['name'] = document.name
1901+
response['rev'] = rev
1902+
document.rev = rev
1903+
document.is_rfc = lambda: False
1904+
response['content_url'] = document.get_href()
1905+
if int(rev)>0:
1906+
response['previous'] = f'{document.name}-{(int(rev)-1):02d}'
1907+
else:
1908+
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
1909+
if match and match.group(2):
1910+
response['previous'] = f'rfc{match.group(2)}'
1911+
if not response:
1912+
raise Http404
1913+
return HttpResponse(json.dumps(response), content_type='application/json')

0 commit comments

Comments
 (0)