Merge pull request #1352 from dod-ccpo/add-subscription-to-env

Add subscription to env
This commit is contained in:
leigh-mil 2020-01-23 20:20:58 -05:00 committed by GitHub
commit 204bf49ff4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 148 additions and 30 deletions

View File

@ -341,6 +341,12 @@ class CloudProviderInterface:
"""
raise NotImplementedError()
def create_subscription(self, environment):
"""Returns True if a new subscription has been created or raises an
exception if an error occurs while creating a subscription.
"""
raise NotImplementedError()
class MockCloudProvider(CloudProviderInterface):
@ -508,6 +514,11 @@ class MockCloudProvider(CloudProviderInterface):
return self._maybe(12)
def create_subscription(self, environment):
self._maybe_raise(self.UNAUTHORIZED_RATE, GeneralCSPException)
return True
def get_calculator_url(self):
return "https://www.rackspace.com/en-us/calculator"

View File

@ -26,7 +26,7 @@ def has_portfolio_applications(_user, portfolio=None, **_kwargs):
def portfolio_applications(portfolio_id):
user_env_roles = EnvironmentRoles.for_user(g.current_user.id, portfolio_id)
environment_access = {
env_role.environment_id: env_role.role for env_role in user_env_roles
env_role.environment_id: env_role.role.value for env_role in user_env_roles
}
return render_template(

View File

@ -1,9 +1,10 @@
from flask import (
current_app as app,
g,
redirect,
render_template,
request as http_request,
url_for,
g,
)
from .blueprint import applications_bp
@ -522,3 +523,31 @@ def resend_invite(application_id, application_role_id):
_anchor="application-members",
)
)
@applications_bp.route(
"/environments/<environment_id>/add_subscription", methods=["POST"]
)
@user_can(Permissions.EDIT_ENVIRONMENT, message="create new environment subscription")
def create_subscription(environment_id):
environment = Environments.get(environment_id)
try:
app.csp.cloud.create_subscription(environment)
flash("environment_subscription_success", name=environment.displayname)
except GeneralCSPException:
flash("environment_subscription_failure")
return (
render_settings_page(application=environment.application, show_flash=True),
400,
)
return redirect(
url_for(
"applications.settings",
application_id=environment.application.id,
fragment="application-environments",
_anchor="application-environments",
)
)

View File

@ -83,6 +83,16 @@ MESSAGES = {
"message": "flash.environment.deleted.message",
"category": "success",
},
"environment_subscription_failure": {
"title": "flash.environment.subscription_failure.title",
"message": "flash.environment.subscription_failure.message",
"category": "error",
},
"environment_subscription_success": {
"title": "flash.environment.subscription_success.title",
"message": "flash.environment.subscription_success.message",
"category": "success",
},
"form_errors": {
"title": "flash.form.errors.title",
"message": "flash.form.errors.message",

View File

@ -106,7 +106,7 @@
&__expanded {
font-size: $small-font-size;
font-weight: $font-normal;
background-color: $color-gray-lightest;
background-color: $color-offwhite;
padding: $gap;
&:last-child {

View File

@ -222,14 +222,21 @@
span.accordion-table__item__toggler {
font-weight: $font-normal;
text-decoration: underline;
font-size: $small-font-size;
&.environment-list__item__members {
float: unset;
font-size: $small-font-size;
margin-left: -$gap;
}
}
}
li.environment-list__edit {
border: 1px solid $color-gray-lighter;
padding: 0 $gap * 3 $gap * 2;
}
.activity-log {
border-top: 3px solid $color-blue;

View File

@ -17,7 +17,7 @@
{% if 0 == environments_obj | length -%}
<div class="empty-state panel__content">
<p class="empty-state__message">
This Application has no environments
{{ 'portfolios.applications.environments.blank_slate' | translate }}
</p>
</div>
{% else %}
@ -31,8 +31,23 @@
<div class="accordion-table__item-content">
<div class="environment-list__item">
<span>
{{ env['name'] }}
<a
href='{{ url_for("applications.access_environment", environment_id=env.id)}}'
target='_blank'
rel='noopener noreferrer'>
{{ env['name'] }} {{ Icon('link', classes='icon--medium icon--primary') }}
</a>
</span>
{% if user_can(permissions.EDIT_ENVIRONMENT) -%}
{{
ToggleButton(
open_html="common.edit"|translate,
close_html="common.close"|translate,
section_name="edit"
)
}}
{%- endif %}
<br>
{% set members_button = "portfolios.applications.member_count" | translate({'count': env['member_count']}) %}
{{
ToggleButton(
@ -42,23 +57,9 @@
classes="environment-list__item__members"
)
}}
{% if user_can(permissions.EDIT_ENVIRONMENT) -%}
{% set edit_environment_button = "Edit" %}
{{
ToggleButton(
open_html=edit_environment_button,
close_html=edit_environment_button,
section_name="edit"
)
}}
{%- endif %}
<br>
{% if env['pending'] -%}
{{ Label(type="changes_pending", classes='label--below')}}
{% else %}
<a href='{{ url_for("applications.access_environment", environment_id=env.id)}}' target='_blank' rel='noopener noreferrer' class='application-list-item__environment__csp_link'>
<span>{{ "portfolios.applications.csp_link" | translate }} {{ Icon('link', classes="icon--tiny") }}</span>
</a>
{%- endif %}
</div>
</div>
@ -66,7 +67,7 @@
{% call ToggleSection(section_name="members") %}
<ul>
{% for member in env['members'] %}
{% set status = ": Access Suspended" if member['status'] == 'disabled' %}
{% set status = "portfolios.applications.environments.disabled"|translate if member['status'] == 'disabled' %}
<li class="accordion-table__item-toggle-content__expanded">
{{ member['user_name'] }}{{ status }}
</li>
@ -77,16 +78,28 @@
{% if user_can(permissions.EDIT_ENVIRONMENT) -%}
{% call ToggleSection(section_name="edit") %}
<ul>
<li class="accordion-table__item-toggle-content__expanded">
<li class="accordion-table__item-toggle-content__expanded environment-list__edit">
<base-form inline-template>
<form action="{{ url_for('applications.update_environment', environment_id=env['id']) }}" method="post" v-on:submit="handleSubmit">
<form
action="{{ url_for('applications.update_environment', environment_id=env['id']) }}"
method="post"
v-on:submit="handleSubmit"
class="col col--half">
{{ edit_form.csrf_token }}
{{ TextInput(edit_form.name, validation='defaultStringField', optional=False) }}
{{
SaveButton(
text=("common.save_changes" | translate)
)
}}
<div class="action-group action-group--tight">
{{
SaveButton(
text=("common.save_changes" | translate)
)
}}
</div>
<button
type="submit"
formaction="{{ url_for('applications.create_subscription', environment_id=env.id)}}"
class="usa-button usa-button-secondary">
{{ "portfolios.applications.environments.add_subscription" | translate }}
</button>
</form>
</base-form>
</li>

View File

@ -2,7 +2,7 @@
{% set class = "usa-button usa-button-primary " + additional_classes %}
{% if element == "button" %}
<button type="submit" class="{{ class }}" tabindex="0" v-bind:disabled="!canSave" {{ form_attr }} >
<button type="submit" class="{{ class }}" tabindex="0" v-bind:disabled="!canSave">
{{ text }}
</button>
{% elif element == 'input' %}

View File

@ -777,3 +777,40 @@ def test_handle_update_member_with_error(set_g, monkeypatch, mock_logger):
handle_update_member(application.id, app_role.id, form_data)
assert mock_logger.messages[-1] == exception
def test_create_subscription_success(client, user_session):
environment = EnvironmentFactory.create()
user_session(environment.portfolio.owner)
response = client.post(
url_for("applications.create_subscription", environment_id=environment.id),
)
assert response.status_code == 302
assert response.location == url_for(
"applications.settings",
application_id=environment.application.id,
_external=True,
fragment="application-environments",
_anchor="application-environments",
)
def test_create_subscription_failure(client, user_session, monkeypatch):
environment = EnvironmentFactory.create()
def _raise_csp_exception(*args, **kwargs):
raise GeneralCSPException("An error occurred.")
monkeypatch.setattr(
"atst.domain.csp.cloud.MockCloudProvider.create_subscription",
_raise_csp_exception,
)
user_session(environment.portfolio.owner)
response = client.post(
url_for("applications.create_subscription", environment_id=environment.id),
)
assert response.status_code == 400

View File

@ -46,6 +46,7 @@ common:
delete_confirm: "Please type the word {word} to confirm:"
dod_id: DoD ID
disable: Disable
edit: Edit
email: Email
lorem: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
name: Name
@ -127,6 +128,12 @@ flash:
deleted:
title: "{environment_name} deleted"
message: The environment "{environment_name}" has been deleted
subscription_failure:
title: Environment subscription error
message: An unexpected problem occurred with your request, please try again. If the problem persists, contact an administrator.
subscription_success:
title: Success!
message: "A subscription has been added to {name} environment"
form:
errors:
title: There were some errors
@ -406,9 +413,13 @@ portfolios:
create_new_env_info: Creating an environment gives you access to the Cloud Service Provider. This environment will function within the constraints of the task order, and any costs will be billed against the portfolio.
csp_link: Cloud Service Provider Link
enter_env_name: "Enter environment name:"
environments:
add_subscription: Add new subscription
blank_slate: This Application has no environments
disabled: ": Access Suspended"
environments_heading: Application Environments
existing_application_title: "{application_name} Application Settings"
member_count: "{count} members"
member_count: "{count} Members"
new_application_title: New Application
settings:
name_description: Application name and description