Merge pull request #398 from dod-ccpo/rename-project-#158473663
Rename project #158473663
This commit is contained in:
commit
d3cb7e3dd1
@ -64,3 +64,15 @@ class Projects(object):
|
||||
raise NotFoundError("projects")
|
||||
|
||||
return projects
|
||||
|
||||
@classmethod
|
||||
def update(cls, user, workspace, project, new_data):
|
||||
if "name" in new_data:
|
||||
project.name = new_data["name"]
|
||||
if "description" in new_data:
|
||||
project.description = new_data["description"]
|
||||
|
||||
db.session.add(project)
|
||||
db.session.commit()
|
||||
|
||||
return project
|
||||
|
@ -4,12 +4,14 @@ from wtforms.validators import Required
|
||||
from atst.forms.validators import ListItemRequired, ListItemsUnique
|
||||
|
||||
|
||||
class NewProjectForm(FlaskForm):
|
||||
|
||||
EMPTY_ENVIRONMENT_NAMES = ["", None]
|
||||
|
||||
class ProjectForm(FlaskForm):
|
||||
name = StringField(label="Project Name", validators=[Required()])
|
||||
description = TextAreaField(label="Description", validators=[Required()])
|
||||
|
||||
|
||||
class NewProjectForm(ProjectForm):
|
||||
EMPTY_ENVIRONMENT_NAMES = ["", None]
|
||||
|
||||
environment_names = FieldList(
|
||||
StringField(label="Environment Name"),
|
||||
validators=[
|
@ -17,7 +17,7 @@ from atst.domain.workspaces import Workspaces
|
||||
from atst.domain.workspace_users import WorkspaceUsers
|
||||
from atst.domain.environments import Environments
|
||||
from atst.domain.environment_roles import EnvironmentRoles
|
||||
from atst.forms.new_project import NewProjectForm
|
||||
from atst.forms.project import NewProjectForm, ProjectForm
|
||||
from atst.forms.new_member import NewMemberForm
|
||||
from atst.forms.edit_member import EditMemberForm
|
||||
from atst.forms.workspace import WorkspaceForm
|
||||
@ -181,17 +181,34 @@ def create_project(workspace_id):
|
||||
def edit_project(workspace_id, project_id):
|
||||
workspace = Workspaces.get_for_update_projects(g.current_user, workspace_id)
|
||||
project = Projects.get(g.current_user, workspace, project_id)
|
||||
form = NewProjectForm(
|
||||
name=project.name,
|
||||
environment_names=[env.name for env in project.environments],
|
||||
description=project.description,
|
||||
)
|
||||
form = ProjectForm(name=project.name, description=project.description)
|
||||
|
||||
return render_template(
|
||||
"workspaces/projects/edit.html", workspace=workspace, project=project, form=form
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/projects/<project_id>/edit", methods=["POST"])
|
||||
def update_project(workspace_id, project_id):
|
||||
workspace = Workspaces.get_for_update_projects(g.current_user, workspace_id)
|
||||
project = Projects.get(g.current_user, workspace, project_id)
|
||||
form = ProjectForm(http_request.form)
|
||||
if form.validate():
|
||||
project_data = form.data
|
||||
Projects.update(g.current_user, workspace, project, project_data)
|
||||
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_projects", workspace_id=workspace.id)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"workspaces/projects/edit.html",
|
||||
workspace=workspace,
|
||||
project=project,
|
||||
form=form,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/members/new")
|
||||
def new_member(workspace_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
|
@ -133,6 +133,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.usa-input__choices { // checkbox & radio sets
|
||||
legend {
|
||||
padding: 0 0 $gap 0;
|
||||
|
@ -1,85 +1,19 @@
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/modal.html" import Modal %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
{% from "components/alert.html" import Alert %}
|
||||
|
||||
<new-project inline-template v-bind:initial-data='{{ form.data|tojson }}' modal-name='{{ modalName }}'>
|
||||
{% set new_project = project is not defined %}
|
||||
{% set form_action = url_for('workspaces.create_project', workspace_id=workspace.id) if new_project else url_for('workspaces.edit_project', workspace_id=workspace.id, project_id=project.id) %}
|
||||
{% set action_text = 'Create' if new_project else 'Update' %}
|
||||
{% set title_text = 'Add a new project' if new_project else 'Edit {} project'.format(project.name) %}
|
||||
{% set title_text = 'Edit {} project'.format(project.name) if project else 'Add a new project' %}
|
||||
|
||||
<form method="POST" action="{{ form_action }}" v-on:submit="handleSubmit">
|
||||
{% call Modal(name=modalName, dismissable=False) %}
|
||||
<h1>{{ action_text }} project !{ name }</h1>
|
||||
|
||||
<p>
|
||||
When you click <em>{{ action_text }} Project</em>, the environments
|
||||
<span v-for="(environment, index) in environments">
|
||||
<strong>!{environment.name}</strong><template v-if="index < (environments.length - 1)">, </template>
|
||||
</span>
|
||||
will be created as individual cloud resource groups under <strong>!{ name }</strong> project.
|
||||
</p>
|
||||
|
||||
<div class='action-group'>
|
||||
<button autofocus type='submit' class='action-group__action usa-button' tabindex='0'>{{ action_text }} Project</button>
|
||||
<button type='button' v-on:click="handleCancelSubmit" class='icon-link action-group__action' tabindex='0'>Cancel</button>
|
||||
</div>
|
||||
{% endcall %}
|
||||
|
||||
{{ form.csrf_token }}
|
||||
<div class="panel">
|
||||
<div class="panel__heading panel__heading--grow">
|
||||
<h1>{{ title_text }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="panel__content">
|
||||
<p>
|
||||
AT-AT allows you to organize your workspace into multiple projects, each of which may have environments.
|
||||
</p>
|
||||
{{ TextInput(form.name) }}
|
||||
{{ TextInput(form.description, paragraph=True) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div> {# this extra div prevents this bug: https://www.pivotaltracker.com/story/show/160768940 #}
|
||||
<div v-cloak v-for="title in errors" :key="title">
|
||||
{{ Alert(message=None, level="error", vue_template=True) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block-list project-list-item">
|
||||
<header class="block-list__header block-list__header--grow">
|
||||
<h2 class="block-list__title">Project Environments</h2>
|
||||
<p>
|
||||
Each environment created within a project is an enclave of cloud resources that is logically separated from each other for increased security.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<ul>
|
||||
<li v-for="(environment, i) in environments" class="block-list__item project-edit__env-list-item">
|
||||
<div class="usa-input">
|
||||
<label :for="'environment_names-' + i">Environment Name</label>
|
||||
<input type="text" :name="'environment_names-' + i" :id="'environment_names-' + i" v-model="environment.name" placeholder="e.g. Development, Staging, Production">
|
||||
</div>
|
||||
<button v-on:click="removeEnvironment(i)" v-if="environments.length > 1" type="button" class='project-edit__env-list-item__remover'>
|
||||
{{ Icon('trash') }}
|
||||
<span>Remove</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="block-list__footer">
|
||||
<button v-on:click="addEnvironment" class="icon-link" tabindex="0" type="button">Add another environment</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-group">
|
||||
<button class="usa-button usa-button-primary" tabindex="0" type="submit">{{ action_text }} Project</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</new-project>
|
||||
{{ form.csrf_token }}
|
||||
<div class="panel">
|
||||
<div class="panel__heading panel__heading--grow">
|
||||
<h1>{{ title_text }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="panel__content">
|
||||
<p>
|
||||
AT-AT allows you to organize your workspace into multiple projects, each of which may have environments.
|
||||
</p>
|
||||
{{ TextInput(form.name) }}
|
||||
{{ TextInput(form.description, paragraph=True) }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,15 +1,36 @@
|
||||
{% extends "workspaces/base.html" %}
|
||||
|
||||
{% from "components/alert.html" import Alert %}
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
|
||||
{% block workspace_content %}
|
||||
|
||||
{% if g.dev %}
|
||||
{{ Alert("In Progress", message="This page is a work in progress. You won't be able to edit environments on this project just yet.", level="warning") }}
|
||||
{% endif %}
|
||||
<form method="POST" action="{{ url_for('workspaces.edit_project', workspace_id=workspace.id, project_id=project.id) }}">
|
||||
|
||||
{% set modalName = "updateProjectConfirmation" %}
|
||||
{% include "fragments/edit_project_form.html" %}
|
||||
{% include "fragments/edit_project_form.html" %}
|
||||
|
||||
<div class="block-list project-list-item">
|
||||
<header class="block-list__header block-list__header--grow">
|
||||
<h2 class="block-list__title">Project Environments</h2>
|
||||
<p>
|
||||
Each environment created within a project is an enclave of cloud resources that is logically separated from each other for increased security.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<ul>
|
||||
{% for environment in project.environments %}
|
||||
<li class="block-list__item project-edit__env-list-item">
|
||||
<div class="usa-input">
|
||||
<label>Environment Name</label>
|
||||
<input type="text" value="{{ environment.name }}" readonly />
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="action-group">
|
||||
<button class="usa-button usa-button-primary" tabindex="0" type="submit">Update Project</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,19 +1,80 @@
|
||||
{% extends "workspaces/base.html" %}
|
||||
|
||||
{% from "components/alert.html" import Alert %}
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/modal.html" import Modal %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
|
||||
{% block workspace_content %}
|
||||
|
||||
{% set modalName = "newProjectConfirmation" %}
|
||||
{% if request.args.get("newWorkspace") %}
|
||||
{{ Alert('Workspace created!',
|
||||
message="\
|
||||
<p>You are now ready to create projects and environments within the JEDI Cloud.</p>
|
||||
",
|
||||
level='success'
|
||||
) }}
|
||||
{% endif %}
|
||||
{% set modalName = "newProjectConfirmation" %}
|
||||
{% if request.args.get("newWorkspace") %}
|
||||
{{ Alert('Workspace created!',
|
||||
message="\
|
||||
<p>You are now ready to create projects and environments within the JEDI Cloud.</p>
|
||||
",
|
||||
level='success'
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
{% include "fragments/edit_project_form.html" %}
|
||||
<new-project inline-template v-bind:initial-data='{{ form.data|tojson }}' modal-name='{{ modalName }}'>
|
||||
<form method="POST" action="{{ url_for('workspaces.create_project', workspace_id=workspace.id) }}" v-on:submit="handleSubmit">
|
||||
|
||||
{% call Modal(name=modalName, dismissable=False) %}
|
||||
<h1>Create project !{ name }</h1>
|
||||
|
||||
<p>
|
||||
When you click <em>Create Project</em>, the environments
|
||||
<span v-for="(environment, index) in environments">
|
||||
<strong>!{environment.name}</strong><template v-if="index < (environments.length - 1)">, </template>
|
||||
</span>
|
||||
will be created as individual cloud resource groups under <strong>!{ name }</strong> project.
|
||||
</p>
|
||||
|
||||
<div class='action-group'>
|
||||
<button autofocus type='submit' class='action-group__action usa-button' tabindex='0'>Create Project</button>
|
||||
<button type='button' v-on:click="handleCancelSubmit" class='icon-link action-group__action' tabindex='0'>Cancel</button>
|
||||
</div>
|
||||
{% endcall %}
|
||||
|
||||
{% include "fragments/edit_project_form.html" %}
|
||||
|
||||
<div> {# this extra div prevents this bug: https://www.pivotaltracker.com/story/show/160768940 #}
|
||||
<div v-cloak v-for="title in errors" :key="title">
|
||||
{{ Alert(message=None, level="error", vue_template=True) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block-list project-list-item">
|
||||
<header class="block-list__header block-list__header--grow">
|
||||
<h2 class="block-list__title">Project Environments</h2>
|
||||
<p>
|
||||
Each environment created within a project is an enclave of cloud resources that is logically separated from each other for increased security.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<ul>
|
||||
<li v-for="(environment, i) in environments" class="block-list__item project-edit__env-list-item">
|
||||
<div class="usa-input">
|
||||
<label :for="'environment_names-' + i">Environment Name</label>
|
||||
<input type="text" :name="'environment_names-' + i" :id="'environment_names-' + i" v-model="environment.name" placeholder="e.g. Development, Staging, Production">
|
||||
</div>
|
||||
<button v-on:click="removeEnvironment(i)" v-if="environments.length > 1" type="button" class='project-edit__env-list-item__remover'>
|
||||
{{ Icon('trash') }}
|
||||
<span>Remove</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="block-list__footer">
|
||||
<button v-on:click="addEnvironment" class="icon-link" tabindex="0" type="button">Add another environment</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-group">
|
||||
<button class="usa-button usa-button-primary" tabindex="0" type="submit">Create Project</button>
|
||||
</div>
|
||||
</form>
|
||||
</new-project>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -24,3 +24,34 @@ def test_workspace_owner_can_view_environments():
|
||||
project = Projects.get(owner, workspace, workspace.projects[0].id)
|
||||
|
||||
assert len(project.environments) == 2
|
||||
|
||||
|
||||
def test_can_only_update_name_and_description():
|
||||
owner = UserFactory.create()
|
||||
workspace = WorkspaceFactory.create(
|
||||
owner=owner,
|
||||
projects=[
|
||||
{
|
||||
"name": "Project 1",
|
||||
"description": "a project",
|
||||
"environments": [{"name": "dev"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
project = Projects.get(owner, workspace, workspace.projects[0].id)
|
||||
env_name = project.environments[0].name
|
||||
Projects.update(
|
||||
owner,
|
||||
workspace,
|
||||
project,
|
||||
{
|
||||
"name": "New Name",
|
||||
"description": "a new project",
|
||||
"environment_name": "prod",
|
||||
},
|
||||
)
|
||||
|
||||
assert project.name == "New Name"
|
||||
assert project.description == "a new project"
|
||||
assert len(project.environments) == 1
|
||||
assert project.environments[0].name == env_name
|
||||
|
@ -115,6 +115,66 @@ def test_view_edit_project(client, user_session):
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_user_with_permission_can_update_project(client, user_session):
|
||||
owner = UserFactory.create()
|
||||
workspace = WorkspaceFactory.create(
|
||||
owner=owner,
|
||||
projects=[
|
||||
{
|
||||
"name": "Awesome Project",
|
||||
"description": "It's really awesome!",
|
||||
"environments": [{"name": "dev"}, {"name": "prod"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
project = workspace.projects[0]
|
||||
user_session(owner)
|
||||
response = client.post(
|
||||
url_for(
|
||||
"workspaces.update_project",
|
||||
workspace_id=workspace.id,
|
||||
project_id=project.id,
|
||||
),
|
||||
data={"name": "Really Cool Project", "description": "A very cool project."},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert project.name == "Really Cool Project"
|
||||
assert project.description == "A very cool project."
|
||||
|
||||
|
||||
def test_user_without_permission_cannot_update_project(client, user_session):
|
||||
dev = UserFactory.create()
|
||||
owner = UserFactory.create()
|
||||
workspace = WorkspaceFactory.create(
|
||||
owner=owner,
|
||||
members=[{"user": dev, "role_name": "developer"}],
|
||||
projects=[
|
||||
{
|
||||
"name": "Great Project",
|
||||
"description": "Cool stuff happening here!",
|
||||
"environments": [{"name": "dev"}, {"name": "prod"}],
|
||||
}
|
||||
],
|
||||
)
|
||||
project = workspace.projects[0]
|
||||
user_session(dev)
|
||||
response = client.post(
|
||||
url_for(
|
||||
"workspaces.update_project",
|
||||
workspace_id=workspace.id,
|
||||
project_id=project.id,
|
||||
),
|
||||
data={"name": "New Name", "description": "A new description."},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
assert project.name == "Great Project"
|
||||
assert project.description == "Cool stuff happening here!"
|
||||
|
||||
|
||||
def test_create_member(client, user_session):
|
||||
owner = UserFactory.create()
|
||||
user = UserFactory.create()
|
||||
|
Loading…
x
Reference in New Issue
Block a user