Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,19 @@ jobs:
PKG_VERSION: ${{needs.prepare.outputs.pkg_version}}

steps:
- name: Refresh Staging DB
uses: the-actions-org/workflow-dispatch@v4
with:
workflow: update-staging-db.yml
repo: ietf-tools/infra-k8s
ref: main
token: ${{ secrets.GH_INFRA_K8S_TOKEN }}
inputs: '{ "sourceDb":"datatracker" }'
wait-for-completion: true
wait-for-completion-timeout: 5m
wait-for-completion-interval: 20s
display-workflow-run-url: false

- name: Deploy to staging
uses: the-actions-org/workflow-dispatch@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion client/agenda/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export const useAgendaStore = defineStore('agenda', {
meetingNumber = meetingData.meetingNumber
}

const resp = await fetch(`/api/meeting/${meetingNumber}/agenda-data`, { credentials: 'omit' })
const resp = await fetch(`/api/meeting/${meetingNumber}/agenda-data`)
if (!resp.ok) {
throw new Error(resp.statusText)
}
Expand Down
2 changes: 1 addition & 1 deletion dev/build/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ghcr.io/ietf-tools/datatracker-app-base:20250402T1611
FROM ghcr.io/ietf-tools/datatracker-app-base:20250421T1600
LABEL maintainer="IETF Tools Team <tools-discuss@ietf.org>"

ENV DEBIAN_FRONTEND=noninteractive
Expand Down
2 changes: 1 addition & 1 deletion dev/build/TARGET_BASE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20250402T1611
20250421T1600
228 changes: 226 additions & 2 deletions ietf/api/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright The IETF Trust 2015-2024, All Rights Reserved
# -*- coding: utf-8 -*-
import base64
import copy
import datetime
import json
import html
Expand Down Expand Up @@ -31,7 +32,7 @@
from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory, WgRfcFactory
from ietf.group.factories import RoleFactory
from ietf.meeting.factories import MeetingFactory, SessionFactory
from ietf.meeting.models import Session
from ietf.meeting.models import Session, Registration
from ietf.nomcom.models import Volunteer
from ietf.nomcom.factories import NomComFactory, nomcom_kwargs_for_year
from ietf.person.factories import PersonFactory, random_faker, EmailFactory, PersonalApiKeyFactory
Expand Down Expand Up @@ -828,6 +829,196 @@ def test_api_new_meeting_registration_nomcom_volunteer(self):
self.assertEqual(volunteer.nomcom, nomcom)
self.assertEqual(volunteer.origin, 'registration')

@override_settings(APP_API_TOKENS={"ietf.api.views.api_new_meeting_registration_v2": ["valid-token"]})
def test_api_new_meeting_registration_v2(self):
meeting = MeetingFactory(type_id='ietf')
person = PersonFactory()
regs = [
{
'affiliation': "Alguma Corporação",
'country_code': 'PT',
'email': person.email().address,
'first_name': person.first_name(),
'last_name': person.last_name(),
'meeting': str(meeting.number),
'reg_type': 'onsite',
'ticket_type': 'week_pass',
'checkedin': False,
'is_nomcom_volunteer': False,
'cancelled': False,
}
]

url = urlreverse('ietf.api.views.api_new_meeting_registration_v2')
#
# Test invalid key
r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "invalid-token"})
self.assertEqual(r.status_code, 403)
#
# Test invalid data
bad_regs = copy.deepcopy(regs)
del(bad_regs[0]['email'])
r = self.client.post(url, data=json.dumps(bad_regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 400)
#
# Test valid POST
r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertContains(r, "Success", status_code=202)
#
# Check record
reg = regs[0]
objects = Registration.objects.filter(email=reg['email'], meeting__number=reg['meeting'])
self.assertEqual(objects.count(), 1)
obj = objects[0]
for key in ['affiliation', 'country_code', 'first_name', 'last_name', 'checkedin']:
self.assertEqual(getattr(obj, key), False if key=='checkedin' else reg.get(key) , "Bad data for field '%s'" % key)
self.assertEqual(obj.tickets.count(), 1)
ticket = obj.tickets.first()
self.assertEqual(ticket.ticket_type.slug, regs[0]['ticket_type'])
self.assertEqual(ticket.attendance_type.slug, regs[0]['reg_type'])
self.assertEqual(obj.person, person)
#
# Test update (switch to remote)
regs = [
{
'affiliation': "Alguma Corporação",
'country_code': 'PT',
'email': person.email().address,
'first_name': person.first_name(),
'last_name': person.last_name(),
'meeting': str(meeting.number),
'reg_type': 'remote',
'ticket_type': 'week_pass',
'checkedin': False,
'is_nomcom_volunteer': False,
'cancelled': False,
}
]
r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertContains(r, "Success", status_code=202)
objects = Registration.objects.filter(email=reg['email'], meeting__number=reg['meeting'])
self.assertEqual(objects.count(), 1)
obj = objects[0]
self.assertEqual(obj.tickets.count(), 1)
ticket = obj.tickets.first()
self.assertEqual(ticket.ticket_type.slug, regs[0]['ticket_type'])
self.assertEqual(ticket.attendance_type.slug, regs[0]['reg_type'])
#
# Test multiple
regs = [
{
'affiliation': "Alguma Corporação",
'country_code': 'PT',
'email': person.email().address,
'first_name': person.first_name(),
'last_name': person.last_name(),
'meeting': str(meeting.number),
'reg_type': 'onsite',
'ticket_type': 'one_day',
'checkedin': False,
'is_nomcom_volunteer': False,
'cancelled': False,
},

{
'affiliation': "Alguma Corporação",
'country_code': 'PT',
'email': person.email().address,
'first_name': person.first_name(),
'last_name': person.last_name(),
'meeting': str(meeting.number),
'reg_type': 'remote',
'ticket_type': 'week_pass',
'checkedin': False,
'is_nomcom_volunteer': False,
'cancelled': False,
}
]

r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertContains(r, "Success", status_code=202)
objects = Registration.objects.filter(email=reg['email'], meeting__number=reg['meeting'])
self.assertEqual(objects.count(), 1)
obj = objects[0]
self.assertEqual(obj.tickets.count(), 2)
self.assertEqual(obj.tickets.filter(attendance_type__slug='onsite').count(), 1)
self.assertEqual(obj.tickets.filter(attendance_type__slug='remote').count(), 1)

@override_settings(APP_API_TOKENS={"ietf.api.views.api_new_meeting_registration_v2": ["valid-token"]})
def test_api_new_meeting_registration_v2_cancelled(self):
meeting = MeetingFactory(type_id='ietf')
person = PersonFactory()
regs = [
{
'affiliation': "Acme",
'country_code': 'US',
'email': person.email().address,
'first_name': person.first_name(),
'last_name': person.last_name(),
'meeting': str(meeting.number),
'reg_type': 'onsite',
'ticket_type': 'week_pass',
'checkedin': False,
'is_nomcom_volunteer': False,
'cancelled': False,
}
]
url = urlreverse('ietf.api.views.api_new_meeting_registration_v2')
self.assertEqual(Registration.objects.count(), 0)
r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertContains(r, "Success", status_code=202)
self.assertEqual(Registration.objects.count(), 1)
regs[0]['cancelled'] = True
r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertContains(r, "Success", status_code=202)
self.assertEqual(Registration.objects.count(), 0)

@override_settings(APP_API_TOKENS={"ietf.api.views.api_new_meeting_registration_v2": ["valid-token"]})
def test_api_new_meeting_registration_v2_nomcom(self):
meeting = MeetingFactory(type_id='ietf')
person = PersonFactory()
regs = [
{
'affiliation': "Acme",
'country_code': 'US',
'email': person.email().address,
'first_name': person.first_name(),
'last_name': person.last_name(),
'meeting': str(meeting.number),
'reg_type': 'onsite',
'ticket_type': 'week_pass',
'checkedin': False,
'is_nomcom_volunteer': False,
'cancelled': False,
}
]

url = urlreverse('ietf.api.views.api_new_meeting_registration_v2')
now = datetime.datetime.now()
if now.month > 10:
year = now.year + 1
else:
year = now.year
# create appropriate group and nomcom objects
nomcom = NomComFactory.create(is_accepting_volunteers=True, **nomcom_kwargs_for_year(year))

# first test is_nomcom_volunteer False
r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertContains(r, "Success", status_code=202)
# assert no Volunteers exists
self.assertEqual(Volunteer.objects.count(), 0)

# test is_nomcom_volunteer True
regs[0]['is_nomcom_volunteer'] = True
r = self.client.post(url, data=json.dumps(regs), content_type='application/json', headers={"X-Api-Key": "valid-token"})
self.assertContains(r, "Success", status_code=202)
# assert Volunteer exists
self.assertEqual(Volunteer.objects.count(), 1)
volunteer = Volunteer.objects.last()
self.assertEqual(volunteer.person, person)
self.assertEqual(volunteer.nomcom, nomcom)
self.assertEqual(volunteer.origin, 'registration')

def test_api_version(self):
DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=datetime.timezone.utc), host='testapi.example.com',tz='UTC')
url = urlreverse('ietf.api.views.version')
Expand Down Expand Up @@ -973,6 +1164,39 @@ def test_active_email_list(self):
self.assertCountEqual(result.keys(), ["addresses"])
self.assertCountEqual(result["addresses"], Email.objects.filter(active=True).values_list("address", flat=True))

@override_settings(APP_API_TOKENS={"ietf.api.views.related_email_list": ["valid-token"]})
def test_related_email_list(self):
joe = EmailFactory(address='joe@work.com')
EmailFactory(address='joe@home.com', person=joe.person)
EmailFactory(address='jòe@spain.com', person=joe.person)
url = urlreverse("ietf.api.views.related_email_list", kwargs={'email': 'joe@home.com'})
# no api key
r = self.client.get(url, headers={})
self.assertEqual(r.status_code, 403)
# invalid api key
r = self.client.get(url, headers={"X-Api-Key": "not-the-valid-token"})
self.assertEqual(r.status_code, 403)
# wrong method
r = self.client.post(url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 405)
# valid
r = self.client.get(url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 200)
self.assertEqual(r.headers["Content-Type"], "application/json")
result = json.loads(r.content)
self.assertCountEqual(result.keys(), ["addresses"])
self.assertCountEqual(result["addresses"], joe.person.email_set.exclude(address='joe@home.com').values_list("address", flat=True))
# non-ascii
non_ascii_url = urlreverse("ietf.api.views.related_email_list", kwargs={'email': 'jòe@spain.com'})
r = self.client.get(non_ascii_url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 200)
result = json.loads(r.content)
self.assertTrue('joe@home.com' in result["addresses"])
# email not found
not_found_url = urlreverse("ietf.api.views.related_email_list", kwargs={'email': 'nobody@nowhere.com'})
r = self.client.get(not_found_url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 404)

@override_settings(APP_API_TOKENS={"ietf.api.views.role_holder_addresses": ["valid-token"]})
def test_role_holder_addresses(self):
url = urlreverse("ietf.api.views.role_holder_addresses")
Expand Down Expand Up @@ -1440,7 +1664,7 @@ def test_api_top_level(self):
resource_list = r.json()

for name in self.apps:
if not name in self.apps:
if not name in resource_list:
sys.stderr.write("Expected a REST API resource for %s, but didn't find one\n" % name)

for name in self.apps:
Expand Down
3 changes: 3 additions & 0 deletions ietf/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,15 @@
# Let MeetEcho upload session polls
url(r'^notify/session/polls/?$', meeting_views.api_upload_polls),
# Let the registration system notify us about registrations
url(r'^notify/meeting/registration/v2/?', api_views.api_new_meeting_registration_v2),
url(r'^notify/meeting/registration/?', api_views.api_new_meeting_registration),
# OpenID authentication provider
url(r'^openid/$', TemplateView.as_view(template_name='api/openid-issuer.html'), name='ietf.api.urls.oidc_issuer'),
url(r'^openid/', include('oidc_provider.urls', namespace='oidc_provider')),
# Email alias listing
url(r'^person/email/$', api_views.active_email_list),
# Related Email listing
url(r'^person/email/(?P<email>[^/\x00]+)/related/$', api_views.related_email_list),
# Draft submission API
url(r'^submit/?$', submit_views.api_submit_tombstone),
# Draft upload API
Expand Down
Loading