mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-13 18:38:33 -04:00
Merge pull request #1846 from alphagov/add-organisations
Add organisations
This commit is contained in:
@@ -51,6 +51,7 @@ from app.notify_client.events_api_client import EventsApiClient
|
||||
from app.notify_client.provider_client import ProviderClient
|
||||
from app.notify_client.email_branding_client import EmailBrandingClient
|
||||
from app.notify_client.models import AnonymousUser
|
||||
from app.notify_client.organisations_api_client import OrganisationsClient
|
||||
from app.notify_client.letter_jobs_client import LetterJobsClient
|
||||
from app.notify_client.inbound_number_client import InboundNumberClient
|
||||
from app.notify_client.billing_api_client import BillingAPIClient
|
||||
@@ -72,6 +73,7 @@ template_statistics_client = TemplateStatisticsApiClient()
|
||||
events_api_client = EventsApiClient()
|
||||
provider_client = ProviderClient()
|
||||
email_branding_client = EmailBrandingClient()
|
||||
organisations_client = OrganisationsClient()
|
||||
asset_fingerprinter = AssetFingerprinter()
|
||||
statsd_client = StatsdClient()
|
||||
deskpro_client = DeskproClient()
|
||||
@@ -108,6 +110,7 @@ def create_app(application):
|
||||
events_api_client.init_app(application)
|
||||
provider_client.init_app(application)
|
||||
email_branding_client.init_app(application)
|
||||
organisations_client.init_app(application)
|
||||
letter_jobs_client.init_app(application)
|
||||
inbound_number_client.init_app(application)
|
||||
billing_api_client.init_app(application)
|
||||
|
||||
@@ -30,6 +30,7 @@ from app.main.views import ( # noqa
|
||||
letter_jobs,
|
||||
email_branding,
|
||||
conversation,
|
||||
organisations,
|
||||
notifications,
|
||||
inbound_number
|
||||
)
|
||||
|
||||
@@ -665,6 +665,11 @@ class ServiceCreateEmailBranding(StripWhitespaceForm):
|
||||
file = FileField_wtf('Upload a PNG logo', validators=[FileAllowed(['png'], 'PNG Images only!')])
|
||||
|
||||
|
||||
class CreateOrUpdateOrganisation(StripWhitespaceForm):
|
||||
|
||||
name = StringField('Name', validators=[DataRequired()])
|
||||
|
||||
|
||||
class LetterBranding(StripWhitespaceForm):
|
||||
|
||||
def __init__(self, choices=[], *args, **kwargs):
|
||||
|
||||
75
app/main/views/organisations.py
Normal file
75
app/main/views/organisations.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from flask import redirect, render_template, url_for
|
||||
from flask_login import login_required
|
||||
|
||||
from app import organisations_client
|
||||
from app.main import main
|
||||
from app.main.forms import CreateOrUpdateOrganisation
|
||||
from app.utils import user_has_permissions
|
||||
|
||||
|
||||
@main.route("/organisations", methods=['GET'])
|
||||
@login_required
|
||||
@user_has_permissions(admin_override=True)
|
||||
def organisations():
|
||||
orgs = organisations_client.get_organisations()
|
||||
|
||||
return render_template(
|
||||
'views/organisations/organisations.html',
|
||||
organisations=orgs
|
||||
)
|
||||
|
||||
|
||||
@main.route("/organisation/<org_id>", methods=['GET'])
|
||||
@login_required
|
||||
@user_has_permissions(admin_override=True)
|
||||
def view_organisation(org_id):
|
||||
org = organisations_client.get_organisation(org_id)
|
||||
|
||||
return render_template(
|
||||
'views/organisations/view-organisation.html',
|
||||
organisation=org
|
||||
)
|
||||
|
||||
|
||||
@main.route("/organisation/<org_id>/edit", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@user_has_permissions(admin_override=True)
|
||||
def update_organisation(org_id):
|
||||
org = organisations_client.get_organisation(org_id)
|
||||
|
||||
form = CreateOrUpdateOrganisation()
|
||||
|
||||
if form.validate_on_submit():
|
||||
organisations_client.update_organisation(
|
||||
org_id=org_id,
|
||||
name=form.name.data
|
||||
)
|
||||
|
||||
return redirect(url_for('.organisations'))
|
||||
|
||||
form.name.data = org['name']
|
||||
|
||||
return render_template(
|
||||
'views/organisations/manage-organisation.html',
|
||||
form=form,
|
||||
organisation=org
|
||||
)
|
||||
|
||||
|
||||
@main.route("/organisations/add", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@user_has_permissions(admin_override=True)
|
||||
def add_organisation():
|
||||
form = CreateOrUpdateOrganisation()
|
||||
|
||||
if form.validate_on_submit():
|
||||
organisations_client.create_organisation(
|
||||
name=form.name.data,
|
||||
)
|
||||
|
||||
return redirect(url_for('.organisations'))
|
||||
|
||||
return render_template(
|
||||
'views/organisations/manage-organisation.html',
|
||||
form=form
|
||||
)
|
||||
30
app/notify_client/organisations_api_client.py
Normal file
30
app/notify_client/organisations_api_client.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from app.notify_client import NotifyAdminAPIClient
|
||||
|
||||
|
||||
class OrganisationsClient(NotifyAdminAPIClient):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("a" * 73, "b")
|
||||
|
||||
def init_app(self, app):
|
||||
self.base_url = app.config['API_HOST_NAME']
|
||||
self.service_id = app.config['ADMIN_CLIENT_USER_NAME']
|
||||
self.api_key = app.config['ADMIN_CLIENT_SECRET']
|
||||
|
||||
def get_organisations(self):
|
||||
return self.get(url='/organisations')
|
||||
|
||||
def get_organisation(self, org_id):
|
||||
return self.get(url='/organisations/{}'.format(org_id))
|
||||
|
||||
def create_organisation(self, name):
|
||||
data = {
|
||||
"name": name
|
||||
}
|
||||
return self.post(url="/organisations", data=data)
|
||||
|
||||
def update_organisation(self, org_id, name):
|
||||
data = {
|
||||
"name": name
|
||||
}
|
||||
return self.post(url="/organisations/{}".format(org_id), data=data)
|
||||
24
app/templates/views/organisations/manage-organisation.html
Normal file
24
app/templates/views/organisations/manage-organisation.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends "views/platform-admin/_base_template.html" %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
{% from "components/textbox.html" import textbox, colour_textbox %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{{ '{} an organisation'.format('Update' if organisation else 'Create')}}
|
||||
{% endblock %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ '{} an organisation'.format('Update' if organisation else 'Create')}}
|
||||
{% endblock %}
|
||||
|
||||
{% block platform_admin_content %}
|
||||
|
||||
<h1 class="heading-large">{{ '{} an organisation'.format('Update' if organisation else 'Create')}}</h1>
|
||||
<form method="post">
|
||||
{{textbox(form.name)}}
|
||||
{{ page_footer(
|
||||
'Save',
|
||||
back_link=url_for('.organisations'),
|
||||
back_link_text='Back to organisations',
|
||||
) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
33
app/templates/views/organisations/organisations.html
Normal file
33
app/templates/views/organisations/organisations.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "views/platform-admin/_base_template.html" %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block per_page_title %}
|
||||
Organisations
|
||||
{% endblock %}
|
||||
|
||||
{% block service_page_title %}
|
||||
Organisations
|
||||
{% endblock %}
|
||||
|
||||
{% block platform_admin_content %}
|
||||
|
||||
<h1 class="heading-large">
|
||||
Organisations
|
||||
</h1>
|
||||
<nav class="browse-list">
|
||||
<ul>
|
||||
{% for org in organisations %}
|
||||
<li class="browse-list-item">
|
||||
<a href="{{ url_for('main.view_organisation', org_id=org['id']) }}" class="browse-list-link">{{ org['name'] }}</a>
|
||||
{% if not org['active'] %}
|
||||
<span class="table-field-status-default heading-medium">- archived</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
<div>
|
||||
<a href="{{ url_for('main.add_organisation') }}" class="browse-list-link">Create an organisation</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
23
app/templates/views/organisations/view-organisation.html
Normal file
23
app/templates/views/organisations/view-organisation.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{% extends "views/platform-admin/_base_template.html" %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{{ organisation['name'] }}
|
||||
{% endblock %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ organisation['name'] }}
|
||||
{% endblock %}
|
||||
|
||||
{% block platform_admin_content %}
|
||||
|
||||
<h1 class="heading-large">
|
||||
<div>{{ organisation['name'] }}</div>
|
||||
</h1>
|
||||
<div class="grid-row">
|
||||
<div class="column-three-quarters">
|
||||
[List of services]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -11,10 +11,11 @@
|
||||
Platform admin
|
||||
</p>
|
||||
<nav class="navigation">
|
||||
{% for link_text, url in [
|
||||
{% for link_text, url in [
|
||||
('Summary', url_for('main.platform_admin')),
|
||||
('Live services', url_for('main.live_services')),
|
||||
('Trial mode services', url_for('main.trial_services')),
|
||||
('Organisations', url_for('main.organisations')),
|
||||
('Providers', url_for('main.view_providers')),
|
||||
('Email branding', url_for('main.email_branding')),
|
||||
('Letter jobs', url_for('main.letter_jobs')),
|
||||
|
||||
124
tests/app/main/views/test_organisations.py
Normal file
124
tests/app/main/views/test_organisations.py
Normal file
@@ -0,0 +1,124 @@
|
||||
from bs4 import BeautifulSoup
|
||||
from flask import url_for
|
||||
|
||||
from tests.conftest import (
|
||||
normalize_spaces
|
||||
)
|
||||
|
||||
|
||||
def test_organisation_page_shows_all_organisations(
|
||||
logged_in_platform_admin_client,
|
||||
mocker
|
||||
):
|
||||
orgs = [
|
||||
{'id': '1', 'name': 'Test 1', 'active': True},
|
||||
{'id': '2', 'name': 'Test 2', 'active': True},
|
||||
{'id': '3', 'name': 'Test 3', 'active': False},
|
||||
]
|
||||
|
||||
mocker.patch(
|
||||
'app.organisations_client.get_organisations', return_value=orgs
|
||||
)
|
||||
response = logged_in_platform_admin_client.get(
|
||||
url_for('.organisations')
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
|
||||
assert normalize_spaces(
|
||||
page.select_one('h1').text
|
||||
) == "Organisations"
|
||||
|
||||
for index, org in enumerate(orgs):
|
||||
assert page.select('a.browse-list-link')[index].text == org['name']
|
||||
if not org['active']:
|
||||
assert page.select_one('.table-field-status-default,heading-medium').text == '- archived'
|
||||
assert normalize_spaces((page.select('a.browse-list-link')[-1]).text) == 'Create an organisation'
|
||||
|
||||
|
||||
def test_view_organisation_shows_the_correct_organisation(
|
||||
logged_in_platform_admin_client,
|
||||
fake_uuid,
|
||||
mocker
|
||||
):
|
||||
org = {'id': fake_uuid, 'name': 'Test 1', 'active': True}
|
||||
mocker.patch(
|
||||
'app.organisations_client.get_organisation', return_value=org
|
||||
)
|
||||
|
||||
response = logged_in_platform_admin_client.get(
|
||||
url_for('.view_organisation', org_id=fake_uuid)
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
|
||||
assert page.select_one('.heading-large div').text == org['name']
|
||||
|
||||
|
||||
def test_edit_organisation_shows_the_correct_organisation(
|
||||
logged_in_platform_admin_client,
|
||||
fake_uuid,
|
||||
mocker
|
||||
):
|
||||
org = {'id': fake_uuid, 'name': 'Test 1', 'active': True}
|
||||
mocker.patch(
|
||||
'app.organisations_client.get_organisation', return_value=org
|
||||
)
|
||||
|
||||
response = logged_in_platform_admin_client.get(
|
||||
url_for('.update_organisation', org_id=fake_uuid)
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
|
||||
assert page.select_one('#name').attrs.get('value') == org['name']
|
||||
|
||||
|
||||
def test_create_new_organisation(
|
||||
logged_in_platform_admin_client,
|
||||
mocker,
|
||||
fake_uuid
|
||||
):
|
||||
mock_create_organisation = mocker.patch(
|
||||
'app.organisations_client.create_organisation'
|
||||
)
|
||||
|
||||
org = {'name': 'new name'}
|
||||
|
||||
logged_in_platform_admin_client.post(
|
||||
url_for('.add_organisation'),
|
||||
content_type='multipart/form-data',
|
||||
data=org
|
||||
)
|
||||
|
||||
mock_create_organisation.assert_called_once_with(name=org['name'])
|
||||
|
||||
|
||||
def test_update_organisation(
|
||||
logged_in_platform_admin_client,
|
||||
mocker,
|
||||
fake_uuid,
|
||||
):
|
||||
org = {'name': 'new name'}
|
||||
|
||||
mocker.patch(
|
||||
'app.organisations_client.get_organisation', return_value=org
|
||||
)
|
||||
mock_update_organisation = mocker.patch(
|
||||
'app.organisations_client.update_organisation'
|
||||
)
|
||||
|
||||
logged_in_platform_admin_client.post(
|
||||
url_for('.update_organisation', org_id=fake_uuid),
|
||||
content_type='multipart/form-data',
|
||||
data=org
|
||||
)
|
||||
|
||||
assert mock_update_organisation.called
|
||||
mock_update_organisation.assert_called_once_with(
|
||||
org_id=fake_uuid,
|
||||
name=org['name']
|
||||
)
|
||||
Reference in New Issue
Block a user