diff --git a/dev/deploy-to-container/settings_local.py b/dev/deploy-to-container/settings_local.py index 60981ba567e..2b0eb6a1f1b 100644 --- a/dev/deploy-to-container/settings_local.py +++ b/dev/deploy-to-container/settings_local.py @@ -68,3 +68,7 @@ SLIDE_STAGING_PATH = '/test/staging/' DE_GFM_BINARY = '/usr/local/bin/de-gfm' + +APP_API_TOKENS = { + "ietf.api.views_rpc" : ["devtoken"], +} diff --git a/docker/configs/settings_local.py b/docker/configs/settings_local.py index 766e39d1a90..1e78b9472c0 100644 --- a/docker/configs/settings_local.py +++ b/docker/configs/settings_local.py @@ -60,8 +60,5 @@ STATIC_IETF_ORG_INTERNAL = "http://static" APP_API_TOKENS = { - "ietf.api.views.rpc_person" : ["devtoken"], - "ietf.api.views.rpc_draft" : ["devtoken"], - "ietf.api.views.submitted_to_rpc" : ["devtoken"], - "ietf.api.views.create_demo_resources" : ["devtoken"], # remove for production! + "ietf.api.views_rpc" : ["devtoken"], } diff --git a/ietf/api/urls.py b/ietf/api/urls.py index 545842266bd..0adc88b9f98 100644 --- a/ietf/api/urls.py +++ b/ietf/api/urls.py @@ -60,14 +60,7 @@ url(r'^rfcdiff-latest-json/(?P[Rr][Ff][Cc] [0-9]+?)(\.txt|\.html)?/?$', api_views.rfcdiff_latest_json), # direct authentication url(r'^directauth/?$', api_views.directauth), - url(r'^rpc/person/(?P[0-9]+)$', api_views.rpc_person), - url(r'^rpc/persons/$', api_views.rpc_persons), - url(r'^rpc/doc/submitted_to_rpc/$', api_views.submitted_to_rpc), - url(r'^rpc/doc/drafts/(?P[0-9]+)$', api_views.rpc_draft), - url(r'^rpc/person/create_demo_person/$', api_views.create_demo_person), - url(r'^rpc/doc/create_demo_draft/$', api_views.create_demo_draft), - - + url(r'^rpc/', include('ietf.api.urls_rpc')), ] # Additional (standard) Tastypie endpoints diff --git a/ietf/api/urls_rpc.py b/ietf/api/urls_rpc.py new file mode 100644 index 00000000000..0e3b176710a --- /dev/null +++ b/ietf/api/urls_rpc.py @@ -0,0 +1,14 @@ +# Copyright The IETF Trust 2023, All Rights Reserved + +from ietf.api import views_rpc + +from ietf.utils.urls import url + +urlpatterns = [ + url(r'^doc/create_demo_draft/$', views_rpc.create_demo_draft), + url(r'^doc/drafts/(?P[0-9]+)$', views_rpc.rpc_draft), + url(r'^doc/submitted_to_rpc/$', views_rpc.submitted_to_rpc), + url(r'^person/create_demo_person/$', views_rpc.create_demo_person), + url(r'^person/(?P[0-9]+)$', views_rpc.rpc_person), + url(r'^persons/$', views_rpc.rpc_persons), +] diff --git a/ietf/api/views.py b/ietf/api/views.py index 54d75d4acf5..b4c15cc5ebd 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -14,13 +14,9 @@ from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.core.validators import validate_email -from django.db.models import Q from django.http import ( HttpResponse, Http404, - JsonResponse, - HttpResponseNotAllowed, - HttpResponseNotFound, ) from django.shortcuts import render, get_object_or_404 from django.urls import reverse @@ -37,13 +33,10 @@ import debug # pyflakes:ignore import ietf -from ietf.person.factories import PersonFactory # DO NOT MERGE INTO MAIN from ietf.person.models import Person, Email from ietf.api import _api_list from ietf.api.serializer import JsonExportMixin -from ietf.api.ietf_utils import is_valid_token, requires_api_token -from ietf.doc.factories import WgDraftFactory # DO NOT MERGE INTO MAIN -from ietf.doc.models import Document +from ietf.api.ietf_utils import is_valid_token from ietf.doc.utils import fuzzy_find_documents from ietf.ietfauth.views import send_account_creation_email from ietf.ietfauth.utils import role_required @@ -446,112 +439,4 @@ def directauth(request): else: return HttpResponse(status=405) -@csrf_exempt -@requires_api_token -def rpc_person(request, person_id): - person = get_object_or_404(Person, pk=person_id) - return JsonResponse({ - "id": person.id, - "plain_name": person.plain_name(), - }) - -@csrf_exempt -@requires_api_token("ietf.api.views.rpc_person") -def rpc_persons(request): - """ Get a batch of rpc person names""" - if request.method != "POST": - return HttpResponseNotAllowed(["POST"]) - - pks = json.loads(request.body) - response = dict() - for p in Person.objects.filter(pk__in=pks): - response[str(p.pk)] = p.plain_name() - return JsonResponse(response) - -@csrf_exempt -@requires_api_token -def rpc_draft(request, doc_id): - if request.method != "GET": - return HttpResponseNotAllowed(["GET"]) - - try: - d = Document.objects.get(pk=doc_id, type_id="draft") - except Document.DoesNotExist: - return HttpResponseNotFound() - return JsonResponse({ - "id": d.pk, - "name": d.name, - "rev": d.rev, - "stream": d.stream.slug, - "title": d.title, - "pages": d.pages, - "authors": [ - { - "id": p.pk, - "plain_name": p.plain_name(), - } for p in d.documentauthor_set.all() - ] - }) - -@csrf_exempt -@requires_api_token -def submitted_to_rpc(request): - """ Return documents in datatracker that have been submitted to the RPC but are not yet in the queue - - Those queries overreturn - there may be things, particularly not from the IETF stream that are already in the queue. - """ - ietf_docs = Q(states__type_id="draft-iesg",states__slug__in=["ann"]) - irtf_iab_ise_docs = Q(states__type_id__in=["draft-stream-iab","draft-stream-irtf","draft-stream-ise"],states__slug__in=["rfc-edit"]) - #TODO: Need a way to talk about editorial stream docs - docs = Document.objects.filter(type_id="draft").filter(ietf_docs|irtf_iab_ise_docs) - response = {"submitted_to_rpc": []} - for doc in docs: - response["submitted_to_rpc"].append({"name":doc.name, "pk": doc.pk, "stream": doc.stream_id, "submitted": f"{doc.sent_to_rfc_editor_event().time:%Y-%m-%d}"}) #TODO reconcile timezone - return JsonResponse(response) - - -@csrf_exempt -@requires_api_token("ietf.api.views.create_demo_resources") -def create_demo_person(request): - """ Helper for creating rpc demo objects - SHOULD NOT MAKE IT INTO PRODUCTION - - """ - if request.method != "POST": - return HttpResponseNotAllowed(["POST"]) - - request_params = json.loads(request.body) - name = request_params["name"] - person = Person.objects.filter(name=name).first() or PersonFactory(name=name) - return JsonResponse({"user_id":person.user.pk,"person_pk":person.pk}) - - -@csrf_exempt -@requires_api_token("ietf.api.views.create_demo_resources") -def create_demo_draft(request): - """ Helper for creating rpc demo objects - SHOULD NOT MAKE IT INTO PRODUCTION - - """ - if request.method != "POST": - return HttpResponseNotAllowed(["POST"]) - - request_params = json.loads(request.body) - name = request_params.get("name") - rev = request_params.get("rev") - states = request_params.get("states") - stream_id = request_params.get("stream_id", "ietf") - doc = None - if not name: - return HttpResponse(status=400, content="Name is required") - doc = Document.objects.filter(name=name).first() - if not doc: - kwargs = {"name": name, "stream_id": stream_id} - if states: - kwargs["states"] = states - if rev: - kwargs["rev"] = rev - doc = WgDraftFactory(**kwargs) # Yes, things may be a little strange if the stream isn't IETF, but until we nned something different... - event_type = "iesg_approved" if stream_id == "ietf" else "requested_publication" - if not doc.docevent_set.filter(type=event_type).exists(): # Not using get_or_create here on purpose - these are wobbly facades we're creating - doc.docevent_set.create(type=event_type, by_id=1, desc="Sent off to the RPC") - return JsonResponse({ "doc_id":doc.pk, "name":doc.name }) diff --git a/ietf/api/views_rpc.py b/ietf/api/views_rpc.py new file mode 100644 index 00000000000..dd17db064a5 --- /dev/null +++ b/ietf/api/views_rpc.py @@ -0,0 +1,129 @@ +# Copyright The IETF Trust 2023, All Rights Reserved + +import json + +from django.db.models import Q +from django.http import ( + HttpResponse, + JsonResponse, + HttpResponseNotAllowed, + HttpResponseNotFound, +) +from django.shortcuts import get_object_or_404 +from django.views.decorators.csrf import csrf_exempt + +from ietf.api.ietf_utils import requires_api_token +from ietf.doc.factories import WgDraftFactory # DO NOT MERGE INTO MAIN +from ietf.doc.models import Document +from ietf.person.factories import PersonFactory # DO NOT MERGE INTO MAIN +from ietf.person.models import Person + +@csrf_exempt +@requires_api_token +def rpc_person(request, person_id): + person = get_object_or_404(Person, pk=person_id) + return JsonResponse({ + "id": person.id, + "plain_name": person.plain_name(), + }) + +@csrf_exempt +@requires_api_token("ietf.api.views_rpc") +def rpc_persons(request): + """ Get a batch of rpc person names""" + if request.method != "POST": + return HttpResponseNotAllowed(["POST"]) + + pks = json.loads(request.body) + response = dict() + for p in Person.objects.filter(pk__in=pks): + response[str(p.pk)] = p.plain_name() + return JsonResponse(response) + +@csrf_exempt +@requires_api_token("ietf.api.views_rpc") +def rpc_draft(request, doc_id): + if request.method != "GET": + return HttpResponseNotAllowed(["GET"]) + + try: + d = Document.objects.get(pk=doc_id, type_id="draft") + except Document.DoesNotExist: + return HttpResponseNotFound() + return JsonResponse({ + "id": d.pk, + "name": d.name, + "rev": d.rev, + "stream": d.stream.slug, + "title": d.title, + "pages": d.pages, + "authors": [ + { + "id": p.pk, + "plain_name": p.plain_name(), + } for p in d.documentauthor_set.all() + ] + }) + +@csrf_exempt +@requires_api_token("ietf.api.views_rpc") +def submitted_to_rpc(request): + """ Return documents in datatracker that have been submitted to the RPC but are not yet in the queue + + Those queries overreturn - there may be things, particularly not from the IETF stream that are already in the queue. + """ + ietf_docs = Q(states__type_id="draft-iesg",states__slug__in=["ann"]) + irtf_iab_ise_docs = Q(states__type_id__in=["draft-stream-iab","draft-stream-irtf","draft-stream-ise"],states__slug__in=["rfc-edit"]) + #TODO: Need a way to talk about editorial stream docs + docs = Document.objects.filter(type_id="draft").filter(ietf_docs|irtf_iab_ise_docs) + response = {"submitted_to_rpc": []} + for doc in docs: + response["submitted_to_rpc"].append({"name":doc.name, "pk": doc.pk, "stream": doc.stream_id, "submitted": f"{doc.sent_to_rfc_editor_event().time:%Y-%m-%d}"}) #TODO reconcile timezone + + return JsonResponse(response) + + +@csrf_exempt +@requires_api_token("ietf.api.views_rpc") +def create_demo_person(request): + """ Helper for creating rpc demo objects - SHOULD NOT MAKE IT INTO PRODUCTION + + """ + if request.method != "POST": + return HttpResponseNotAllowed(["POST"]) + + request_params = json.loads(request.body) + name = request_params["name"] + person = Person.objects.filter(name=name).first() or PersonFactory(name=name) + return JsonResponse({"user_id":person.user.pk,"person_pk":person.pk}) + + +@csrf_exempt +@requires_api_token("ietf.api.views_rpc") +def create_demo_draft(request): + """ Helper for creating rpc demo objects - SHOULD NOT MAKE IT INTO PRODUCTION + + """ + if request.method != "POST": + return HttpResponseNotAllowed(["POST"]) + + request_params = json.loads(request.body) + name = request_params.get("name") + rev = request_params.get("rev") + states = request_params.get("states") + stream_id = request_params.get("stream_id", "ietf") + doc = None + if not name: + return HttpResponse(status=400, content="Name is required") + doc = Document.objects.filter(name=name).first() + if not doc: + kwargs = {"name": name, "stream_id": stream_id} + if states: + kwargs["states"] = states + if rev: + kwargs["rev"] = rev + doc = WgDraftFactory(**kwargs) # Yes, things may be a little strange if the stream isn't IETF, but until we nned something different... + event_type = "iesg_approved" if stream_id == "ietf" else "requested_publication" + if not doc.docevent_set.filter(type=event_type).exists(): # Not using get_or_create here on purpose - these are wobbly facades we're creating + doc.docevent_set.create(type=event_type, by_id=1, desc="Sent off to the RPC") + return JsonResponse({ "doc_id":doc.pk, "name":doc.name }) diff --git a/rpcapi.yaml b/rpcapi.yaml index e314e4cae85..1f9c0ef9099 100644 --- a/rpcapi.yaml +++ b/rpcapi.yaml @@ -4,7 +4,7 @@ info: description: Datatracker RPC API version: 1.0.0 servers: - - url: 'http://localhost:8000/api/rpc/' + - url: 'http://localhost:8000/api/rpc' paths: /person/{personId}: get: