project -> application everywhere

This commit is contained in:
dandds
2019-01-10 16:38:00 -05:00
parent 9ad3c45200
commit 3fc323d785
67 changed files with 644 additions and 609 deletions

View File

@@ -3,46 +3,50 @@ from atst.domain.authz import Authorization
from atst.domain.environments import Environments
from atst.domain.exceptions import NotFoundError
from atst.models.permissions import Permissions
from atst.models.project import Project
from atst.models.application import Application
from atst.models.environment import Environment
from atst.models.environment_role import EnvironmentRole
class Projects(object):
class Applications(object):
@classmethod
def create(cls, user, workspace, name, description, environment_names):
project = Project(workspace=workspace, name=name, description=description)
db.session.add(project)
application = Application(
workspace=workspace, name=name, description=description
)
db.session.add(application)
Environments.create_many(project, environment_names)
Environments.create_many(application, environment_names)
db.session.commit()
return project
return application
@classmethod
def get(cls, user, workspace, project_id):
# TODO: this should check permission for this particular project
def get(cls, user, workspace, application_id):
# TODO: this should check permission for this particular application
Authorization.check_workspace_permission(
user,
workspace,
Permissions.VIEW_APPLICATION_IN_WORKSPACE,
"view project in workspace",
"view application in workspace",
)
try:
project = db.session.query(Project).filter_by(id=project_id).one()
application = (
db.session.query(Application).filter_by(id=application_id).one()
)
except NoResultFound:
raise NotFoundError("project")
raise NotFoundError("application")
return project
return application
@classmethod
def for_user(self, user, workspace):
return (
db.session.query(Project)
db.session.query(Application)
.join(Environment)
.join(EnvironmentRole)
.filter(Project.workspace_id == workspace.id)
.filter(Application.workspace_id == workspace.id)
.filter(EnvironmentRole.user_id == user.id)
.all()
)
@@ -53,26 +57,26 @@ class Projects(object):
user,
workspace,
Permissions.VIEW_APPLICATION_IN_WORKSPACE,
"view project in workspace",
"view application in workspace",
)
try:
projects = (
db.session.query(Project).filter_by(workspace_id=workspace.id).all()
applications = (
db.session.query(Application).filter_by(workspace_id=workspace.id).all()
)
except NoResultFound:
raise NotFoundError("projects")
raise NotFoundError("applications")
return projects
return applications
@classmethod
def update(cls, user, workspace, project, new_data):
def update(cls, user, workspace, application, new_data):
if "name" in new_data:
project.name = new_data["name"]
application.name = new_data["name"]
if "description" in new_data:
project.description = new_data["description"]
application.description = new_data["description"]
db.session.add(project)
db.session.add(application)
db.session.commit()
return project
return application

View File

@@ -22,12 +22,12 @@ class MockEnvironment:
self.name = env_name
class MockProject:
def __init__(self, project_name, envs):
class MockApplication:
def __init__(self, application_name, envs):
def make_env(name):
return MockEnvironment("{}_{}".format(project_name, name), name)
return MockEnvironment("{}_{}".format(application_name, name), name)
self.name = project_name
self.name = application_name
self.environments = [make_env(env_name) for env_name in envs]
@@ -161,13 +161,13 @@ class MockReportingProvider(ReportingInterface):
REPORT_FIXTURE_MAP = {
"Aardvark": {
"cumulative": CUMULATIVE_BUDGET_AARDVARK,
"projects": [
MockProject("LC04", ["Integ", "PreProd", "Prod"]),
MockProject("SF18", ["Integ", "PreProd", "Prod"]),
MockProject("Canton", ["Prod"]),
MockProject("BD04", ["Integ", "PreProd"]),
MockProject("SCV18", ["Dev"]),
MockProject(
"applications": [
MockApplication("LC04", ["Integ", "PreProd", "Prod"]),
MockApplication("SF18", ["Integ", "PreProd", "Prod"]),
MockApplication("Canton", ["Prod"]),
MockApplication("BD04", ["Integ", "PreProd"]),
MockApplication("SCV18", ["Dev"]),
MockApplication(
"Crown",
[
"CR Portal Dev",
@@ -182,9 +182,9 @@ class MockReportingProvider(ReportingInterface):
},
"Beluga": {
"cumulative": CUMULATIVE_BUDGET_BELUGA,
"projects": [
MockProject("NP02", ["Integ", "PreProd", "NP02_Prod"]),
MockProject("FM", ["Integ", "Prod"]),
"applications": [
MockApplication("NP02", ["Integ", "PreProd", "NP02_Prod"]),
MockApplication("FM", ["Integ", "Prod"]),
],
"budget": 70000,
},
@@ -194,8 +194,8 @@ class MockReportingProvider(ReportingInterface):
return sum(
[
spend
for project in data
for env in project.environments
for application in data
for env in application.environments
for spend in self.MONTHLY_SPEND_BY_ENVIRONMENT[env.id].values()
]
)
@@ -210,31 +210,31 @@ class MockReportingProvider(ReportingInterface):
def get_total_spending(self, workspace):
if workspace.name in self.REPORT_FIXTURE_MAP:
return self._sum_monthly_spend(
self.REPORT_FIXTURE_MAP[workspace.name]["projects"]
self.REPORT_FIXTURE_MAP[workspace.name]["applications"]
)
return 0
def _rollup_project_totals(self, data):
project_totals = {}
for project, environments in data.items():
project_spend = [
def _rollup_application_totals(self, data):
application_totals = {}
for application, environments in data.items():
application_spend = [
(month, spend)
for env in environments.values()
if env
for month, spend in env.items()
]
project_totals[project] = {
application_totals[application] = {
month: sum([spend[1] for spend in spends])
for month, spends in groupby(sorted(project_spend), lambda x: x[0])
for month, spends in groupby(sorted(application_spend), lambda x: x[0])
}
return project_totals
return application_totals
def _rollup_workspace_totals(self, project_totals):
def _rollup_workspace_totals(self, application_totals):
monthly_spend = [
(month, spend)
for project in project_totals.values()
for month, spend in project.items()
for application in application_totals.values()
for month, spend in application.items()
]
workspace_totals = {}
for month, spends in groupby(sorted(monthly_spend), lambda m: m[0]):
@@ -254,39 +254,39 @@ class MockReportingProvider(ReportingInterface):
return self.MONTHLY_SPEND_BY_ENVIRONMENT.get(environment_id, {})
def monthly_totals(self, workspace):
"""Return month totals rolled up by environment, project, and workspace.
"""Return month totals rolled up by environment, application, and workspace.
Data should returned with three top level keys, "workspace", "projects",
Data should returned with three top level keys, "workspace", "applications",
and "environments".
The "projects" key will have budget data per month for each project,
The "applications" key will have budget data per month for each application,
The "environments" key will have budget data for each environment.
The "workspace" key will be total monthly spending for the workspace.
For example:
{
"environments": { "X-Wing": { "Prod": { "01/2018": 75.42 } } },
"projects": { "X-Wing": { "01/2018": 75.42 } },
"applications": { "X-Wing": { "01/2018": 75.42 } },
"workspace": { "01/2018": 75.42 },
}
"""
projects = workspace.projects
applications = workspace.applications
if workspace.name in self.REPORT_FIXTURE_MAP:
projects = self.REPORT_FIXTURE_MAP[workspace.name]["projects"]
applications = self.REPORT_FIXTURE_MAP[workspace.name]["applications"]
environments = {
project.name: {
application.name: {
env.name: self.monthly_totals_for_environment(env.id)
for env in project.environments
for env in application.environments
}
for project in projects
for application in applications
}
project_totals = self._rollup_project_totals(environments)
workspace_totals = self._rollup_workspace_totals(project_totals)
application_totals = self._rollup_application_totals(environments)
workspace_totals = self._rollup_workspace_totals(application_totals)
return {
"environments": environments,
"projects": project_totals,
"applications": application_totals,
"workspace": workspace_totals,
}

View File

@@ -4,7 +4,7 @@ from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models.environment import Environment
from atst.models.environment_role import EnvironmentRole
from atst.models.project import Project
from atst.models.application import Application
from atst.models.permissions import Permissions
from atst.domain.authz import Authorization
from atst.domain.environment_roles import EnvironmentRoles
@@ -14,18 +14,18 @@ from .exceptions import NotFoundError
class Environments(object):
@classmethod
def create(cls, project, name):
environment = Environment(project=project, name=name)
def create(cls, application, name):
environment = Environment(application=application, name=name)
environment.cloud_id = app.csp.cloud.create_application(environment.name)
db.session.add(environment)
db.session.commit()
return environment
@classmethod
def create_many(cls, project, names):
def create_many(cls, application, names):
environments = []
for name in names:
environment = Environments.create(project, name)
environment = Environments.create(application, name)
environments.append(environment)
db.session.add_all(environments)
@@ -40,13 +40,13 @@ class Environments(object):
return environment
@classmethod
def for_user(cls, user, project):
def for_user(cls, user, application):
return (
db.session.query(Environment)
.join(EnvironmentRole)
.join(Project)
.join(Application)
.filter(EnvironmentRole.user_id == user.id)
.filter(Environment.project_id == project.id)
.filter(Environment.project_id == application.id)
.all()
)

View File

@@ -58,7 +58,7 @@ WORKSPACE_ROLES = [
{
"name": "owner",
"display_name": "Workspace Owner",
"description": "Adds, edits, deactivates access to all projects, environments, and members. Views budget reports. Initiates and edits JEDI Cloud requests.",
"description": "Adds, edits, deactivates access to all applications, environments, and members. Views budget reports. Initiates and edits JEDI Cloud requests.",
"permissions": [
Permissions.REQUEST_JEDI_WORKSPACE,
Permissions.VIEW_ORIGINAL_JEDI_REQEUST,
@@ -94,7 +94,7 @@ WORKSPACE_ROLES = [
{
"name": "admin",
"display_name": "Administrator",
"description": "Adds and edits projects, environments, members, but cannot deactivate. Cannot view budget reports or JEDI Cloud requests.",
"description": "Adds and edits applications, environments, members, but cannot deactivate. Cannot view budget reports or JEDI Cloud requests.",
"permissions": [
Permissions.VIEW_USAGE_REPORT,
Permissions.ADD_AND_ASSIGN_CSP_ROLES,
@@ -125,13 +125,13 @@ WORKSPACE_ROLES = [
{
"name": "developer",
"display_name": "Developer",
"description": "Views only the projects and environments they are granted access to. Can also view members associated with each environment.",
"description": "Views only the applications and environments they are granted access to. Can also view members associated with each environment.",
"permissions": [Permissions.VIEW_USAGE_REPORT, Permissions.VIEW_WORKSPACE],
},
{
"name": "billing_auditor",
"display_name": "Billing Auditor",
"description": "Views only the projects and environments they are granted access to. Can also view budgets and reports associated with the workspace.",
"description": "Views only the applications and environments they are granted access to. Can also view budgets and reports associated with the workspace.",
"permissions": [
Permissions.VIEW_USAGE_REPORT,
Permissions.VIEW_USAGE_DOLLARS,
@@ -140,7 +140,7 @@ WORKSPACE_ROLES = [
},
{
"name": "security_auditor",
"description": "Views only the projects and environments they are granted access to. Can also view activity logs.",
"description": "Views only the applications and environments they are granted access to. Can also view activity logs.",
"display_name": "Security Auditor",
"permissions": [
Permissions.VIEW_ASSIGNED_ATAT_ROLE_CONFIGURATIONS,

View File

@@ -1,6 +1,6 @@
from atst.domain.authz import Authorization
from atst.models.permissions import Permissions
from atst.domain.projects import Projects
from atst.domain.applications import Applications
from atst.domain.environments import Environments
@@ -24,25 +24,27 @@ class ScopedResource(object):
class ScopedWorkspace(ScopedResource):
"""
An object that obeys the same API as a Workspace, but with the added
functionality that it only returns sub-resources (projects and environments)
functionality that it only returns sub-resources (applications and environments)
that the given user is allowed to see.
"""
@property
def projects(self):
can_view_all_projects = Authorization.has_workspace_permission(
def applications(self):
can_view_all_applications = Authorization.has_workspace_permission(
self.user, self.resource, Permissions.VIEW_APPLICATION_IN_WORKSPACE
)
if can_view_all_projects:
projects = self.resource.projects
if can_view_all_applications:
applications = self.resource.applications
else:
projects = Projects.for_user(self.user, self.resource)
applications = Applications.for_user(self.user, self.resource)
return [ScopedProject(self.user, project) for project in projects]
return [
ScopedApplication(self.user, application) for application in applications
]
class ScopedProject(ScopedResource):
class ScopedApplication(ScopedResource):
"""
An object that obeys the same API as a Workspace, but with the added
functionality that it only returns sub-resources (environments)

View File

@@ -44,10 +44,10 @@ class Workspaces(object):
return ScopedWorkspace(user, workspace)
@classmethod
def get_for_update_projects(cls, user, workspace_id):
def get_for_update_applications(cls, user, workspace_id):
workspace = WorkspacesQuery.get(workspace_id)
Authorization.check_workspace_permission(
user, workspace, Permissions.ADD_APPLICATION_IN_WORKSPACE, "add project"
user, workspace, Permissions.ADD_APPLICATION_IN_WORKSPACE, "add application"
)
return workspace

View File

@@ -5,29 +5,29 @@ from atst.forms.validators import ListItemRequired, ListItemsUnique
from atst.utils.localization import translate
class ProjectForm(FlaskForm):
class ApplicationForm(FlaskForm):
name = StringField(
label=translate("forms.project.name_label"), validators=[Required()]
label=translate("forms.application.name_label"), validators=[Required()]
)
description = TextAreaField(
label=translate("forms.project.description_label"), validators=[Required()]
label=translate("forms.application.description_label"), validators=[Required()]
)
class NewProjectForm(ProjectForm):
class NewApplicationForm(ApplicationForm):
EMPTY_ENVIRONMENT_NAMES = ["", None]
environment_names = FieldList(
StringField(label=translate("forms.project.environment_names_label")),
StringField(label=translate("forms.application.environment_names_label")),
validators=[
ListItemRequired(
message=translate(
"forms.project.environment_names_required_validation_message"
"forms.application.environment_names_required_validation_message"
)
),
ListItemsUnique(
message=translate(
"forms.project.environment_names_unique_validation_message"
"forms.application.environment_names_unique_validation_message"
)
),
],

View File

@@ -6,8 +6,8 @@ SERVICE_BRANCHES = [
("Army and Air Force Exchange Service", "Army and Air Force Exchange Service"),
("Army, Department of the", "Army, Department of the"),
(
"Defense Advanced Research Projects Agency",
"Defense Advanced Research Projects Agency",
"Defense Advanced Research Applications Agency",
"Defense Advanced Research Applications Agency",
),
("Defense Commissary Agency", "Defense Commissary Agency"),
("Defense Contract Audit Agency", "Defense Contract Audit Agency"),
@@ -137,7 +137,7 @@ ENVIRONMENT_ROLES = [
"billing_administrator",
{
"name": "Billing Administrator",
"description": "Views cloud resource usage, budget reports, and invoices; Tracks budgets, including spend reports, cost planning and projections, and sets limits based on cloud service usage.",
"description": "Views cloud resource usage, budget reports, and invoices; Tracks budgets, including spend reports, cost planning and applicationions, and sets limits based on cloud service usage.",
},
),
(
@@ -162,7 +162,7 @@ ENVIRONMENT_ROLES = [
ENV_ROLE_MODAL_DESCRIPTION = {
"header": "Assign Environment Role",
"body": "An environment role determines the permissions a member of the workspace assumes when using the JEDI Cloud.<br/><br/>A member may have different environment roles across different projects. A member can only have one assigned environment role in a given environment.",
"body": "An environment role determines the permissions a member of the workspace assumes when using the JEDI Cloud.<br/><br/>A member may have different environment roles across different applications. A member can only have one assigned environment role in a given environment.",
}
FUNDING_TYPES = [
@@ -210,7 +210,7 @@ TEAM_EXPERIENCE = [
("built_3", "Built or Migrated 3-5 applications"),
(
"built_many",
"Built or migrated many applications, or consulted on several such projects",
"Built or migrated many applications, or consulted on several such applications",
),
]

View File

@@ -8,7 +8,7 @@ from .data import WORKSPACE_ROLES
class EditMemberForm(FlaskForm):
# This form also accepts a field for each environment in each project
# This form also accepts a field for each environment in each application
# that the user is a member of
workspace_role = SelectField(

View File

@@ -11,7 +11,7 @@ from .workspace_role import WorkspaceRole
from .pe_number import PENumber
from .legacy_task_order import LegacyTaskOrder
from .workspace import Workspace
from .project import Project
from .application import Application
from .environment import Environment
from .attachment import Attachment
from .request_revision import RequestRevision

View File

@@ -6,7 +6,7 @@ from atst.models.types import Id
from atst.models import mixins
class Project(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
class Application(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
__tablename__ = "projects"
id = Id()
@@ -15,13 +15,13 @@ class Project(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
workspace_id = Column(ForeignKey("workspaces.id"), nullable=False)
workspace = relationship("Workspace")
environments = relationship("Environment", back_populates="project")
environments = relationship("Environment", back_populates="application")
@property
def displayname(self):
return self.name
def __repr__(self): # pragma: no cover
return "<Project(name='{}', description='{}', workspace='{}', id='{}')>".format(
return "<Application(name='{}', description='{}', workspace='{}', id='{}')>".format(
self.name, self.description, self.workspace.name, self.id
)

View File

@@ -13,7 +13,7 @@ class Environment(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
name = Column(String, nullable=False)
project_id = Column(ForeignKey("projects.id"), nullable=False)
project = relationship("Project")
application = relationship("Application")
cloud_id = Column(String)
@@ -31,16 +31,16 @@ class Environment(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
@property
def workspace(self):
return self.project.workspace
return self.application.workspace
def auditable_workspace_id(self):
return self.project.workspace_id
return self.application.workspace_id
def __repr__(self):
return "<Environment(name='{}', num_users='{}', project='{}', workspace='{}', id='{}')>".format(
return "<Environment(name='{}', num_users='{}', application='{}', workspace='{}', id='{}')>".format(
self.name,
self.num_users,
self.project.name,
self.project.workspace.name,
self.application.name,
self.application.workspace.name,
self.id,
)

View File

@@ -45,10 +45,10 @@ class EnvironmentRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
"role": self.role,
"environment": self.environment.displayname,
"environment_id": str(self.environment_id),
"project": self.environment.project.name,
"project_id": str(self.environment.project_id),
"workspace": self.environment.project.workspace.name,
"workspace_id": str(self.environment.project.workspace.id),
"application": self.environment.application.name,
"application_id": str(self.environment.project_id),
"workspace": self.environment.application.workspace.name,
"workspace_id": str(self.environment.application.workspace.id),
}

View File

@@ -47,7 +47,7 @@ class TaskOrder(Base, mixins.TimestampsMixin):
defense_component = Column(String) # Department of Defense Component
app_migration = Column(String) # App Migration
native_apps = Column(String) # Native Apps
complexity = Column(ARRAY(String)) # Project Complexity
complexity = Column(ARRAY(String)) # Application Complexity
complexity_other = Column(String)
dev_team = Column(ARRAY(String)) # Development Team
dev_team_other = Column(String)

View File

@@ -14,7 +14,7 @@ class Workspace(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
id = types.Id()
name = Column(String)
request_id = Column(ForeignKey("requests.id"), nullable=True)
projects = relationship("Project", back_populates="workspace")
applications = relationship("Application", back_populates="workspace")
roles = relationship("WorkspaceRole")
task_orders = relationship("TaskOrder")
@@ -54,7 +54,7 @@ class Workspace(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
@property
def all_environments(self):
return list(chain.from_iterable(p.environments for p in self.projects))
return list(chain.from_iterable(p.environments for p in self.applications))
def auditable_workspace_id(self):
return self.id

View File

@@ -8,7 +8,7 @@ from .types import Id
from atst.database import db
from atst.models.environment_role import EnvironmentRole
from atst.models.project import Project
from atst.models.application import Application
from atst.models.environment import Environment
from atst.models.role import Role
@@ -126,9 +126,9 @@ class WorkspaceRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
return (
db.session.query(EnvironmentRole)
.join(EnvironmentRole.environment)
.join(Environment.project)
.join(Project.workspace)
.filter(Project.workspace_id == self.workspace_id)
.join(Environment.application)
.join(Application.workspace)
.filter(Application.workspace_id == self.workspace_id)
.filter(EnvironmentRole.user_id == self.user_id)
.count()
)
@@ -138,9 +138,9 @@ class WorkspaceRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
return (
db.session.query(EnvironmentRole)
.join(EnvironmentRole.environment)
.join(Environment.project)
.join(Project.workspace)
.filter(Project.workspace_id == self.workspace_id)
.join(Environment.application)
.join(Application.workspace)
.filter(Application.workspace_id == self.workspace_id)
.filter(EnvironmentRole.user_id == self.user_id)
.all()
)

View File

@@ -67,7 +67,7 @@ def home():
)
else:
return redirect(
url_for("workspaces.workspace_projects", workspace_id=workspace_id)
url_for("workspaces.workspace_applications", workspace_id=workspace_id)
)
else:
return redirect(url_for("workspaces.workspaces"))

View File

@@ -250,7 +250,9 @@ def update_financial_verification(request_id):
if updated_request.legacy_task_order.verified:
workspace = Requests.auto_approve_and_create_workspace(updated_request)
flash("new_workspace")
return redirect(url_for("workspaces.new_project", workspace_id=workspace.id))
return redirect(
url_for("workspaces.new_application", workspace_id=workspace.id)
)
else:
return redirect(url_for("requests.requests_index", modal="pendingCCPOApproval"))

View File

@@ -66,7 +66,7 @@ class RequestsIndex(object):
def _workspace_link_for_request(self, request):
if request.is_approved:
return url_for(
"workspaces.workspace_projects", workspace_id=request.workspace.id
"workspaces.workspace_applications", workspace_id=request.workspace.id
)
else:
return None

View File

@@ -3,7 +3,7 @@ from flask import Blueprint, request as http_request, g, render_template
workspaces_bp = Blueprint("workspaces", __name__)
from . import index
from . import projects
from . import applications
from . import members
from . import invitations
from . import task_orders

View File

@@ -0,0 +1,102 @@
from flask import (
current_app as app,
g,
redirect,
render_template,
request as http_request,
url_for,
)
from . import workspaces_bp
from atst.domain.environment_roles import EnvironmentRoles
from atst.domain.exceptions import UnauthorizedError
from atst.domain.applications import Applications
from atst.domain.workspaces import Workspaces
from atst.forms.application import NewApplicationForm, ApplicationForm
@workspaces_bp.route("/workspaces/<workspace_id>/applications")
def workspace_applications(workspace_id):
workspace = Workspaces.get(g.current_user, workspace_id)
return render_template("workspaces/applications/index.html", workspace=workspace)
@workspaces_bp.route("/workspaces/<workspace_id>/applications/new")
def new_application(workspace_id):
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
form = NewApplicationForm()
return render_template(
"workspaces/applications/new.html", workspace=workspace, form=form
)
@workspaces_bp.route("/workspaces/<workspace_id>/applications/new", methods=["POST"])
def create_application(workspace_id):
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
form = NewApplicationForm(http_request.form)
if form.validate():
application_data = form.data
Applications.create(
g.current_user,
workspace,
application_data["name"],
application_data["description"],
application_data["environment_names"],
)
return redirect(
url_for("workspaces.workspace_applications", workspace_id=workspace.id)
)
else:
return render_template(
"workspaces/applications/new.html", workspace=workspace, form=form
)
@workspaces_bp.route("/workspaces/<workspace_id>/applications/<application_id>/edit")
def edit_application(workspace_id, application_id):
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
application = Applications.get(g.current_user, workspace, application_id)
form = ApplicationForm(name=application.name, description=application.description)
return render_template(
"workspaces/applications/edit.html",
workspace=workspace,
application=application,
form=form,
)
@workspaces_bp.route(
"/workspaces/<workspace_id>/applications/<application_id>/edit", methods=["POST"]
)
def update_application(workspace_id, application_id):
workspace = Workspaces.get_for_update_applications(g.current_user, workspace_id)
application = Applications.get(g.current_user, workspace, application_id)
form = ApplicationForm(http_request.form)
if form.validate():
application_data = form.data
Applications.update(g.current_user, workspace, application, application_data)
return redirect(
url_for("workspaces.workspace_applications", workspace_id=workspace.id)
)
else:
return render_template(
"workspaces/applications/edit.html",
workspace=workspace,
application=application,
form=form,
)
@workspaces_bp.route("/workspaces/<workspace_id>/environments/<environment_id>/access")
def access_environment(workspace_id, environment_id):
env_role = EnvironmentRoles.get(g.current_user.id, environment_id)
if not env_role:
raise UnauthorizedError(
g.current_user, "access environment {}".format(environment_id)
)
else:
token = app.csp.cloud.get_access_token(env_role)
return redirect(url_for("atst.csp_environment_access", token=token))

View File

@@ -32,7 +32,7 @@ def edit_workspace(workspace_id):
if form.validate():
Workspaces.update(workspace, form.data)
return redirect(
url_for("workspaces.workspace_projects", workspace_id=workspace.id)
url_for("workspaces.workspace_applications", workspace_id=workspace.id)
)
else:
return render_template("workspaces/edit.html", form=form, workspace=workspace)
@@ -40,7 +40,9 @@ def edit_workspace(workspace_id):
@workspaces_bp.route("/workspaces/<workspace_id>")
def show_workspace(workspace_id):
return redirect(url_for("workspaces.workspace_projects", workspace_id=workspace_id))
return redirect(
url_for("workspaces.workspace_applications", workspace_id=workspace_id)
)
@workspaces_bp.route("/workspaces/<workspace_id>/reports")

View File

@@ -4,7 +4,7 @@ from flask import render_template, request as http_request, g, redirect, url_for
from . import workspaces_bp
from atst.domain.exceptions import AlreadyExistsError
from atst.domain.projects import Projects
from atst.domain.applications import Applications
from atst.domain.workspaces import Workspaces
from atst.domain.workspace_roles import WorkspaceRoles, MEMBER_STATUS_CHOICES
from atst.domain.environments import Environments
@@ -101,7 +101,7 @@ def view_member(workspace_id, member_id):
"edit this workspace user",
)
member = WorkspaceRoles.get(workspace_id, member_id)
projects = Projects.get_all(g.current_user, member, workspace)
applications = Applications.get_all(g.current_user, member, workspace)
form = EditMemberForm(workspace_role=member.role_name)
editable = g.current_user == member.user
can_revoke_access = Workspaces.can_revoke_access_for(workspace, member)
@@ -113,7 +113,7 @@ def view_member(workspace_id, member_id):
"workspaces/members/edit.html",
workspace=workspace,
member=member,
projects=projects,
applications=applications,
form=form,
choices=ENVIRONMENT_ROLES,
env_role_modal_description=ENV_ROLE_MODAL_DESCRIPTION,

View File

@@ -1,99 +0,0 @@
from flask import (
current_app as app,
g,
redirect,
render_template,
request as http_request,
url_for,
)
from . import workspaces_bp
from atst.domain.environment_roles import EnvironmentRoles
from atst.domain.exceptions import UnauthorizedError
from atst.domain.projects import Projects
from atst.domain.workspaces import Workspaces
from atst.forms.project import NewProjectForm, ProjectForm
@workspaces_bp.route("/workspaces/<workspace_id>/projects")
def workspace_projects(workspace_id):
workspace = Workspaces.get(g.current_user, workspace_id)
return render_template("workspaces/projects/index.html", workspace=workspace)
@workspaces_bp.route("/workspaces/<workspace_id>/projects/new")
def new_project(workspace_id):
workspace = Workspaces.get_for_update_projects(g.current_user, workspace_id)
form = NewProjectForm()
return render_template(
"workspaces/projects/new.html", workspace=workspace, form=form
)
@workspaces_bp.route("/workspaces/<workspace_id>/projects/new", methods=["POST"])
def create_project(workspace_id):
workspace = Workspaces.get_for_update_projects(g.current_user, workspace_id)
form = NewProjectForm(http_request.form)
if form.validate():
project_data = form.data
Projects.create(
g.current_user,
workspace,
project_data["name"],
project_data["description"],
project_data["environment_names"],
)
return redirect(
url_for("workspaces.workspace_projects", workspace_id=workspace.id)
)
else:
return render_template(
"workspaces/projects/new.html", workspace=workspace, form=form
)
@workspaces_bp.route("/workspaces/<workspace_id>/projects/<project_id>/edit")
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 = ProjectForm(name=project.name, description=project.description)
return render_template(
"workspaces/projects/edit.html", workspace=workspace, project=project, form=form
)
@workspaces_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,
)
@workspaces_bp.route("/workspaces/<workspace_id>/environments/<environment_id>/access")
def access_environment(workspace_id, environment_id):
env_role = EnvironmentRoles.get(g.current_user.id, environment_id)
if not env_role:
raise UnauthorizedError(
g.current_user, "access environment {}".format(environment_id)
)
else:
token = app.csp.cloud.get_access_token(env_role)
return redirect(url_for("atst.csp_environment_access", token=token))