Skip to content

Commit 86997e1

Browse files
committed
Turned the api.py file into a module. Moved the makeresources management command to the api module. Added some api tests. Added crawling of api files to the test-crawler. Adjusted some resource files discovered by the test suite and test-crawler. Removed a bunch of empty model files.
- Legacy-Id: 9144
1 parent 198c16b commit 86997e1

23 files changed

Lines changed: 168 additions & 25 deletions

File tree

bin/test-crawl

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22

3-
import os, sys, re, datetime, argparse, traceback, tempfile
3+
import os, sys, re, datetime, argparse, traceback, tempfile, json
44

55
# args
66
parser = argparse.ArgumentParser(
@@ -51,6 +51,7 @@ if args.url_file:
5151

5252
if not initial_urls:
5353
initial_urls.append("/")
54+
initial_urls.append("/api/v1")
5455

5556
visited = set()
5657
urls = {} # url -> referrer
@@ -78,8 +79,30 @@ def extract_html_urls(content):
7879

7980
yield url
8081

82+
def extract_tastypie_urls(content):
83+
VISIT_OBJECTS = False
84+
VISIT_NEXT = False
85+
data = json.loads(content)
86+
for item in data:
87+
if type(data[item]) is dict:
88+
if "list_endpoint" in data[item]:
89+
uri = data[item]["list_endpoint"]
90+
yield uri
91+
if VISIT_NEXT:
92+
if "meta" in data and "next" in data["meta"]:
93+
uri = data["meta"]["next"]
94+
if uri != None:
95+
yield uri
96+
if VISIT_OBJECTS:
97+
if "objects" in data:
98+
object_list = data["objects"]
99+
for i in range(len(object_list)):
100+
if "resource_uri" in object_list[i]:
101+
uri = object_list[i]["resource_uri"]
102+
yield uri
103+
81104
django.setup()
82-
client = django.test.Client()
105+
client = django.test.Client(Accept='text/html,text/plain,application/json')
83106

84107
for url in initial_urls:
85108
urls[url] = "[initial]"
@@ -150,8 +173,19 @@ while urls:
150173
log("=============")
151174
log(traceback.format_exc())
152175
log("=============")
176+
elif ctype == "application/json":
177+
try:
178+
for u in extract_tastypie_urls(r.content):
179+
if u not in visited and u not in urls:
180+
urls[u] = url
181+
referrers[u] = url
182+
except:
183+
log("error extracting urls from %s" % url)
184+
log("=============")
185+
log(traceback.format_exc())
186+
log("=============")
153187
else:
154-
tags.append(u"FAIL (from %s)" % referrer)
188+
tags.append(u"FAIL for %s\n (from %s)" % (url, referrer))
155189
errors += 1
156190

157191
if elapsed.total_seconds() > slow_threshold:
@@ -164,9 +198,9 @@ while urls:
164198
sec = acc_secs % 60
165199

166200
if (len(visited) % 100) == 1:
167-
log("\nElapsed Visited Queue Code Time Url ... Notes")
201+
log("\nElapsed Visited Queue Code Time Url ... Notes")
168202

169-
log("%2d:%02d:%02d %7d %6d %s %.3fs %s %s" % (hrs,min,sec, len(visited), len(urls), r.status_code, elapsed.total_seconds(), url, " ".join(tags)))
203+
log("%2d:%02d:%02d %7d %6d %s %6.3fs %s %s" % (hrs,min,sec, len(visited), len(urls), r.status_code, elapsed.total_seconds(), url, " ".join(tags)))
170204

171205
logfile.close()
172206
sys.stderr.write("Output written to %s\n\n" % logfile.name)
File renamed without changes.
File renamed without changes.

ietf/api/tests.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import os
2+
import sys
3+
import json
4+
5+
from django.test import Client
6+
from django.conf import settings
7+
from django.utils.importlib import import_module
8+
from django.db import models
9+
10+
from tastypie.test import ResourceTestCase
11+
12+
import debug # pyflakes:ignore
13+
14+
OMITTED_APPS = (
15+
'ietf.secr.meetings',
16+
'ietf.secr.proceedings',
17+
'ietf.ipr',
18+
)
19+
20+
class TastypieApiTestCase(ResourceTestCase):
21+
def __init__(self, *args, **kwargs):
22+
self.apps = {}
23+
for app_name in settings.INSTALLED_APPS:
24+
if app_name.startswith('ietf') and not app_name in OMITTED_APPS:
25+
app = import_module(app_name)
26+
name = app_name.split('.',1)[-1]
27+
models_path = os.path.join(os.path.dirname(app.__file__), "models.py")
28+
if os.path.exists(models_path):
29+
self.apps[name] = app
30+
super(ResourceTestCase, self).__init__(*args, **kwargs)
31+
32+
def test_api_top_level(self):
33+
client = Client(Accept='application/json')
34+
r = client.get("/api/v1/")
35+
self.assertValidJSONResponse(r)
36+
resource_list = json.loads(r.content)
37+
38+
for name in self.apps:
39+
if not name in self.apps:
40+
sys.stderr.write("Expected a REST API resource for %s, but didn't find one\n" % name)
41+
42+
for name in self.apps:
43+
self.assertIn(name, resource_list,
44+
"Expected a REST API resource for %s, but didn't find one" % name)
45+
46+
def test_all_model_resources_exist(self):
47+
client = Client(Accept='application/json')
48+
r = client.get("/api/v1")
49+
top = json.loads(r.content)
50+
for name in self.apps:
51+
app = self.apps[name]
52+
self.assertEqual("/api/v1/%s/"%name, top[name]["list_endpoint"])
53+
r = client.get(top[name]["list_endpoint"])
54+
self.assertValidJSONResponse(r)
55+
app_resources = json.loads(r.content)
56+
model_list = models.get_models(app.models)
57+
for model in model_list:
58+
if not model._meta.model_name in app_resources.keys():
59+
#print("There doesn't seem to be any resource for model %s.models.%s"%(app.__name__,model.__name__,))
60+
self.assertIn(model._meta.model_name, app_resources.keys(),
61+
"There doesn't seem to be any API resource for model %s.models.%s"%(app.__name__,model.__name__,))
62+

ietf/idindex/models.py

Whitespace-only changes.

ietf/ietfauth/models.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

ietf/mailinglists/models.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

ietf/meeting/resources.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
from ietf import api
77

8-
from ietf.meeting.models import * # pyflakes:ignore
9-
8+
from ietf.meeting.models import ( Meeting, ResourceAssociation, Constraint, Room, Schedule, Session,
9+
TimeSlot, ScheduledSession, SessionPresentation )
1010

1111
from ietf.name.resources import MeetingTypeNameResource
1212
class MeetingResource(ModelResource):
@@ -38,7 +38,7 @@ class ResourceAssociationResource(ModelResource):
3838
name = ToOneField(RoomResourceNameResource, 'name')
3939
class Meta:
4040
queryset = ResourceAssociation.objects.all()
41-
#resource_name = 'resourceassociation'
41+
resource_name = 'resourceassociation'
4242
filtering = {
4343
"id": ALL,
4444
"icon": ALL,
@@ -144,7 +144,7 @@ class TimeSlotResource(ModelResource):
144144
type = ToOneField(TimeSlotTypeNameResource, 'type')
145145
location = ToOneField(RoomResource, 'location', null=True)
146146
sessions = ToManyField(SessionResource, 'sessions', null=True)
147-
duration = TimedeltaField()
147+
duration = api.TimedeltaField()
148148
class Meta:
149149
queryset = TimeSlot.objects.all()
150150
#resource_name = 'timeslot'
@@ -183,3 +183,20 @@ class Meta:
183183
}
184184
api.meeting.register(ScheduledSessionResource())
185185

186+
187+
188+
from ietf.doc.resources import DocumentResource
189+
class SessionPresentationResource(ModelResource):
190+
session = ToOneField(SessionResource, 'session')
191+
document = ToOneField(DocumentResource, 'document')
192+
class Meta:
193+
queryset = SessionPresentation.objects.all()
194+
#resource_name = 'sessionpresentation'
195+
filtering = {
196+
"id": ALL,
197+
"rev": ALL,
198+
"session": ALL_WITH_RELATIONS,
199+
"document": ALL_WITH_RELATIONS,
200+
}
201+
api.meeting.register(SessionPresentationResource())
202+

ietf/name/resources.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ class Meta:
223223
class RoomResourceNameResource(ModelResource):
224224
class Meta:
225225
queryset = RoomResourceName.objects.all()
226-
#resource_name = 'roomresourcename'
226+
resource_name = 'roomresourcename' # Needed because tastypie otherwise removes 'resource' from the name
227227
filtering = {
228228
"slug": ALL,
229229
"name": ALL,
@@ -299,3 +299,44 @@ class Meta:
299299
}
300300
api.name.register(NomineePositionStateNameResource())
301301

302+
303+
304+
class IprDisclosureStateNameResource(ModelResource):
305+
class Meta:
306+
queryset = IprDisclosureStateName.objects.all()
307+
#resource_name = 'iprdisclosurestatename'
308+
filtering = {
309+
"slug": ALL,
310+
"name": ALL,
311+
"desc": ALL,
312+
"used": ALL,
313+
"order": ALL,
314+
}
315+
api.name.register(IprDisclosureStateNameResource())
316+
317+
class IprEventTypeNameResource(ModelResource):
318+
class Meta:
319+
queryset = IprEventTypeName.objects.all()
320+
#resource_name = 'ipreventtypename'
321+
filtering = {
322+
"slug": ALL,
323+
"name": ALL,
324+
"desc": ALL,
325+
"used": ALL,
326+
"order": ALL,
327+
}
328+
api.name.register(IprEventTypeNameResource())
329+
330+
class IprLicenseTypeNameResource(ModelResource):
331+
class Meta:
332+
queryset = IprLicenseTypeName.objects.all()
333+
#resource_name = 'iprlicensetypename'
334+
filtering = {
335+
"slug": ALL,
336+
"name": ALL,
337+
"desc": ALL,
338+
"used": ALL,
339+
"order": ALL,
340+
}
341+
api.name.register(IprLicenseTypeNameResource())
342+

ietf/release/models.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)