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
This commit is contained in:
Henrik Levkowetz 2015-03-03 20:23:36 +00:00
parent 198c16b9aa
commit 86997e1e95
23 changed files with 168 additions and 25 deletions

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python
import os, sys, re, datetime, argparse, traceback, tempfile
import os, sys, re, datetime, argparse, traceback, tempfile, json
# args
parser = argparse.ArgumentParser(
@ -51,6 +51,7 @@ if args.url_file:
if not initial_urls:
initial_urls.append("/")
initial_urls.append("/api/v1")
visited = set()
urls = {} # url -> referrer
@ -78,8 +79,30 @@ def extract_html_urls(content):
yield url
def extract_tastypie_urls(content):
VISIT_OBJECTS = False
VISIT_NEXT = False
data = json.loads(content)
for item in data:
if type(data[item]) is dict:
if "list_endpoint" in data[item]:
uri = data[item]["list_endpoint"]
yield uri
if VISIT_NEXT:
if "meta" in data and "next" in data["meta"]:
uri = data["meta"]["next"]
if uri != None:
yield uri
if VISIT_OBJECTS:
if "objects" in data:
object_list = data["objects"]
for i in range(len(object_list)):
if "resource_uri" in object_list[i]:
uri = object_list[i]["resource_uri"]
yield uri
django.setup()
client = django.test.Client()
client = django.test.Client(Accept='text/html,text/plain,application/json')
for url in initial_urls:
urls[url] = "[initial]"
@ -150,8 +173,19 @@ while urls:
log("=============")
log(traceback.format_exc())
log("=============")
elif ctype == "application/json":
try:
for u in extract_tastypie_urls(r.content):
if u not in visited and u not in urls:
urls[u] = url
referrers[u] = url
except:
log("error extracting urls from %s" % url)
log("=============")
log(traceback.format_exc())
log("=============")
else:
tags.append(u"FAIL (from %s)" % referrer)
tags.append(u"FAIL for %s\n (from %s)" % (url, referrer))
errors += 1
if elapsed.total_seconds() > slow_threshold:
@ -164,9 +198,9 @@ while urls:
sec = acc_secs % 60
if (len(visited) % 100) == 1:
log("\nElapsed Visited Queue Code Time Url ... Notes")
log("\nElapsed Visited Queue Code Time Url ... Notes")
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)))
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)))
logfile.close()
sys.stderr.write("Output written to %s\n\n" % logfile.name)

62
ietf/api/tests.py Normal file
View file

@ -0,0 +1,62 @@
import os
import sys
import json
from django.test import Client
from django.conf import settings
from django.utils.importlib import import_module
from django.db import models
from tastypie.test import ResourceTestCase
import debug # pyflakes:ignore
OMITTED_APPS = (
'ietf.secr.meetings',
'ietf.secr.proceedings',
'ietf.ipr',
)
class TastypieApiTestCase(ResourceTestCase):
def __init__(self, *args, **kwargs):
self.apps = {}
for app_name in settings.INSTALLED_APPS:
if app_name.startswith('ietf') and not app_name in OMITTED_APPS:
app = import_module(app_name)
name = app_name.split('.',1)[-1]
models_path = os.path.join(os.path.dirname(app.__file__), "models.py")
if os.path.exists(models_path):
self.apps[name] = app
super(ResourceTestCase, self).__init__(*args, **kwargs)
def test_api_top_level(self):
client = Client(Accept='application/json')
r = client.get("/api/v1/")
self.assertValidJSONResponse(r)
resource_list = json.loads(r.content)
for name in self.apps:
if not name in self.apps:
sys.stderr.write("Expected a REST API resource for %s, but didn't find one\n" % name)
for name in self.apps:
self.assertIn(name, resource_list,
"Expected a REST API resource for %s, but didn't find one" % name)
def test_all_model_resources_exist(self):
client = Client(Accept='application/json')
r = client.get("/api/v1")
top = json.loads(r.content)
for name in self.apps:
app = self.apps[name]
self.assertEqual("/api/v1/%s/"%name, top[name]["list_endpoint"])
r = client.get(top[name]["list_endpoint"])
self.assertValidJSONResponse(r)
app_resources = json.loads(r.content)
model_list = models.get_models(app.models)
for model in model_list:
if not model._meta.model_name in app_resources.keys():
#print("There doesn't seem to be any resource for model %s.models.%s"%(app.__name__,model.__name__,))
self.assertIn(model._meta.model_name, app_resources.keys(),
"There doesn't seem to be any API resource for model %s.models.%s"%(app.__name__,model.__name__,))

View file

View file

@ -1 +0,0 @@

View file

@ -1,2 +0,0 @@
# Copyright The IETF Trust 2007, All Rights Reserved

View file

@ -5,8 +5,8 @@ from tastypie.constants import ALL, ALL_WITH_RELATIONS
from ietf import api
from ietf.meeting.models import * # pyflakes:ignore
from ietf.meeting.models import ( Meeting, ResourceAssociation, Constraint, Room, Schedule, Session,
TimeSlot, ScheduledSession, SessionPresentation )
from ietf.name.resources import MeetingTypeNameResource
class MeetingResource(ModelResource):
@ -38,7 +38,7 @@ class ResourceAssociationResource(ModelResource):
name = ToOneField(RoomResourceNameResource, 'name')
class Meta:
queryset = ResourceAssociation.objects.all()
#resource_name = 'resourceassociation'
resource_name = 'resourceassociation'
filtering = {
"id": ALL,
"icon": ALL,
@ -144,7 +144,7 @@ class TimeSlotResource(ModelResource):
type = ToOneField(TimeSlotTypeNameResource, 'type')
location = ToOneField(RoomResource, 'location', null=True)
sessions = ToManyField(SessionResource, 'sessions', null=True)
duration = TimedeltaField()
duration = api.TimedeltaField()
class Meta:
queryset = TimeSlot.objects.all()
#resource_name = 'timeslot'
@ -183,3 +183,20 @@ class ScheduledSessionResource(ModelResource):
}
api.meeting.register(ScheduledSessionResource())
from ietf.doc.resources import DocumentResource
class SessionPresentationResource(ModelResource):
session = ToOneField(SessionResource, 'session')
document = ToOneField(DocumentResource, 'document')
class Meta:
queryset = SessionPresentation.objects.all()
#resource_name = 'sessionpresentation'
filtering = {
"id": ALL,
"rev": ALL,
"session": ALL_WITH_RELATIONS,
"document": ALL_WITH_RELATIONS,
}
api.meeting.register(SessionPresentationResource())

View file

@ -223,7 +223,7 @@ api.name.register(DocRelationshipNameResource())
class RoomResourceNameResource(ModelResource):
class Meta:
queryset = RoomResourceName.objects.all()
#resource_name = 'roomresourcename'
resource_name = 'roomresourcename' # Needed because tastypie otherwise removes 'resource' from the name
filtering = {
"slug": ALL,
"name": ALL,
@ -299,3 +299,44 @@ class NomineePositionStateNameResource(ModelResource):
}
api.name.register(NomineePositionStateNameResource())
class IprDisclosureStateNameResource(ModelResource):
class Meta:
queryset = IprDisclosureStateName.objects.all()
#resource_name = 'iprdisclosurestatename'
filtering = {
"slug": ALL,
"name": ALL,
"desc": ALL,
"used": ALL,
"order": ALL,
}
api.name.register(IprDisclosureStateNameResource())
class IprEventTypeNameResource(ModelResource):
class Meta:
queryset = IprEventTypeName.objects.all()
#resource_name = 'ipreventtypename'
filtering = {
"slug": ALL,
"name": ALL,
"desc": ALL,
"used": ALL,
"order": ALL,
}
api.name.register(IprEventTypeNameResource())
class IprLicenseTypeNameResource(ModelResource):
class Meta:
queryset = IprLicenseTypeName.objects.all()
#resource_name = 'iprlicensetypename'
filtering = {
"slug": ALL,
"name": ALL,
"desc": ALL,
"used": ALL,
"order": ALL,
}
api.name.register(IprLicenseTypeNameResource())

View file

@ -1 +0,0 @@
# This app has no models

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -201,6 +201,7 @@ INSTALLED_APPS = (
'django.contrib.humanize',
'django.contrib.messages',
'tastypie',
'ietf.api',
'ietf.person',
'ietf.name',
'ietf.group',
@ -270,10 +271,10 @@ TEST_BLUESHEET_DIR = "tmp-bluesheet-dir"
TEST_CODE_COVERAGE_EXCLUDE = [
"*/tests*",
"*/0*",
"*/admin.py",
"*/migrations/*",
"*/test_runner.py"
"ietf/settings*",
"ietf/utils/test_runner.py",
]
TEST_CODE_COVERAGE_MASTER_FILE = "coverage-master.json"
TEST_CODE_COVERAGE_LATEST_FILE = "coverage-latest.json"

View file

View file

View file

@ -371,7 +371,7 @@ class Serializer(object):
options = options or {}
data = self.to_simple(data, options)
return djangojson.json.dumps(data, cls=djangojson.DjangoJSONEncoder, sort_keys=True, ensure_ascii=False)
return djangojson.json.dumps(data, cls=djangojson.DjangoJSONEncoder, sort_keys=True, ensure_ascii=False, indent=2)
def from_json(self, content):
"""