project -> application everywhere
This commit is contained in:
@@ -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
|
@@ -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,
|
||||
}
|
||||
|
||||
|
@@ -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()
|
||||
)
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
)
|
||||
),
|
||||
],
|
@@ -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",
|
||||
),
|
||||
]
|
||||
|
||||
|
@@ -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(
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
)
|
@@ -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,
|
||||
)
|
||||
|
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
)
|
||||
|
@@ -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"))
|
||||
|
@@ -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"))
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
102
atst/routes/workspaces/applications.py
Normal file
102
atst/routes/workspaces/applications.py
Normal 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))
|
@@ -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")
|
||||
|
@@ -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,
|
||||
|
@@ -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))
|
Reference in New Issue
Block a user