mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-05 16:58:25 -04:00
@@ -111,7 +111,12 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-field-heading {
|
.table-field-headings,
|
||||||
|
.table-field-headings-visible {
|
||||||
|
|
||||||
|
th {
|
||||||
|
@include bold-19;
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard-table &-first {
|
.dashboard-table &-first {
|
||||||
width: 52.5%;
|
width: 52.5%;
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ from wtforms import (
|
|||||||
FileField,
|
FileField,
|
||||||
BooleanField,
|
BooleanField,
|
||||||
HiddenField,
|
HiddenField,
|
||||||
IntegerField
|
IntegerField,
|
||||||
|
RadioField
|
||||||
)
|
)
|
||||||
from wtforms.fields.html5 import EmailField, TelField
|
from wtforms.fields.html5 import EmailField, TelField
|
||||||
from wtforms.validators import (DataRequired, Email, Length, Regexp)
|
from wtforms.validators import (DataRequired, Email, Length, Regexp)
|
||||||
|
|
||||||
from app.main.validators import (Blacklist, CsvFileValidator, ValidEmailDomainRegex, NoCommasInPlaceHolders)
|
from app.main.validators import (Blacklist, CsvFileValidator, ValidEmailDomainRegex, NoCommasInPlaceHolders)
|
||||||
|
from app.notify_client.api_key_api_client import KEY_TYPE_NORMAL, KEY_TYPE_TEST, KEY_TYPE_TEAM
|
||||||
|
|
||||||
|
|
||||||
def email_address(label='Email address'):
|
def email_address(label='Email address'):
|
||||||
@@ -291,6 +293,18 @@ class CreateKeyForm(Form):
|
|||||||
self.existing_key_names = [x.lower() for x in existing_key_names]
|
self.existing_key_names = [x.lower() for x in existing_key_names]
|
||||||
super(CreateKeyForm, self).__init__(*args, **kwargs)
|
super(CreateKeyForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
key_type = RadioField(
|
||||||
|
'What should Notify do when you use this key?',
|
||||||
|
choices=[
|
||||||
|
(KEY_TYPE_NORMAL, 'Send messages to anyone'),
|
||||||
|
(KEY_TYPE_TEST, 'Simulate sending messages to anyone'),
|
||||||
|
(KEY_TYPE_TEAM, 'Only send messages to members of your team')
|
||||||
|
],
|
||||||
|
validators=[
|
||||||
|
DataRequired()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
key_name = StringField(u'Description of key', validators=[
|
key_name = StringField(u'Description of key', validators=[
|
||||||
DataRequired(message='You need to give the key a name')
|
DataRequired(message='You need to give the key a name')
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -25,12 +25,16 @@ def create_api_key(service_id):
|
|||||||
]
|
]
|
||||||
form = CreateKeyForm(key_names)
|
form = CreateKeyForm(key_names)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
secret = api_key_api_client.create_api_key(service_id=service_id, key_name=form.key_name.data)
|
secret = api_key_api_client.create_api_key(
|
||||||
|
service_id=service_id,
|
||||||
|
key_name=form.key_name.data,
|
||||||
|
key_type=form.key_type.data
|
||||||
|
)
|
||||||
return render_template('views/api-keys/show.html', secret=secret,
|
return render_template('views/api-keys/show.html', secret=secret,
|
||||||
key_name=form.key_name.data)
|
key_name=form.key_name.data)
|
||||||
return render_template(
|
return render_template(
|
||||||
'views/api-keys/create.html',
|
'views/api-keys/create.html',
|
||||||
key_name=form.key_name
|
form=form
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from app.notify_client import _attach_current_user
|
|||||||
# must match key types in notifications-api/app/models.py
|
# must match key types in notifications-api/app/models.py
|
||||||
KEY_TYPE_NORMAL = 'normal'
|
KEY_TYPE_NORMAL = 'normal'
|
||||||
KEY_TYPE_TEAM = 'team'
|
KEY_TYPE_TEAM = 'team'
|
||||||
|
KEY_TYPE_TEST = 'test'
|
||||||
|
|
||||||
|
|
||||||
class ApiKeyApiClient(BaseAPIClient):
|
class ApiKeyApiClient(BaseAPIClient):
|
||||||
@@ -24,10 +25,10 @@ class ApiKeyApiClient(BaseAPIClient):
|
|||||||
else:
|
else:
|
||||||
return self.get(url='/service/{}/api-keys'.format(service_id))
|
return self.get(url='/service/{}/api-keys'.format(service_id))
|
||||||
|
|
||||||
def create_api_key(self, service_id, key_name):
|
def create_api_key(self, service_id, key_name, key_type):
|
||||||
data = {
|
data = {
|
||||||
'name': key_name,
|
'name': key_name,
|
||||||
'key_type': KEY_TYPE_NORMAL
|
'key_type': key_type
|
||||||
}
|
}
|
||||||
_attach_current_user(data)
|
_attach_current_user(data)
|
||||||
key = self.post(url='/service/{}/api-key'.format(service_id), data=data)
|
key = self.post(url='/service/{}/api-key'.format(service_id), data=data)
|
||||||
|
|||||||
23
app/templates/components/radios.html
Normal file
23
app/templates/components/radios.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% macro radios(
|
||||||
|
field,
|
||||||
|
hint=None
|
||||||
|
) %}
|
||||||
|
<div class="form-group {% if field.errors %} error{% endif %}">
|
||||||
|
<fieldset>
|
||||||
|
<legend class="form-label">
|
||||||
|
{{ field.label }}
|
||||||
|
{% if field.errors %}
|
||||||
|
<span class="error-message">
|
||||||
|
{{ field.errors[0] }}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</legend>
|
||||||
|
{% for option in field %}
|
||||||
|
<label class="block-label" for="{{ option.id }}">
|
||||||
|
{{ option }}
|
||||||
|
{{ option.label.text }}
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
<li><a href="{{ url_for('.manage_users', service_id=current_service.id) }}">Team members</a></li>
|
<li><a href="{{ url_for('.manage_users', service_id=current_service.id) }}">Team members</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if current_user.has_permissions(['manage_api_keys']) %}
|
{% if current_user.has_permissions(['manage_api_keys']) %}
|
||||||
<li><a href="{{ url_for('.api_keys', service_id=current_service.id) }}">API keys</a></li>
|
<li><a href="{{ url_for('.api_keys', service_id=current_service.id) }}">API integration</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
<div class="column-two-thirds">
|
<div class="column-two-thirds">
|
||||||
<h1 class="heading-large">
|
<h1 class="heading-large">
|
||||||
API keys
|
API integration
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="column-one-third">
|
<div class="column-one-third">
|
||||||
@@ -23,15 +23,29 @@
|
|||||||
keys,
|
keys,
|
||||||
empty_message="You haven’t created any API keys yet",
|
empty_message="You haven’t created any API keys yet",
|
||||||
caption="API keys",
|
caption="API keys",
|
||||||
caption_visible=False,
|
caption_visible=false,
|
||||||
field_headings=['Key name', hidden_field_heading('Action')]
|
field_headings=[
|
||||||
|
'API keys',
|
||||||
|
hidden_field_heading('Action')
|
||||||
|
],
|
||||||
) %}
|
) %}
|
||||||
{% call field() %}
|
{% call field() %}
|
||||||
|
<div class="file-list">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
|
<span class="file-list-hint">
|
||||||
|
{% if item.key_type == 'normal' %}
|
||||||
|
<span class="visually-hidden">Normal</span>
|
||||||
|
{% elif item.key_type == 'team' %}
|
||||||
|
Only sends to team members
|
||||||
|
{% elif item.key_type == 'test' %}
|
||||||
|
Simulates sending messages
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{% if item.expiry_date %}
|
{% if item.expiry_date %}
|
||||||
{% call field(align='right', status='default') %}
|
{% call field(align='right') %}
|
||||||
Revoked {{ item.expiry_date|format_datetime }}
|
Revoked {{ item.expiry_date|format_datetime_short }}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% call field(align='right', status='error') %}
|
{% call field(align='right', status='error') %}
|
||||||
@@ -44,10 +58,11 @@
|
|||||||
{{ api_key(current_service.id, "Service ID", thing="service ID") }}
|
{{ api_key(current_service.id, "Service ID", thing="service ID") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2 class="heading-small">Documentation</h2>
|
||||||
<p>
|
<p>
|
||||||
API usage is described in the
|
See the
|
||||||
<a href="{{ url_for('.documentation', service_id=current_service.id) }}">
|
<a href="{{ url_for('.documentation') }}">
|
||||||
API documentation</a>.
|
API documentation</a> for clients and specfications.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends "withnav_template.html" %}
|
{% extends "withnav_template.html" %}
|
||||||
{% from "components/page-footer.html" import page_footer %}
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
{% from "components/textbox.html" import textbox %}
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
{% from "components/radios.html" import radios %}
|
||||||
|
|
||||||
{% block page_title %}
|
{% block page_title %}
|
||||||
Add a new API key – GOV.UK Notify
|
Add a new API key – GOV.UK Notify
|
||||||
@@ -9,11 +10,12 @@
|
|||||||
{% block maincolumn_content %}
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
<h1 class="heading-large">
|
<h1 class="heading-large">
|
||||||
Add a new API key
|
Create an API key
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{{ textbox(key_name, hint='eg CRM application') }}
|
{{ radios(form.key_type) }}
|
||||||
|
{{ textbox(form.key_name, label='Name for this key') }}
|
||||||
{{ page_footer('Continue') }}
|
{{ page_footer('Continue') }}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
from werkzeug.datastructures import MultiDict
|
from werkzeug.datastructures import MultiDict
|
||||||
|
|
||||||
from app.main.forms import CreateKeyForm
|
from app.main.forms import CreateKeyForm
|
||||||
@@ -11,4 +13,20 @@ def test_return_validation_error_when_key_name_exists(app_):
|
|||||||
form = CreateKeyForm(_get_names(),
|
form = CreateKeyForm(_get_names(),
|
||||||
formdata=MultiDict([('key_name', 'Some key')]))
|
formdata=MultiDict([('key_name', 'Some key')]))
|
||||||
form.validate()
|
form.validate()
|
||||||
assert {'key_name': ['A key with this name already exists']} == form.errors
|
assert form.errors['key_name'] == ['A key with this name already exists']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'key_type, expected_error', [
|
||||||
|
('', 'This field is required.'),
|
||||||
|
('invalid', 'Not a valid choice')
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_return_validation_error_when_key_type_not_chosen(app_, key_type, expected_error):
|
||||||
|
|
||||||
|
with app_.test_request_context():
|
||||||
|
form = CreateKeyForm(
|
||||||
|
[],
|
||||||
|
formdata=MultiDict([('key_name', 'Some key'), ('key_type', key_type)]))
|
||||||
|
form.validate()
|
||||||
|
assert form.errors['key_type'] == [expected_error]
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def test_should_show_api_keys_page(app_,
|
|||||||
resp_data = response.get_data(as_text=True)
|
resp_data = response.get_data(as_text=True)
|
||||||
assert 'some key name' in resp_data
|
assert 'some key name' in resp_data
|
||||||
assert 'another key name' in resp_data
|
assert 'another key name' in resp_data
|
||||||
assert 'Revoked Thursday 01 January 1970 at 01:00' in resp_data
|
assert 'Revoked 1 January at 01:00' in resp_data
|
||||||
mock_get_api_keys.assert_called_once_with(service_id=fake_uuid)
|
mock_get_api_keys.assert_called_once_with(service_id=fake_uuid)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,8 +72,13 @@ def test_should_create_api_key_with_type_normal(app_,
|
|||||||
|
|
||||||
with app_.test_request_context(), app_.test_client() as client:
|
with app_.test_request_context(), app_.test_client() as client:
|
||||||
client.login(api_user_active)
|
client.login(api_user_active)
|
||||||
response = client.post(url_for('main.create_api_key', service_id=service_id),
|
response = client.post(
|
||||||
data={'key_name': 'some default key name'})
|
url_for('main.create_api_key', service_id=service_id),
|
||||||
|
data={
|
||||||
|
'key_name': 'some default key name',
|
||||||
|
'key_type': 'normal'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert 'some default key name' in response.get_data(as_text=True)
|
assert 'some default key name' in response.get_data(as_text=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user