Skip to content

Commit

Permalink
WIP for #1070
Browse files Browse the repository at this point in the history
  • Loading branch information
oharsta committed Nov 21, 2023
1 parent 542bb31 commit 3ff9340
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 38 deletions.
12 changes: 9 additions & 3 deletions client/src/components/redesign/ServiceConnectionRequests.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,27 @@ class ServiceConnectionRequests extends React.Component {
},
{
key: "collaboration__name",
class: isEmpty(service) ? "" : "has-service",
header: I18n.t("models.serviceConnectionRequests.name"),
mapper: serviceConnectionRequest => serviceConnectionRequest.collaboration.name
},
isEmpty(service) ? {
key: "service__name",
header: I18n.t("models.serviceConnectionRequests.serviceName"),
mapper: serviceConnectionRequest => serviceConnectionRequest.service.name
} : null,
{
key: "requester__name",
class: isEmpty(service) ? "" : "has-service",
header: I18n.t("models.serviceConnectionRequests.requester"),
mapper: serviceConnectionRequest => <UserColumn entity={{user:serviceConnectionRequest.requester}} currentUser={currentUser}/>
},
{
{
key: "user__schac_home_organisation",
header: I18n.t("models.users.institute"),
mapper: serviceConnectionRequest => <InstituteColumn entity={{user:serviceConnectionRequest.requester}} currentUser={currentUser}/>
},

]
].filter(tab => tab !== null)
return (
<div>
<Entities entities={serviceConnectionRequests}
Expand Down
19 changes: 16 additions & 3 deletions client/src/components/redesign/ServiceConnectionRequests.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,28 @@ table.serviceConnectionRequests {
thead {
th {
&.collaboration__name {
width: 40%;
width: 25%;
padding-left: 15px;

&.has-service {
width: 40%;
}
}

&.service__name {
width: 25%;
}

&.requester__name {
width: 30%;
width: 25%;

&.has-service {
width: 35%;
}
}

&.user__schac_home_organisation {
width: 30%;
width: 25%;
}

}
Expand Down
4 changes: 3 additions & 1 deletion client/src/components/redesign/Services.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ class Services extends React.Component {
return;
}
promise.then(services => {
services.forEach(s => s.connection_requests_count = s.service_connection_requests.length)
services.forEach(s => s.connection_requests_count = s.service_connection_requests
.filter(scr => !scr.pending_organisation_approval)
.length)
this.setState({services: services, loading: false})
});
}
Expand Down
30 changes: 15 additions & 15 deletions client/src/components/redesign/UsedServices.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,12 @@ class UsedServices extends React.Component {
};

serviceAllowedToConnect = (service, collaboration) => {
return service.automatic_connection_allowed ||
service.automatic_connection_allowed_organisations.some(org => org.id === collaboration.organisation.id);
const requiresApproval = collaboration.organisation.service_connection_requires_approval;
return !requiresApproval && (service.automatic_connection_allowed ||
service.automatic_connection_allowed_organisations.some(org => org.id === collaboration.organisation.id));
}

getServiceAction = service => {
const {collaboration} = this.props;
getServiceAction = (service, collaboration) => {
if (service.usedService && !service.connectionRequest &&
collaboration.organisation.services.some(s => s.id === service.id)) {
return null;
Expand Down Expand Up @@ -372,14 +372,14 @@ class UsedServices extends React.Component {
);
}

renderConnectedServices = (user, usedServices) => {
renderConnectedServices = (user, usedServices, collaboration) => {
return (
<div>
{isEmpty(usedServices) && <div className="no-services">
<p className={"margin"}>{I18n.t("models.collaboration.noServices")}
<a href={"/#"}
onClick={this.gotoConnectService}>{I18n.t("models.collaboration.connectFirstService")}</a>
</p>
<a href={"/#"}
onClick={this.gotoConnectService}>{I18n.t("models.collaboration.connectFirstService")}</a>
</p>
<NoServicesIcon/>
</div>
}
Expand All @@ -389,7 +389,7 @@ class UsedServices extends React.Component {
nameLinkAction={e => this.openService(service, e)}
status={this.getServiceStatus(service)}
message={this.getServiceMessage(service)}
ActionButton={this.getServiceAction(service)}
ActionButton={this.getServiceAction(service, collaboration)}
limitWidth={true}
launchLink={true}
user={user}
Expand All @@ -399,15 +399,15 @@ class UsedServices extends React.Component {
);
}

renderAvailableServices = (user, usedServices) => {
renderAvailableServices = (user, usedServices, collaboration) => {
return (
<div>
{usedServices.map(service =>
<ServiceCard service={service}
nameLinkAction={e => this.openService(service, e)}
status={this.getServiceStatus(service)}
message={this.getServiceStatus(service)}
ActionButton={this.getServiceAction(service)}
ActionButton={this.getServiceAction(service, collaboration)}
limitWidth={true}
launchLink={true}
user={user}
Expand All @@ -434,12 +434,12 @@ class UsedServices extends React.Component {
.filter(service => isEmpty(query) || service.name.toLowerCase().indexOf(query) > -1)
}

renderCurrentTab = (currentTab, usedServices, availableServices, query, user) => {
renderCurrentTab = (currentTab, usedServices, availableServices, query, user, collaboration) => {
switch (currentTab) {
case CONNECTIONS:
return this.renderConnectedServices(user, this.sortAndFilter(usedServices, query));
return this.renderConnectedServices(user, this.sortAndFilter(usedServices, query), collaboration);
case AVAILABLE:
return this.renderAvailableServices(user, this.sortAndFilter(availableServices, query));
return this.renderAvailableServices(user, this.sortAndFilter(availableServices, query), collaboration);
default:
throw new Error(`unknown-tab: ${currentTab}`);
}
Expand Down Expand Up @@ -510,7 +510,7 @@ class UsedServices extends React.Component {
{this.renderSearch(query)}
</div>

{this.renderCurrentTab(currentTab, usedServices, services, query, user)}
{this.renderCurrentTab(currentTab, usedServices, services, query, user, collaboration)}
</div>

</div>
Expand Down
3 changes: 2 additions & 1 deletion client/src/locale/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,8 @@ const en = {
edit: "Approve / decline",
details: "Service connection request made by {{name}} on {{date}} for collaboration {{collaborationName}}",
requester: "Requester",
name: "Collaboration"
name: "Collaboration",
serviceName: "Service"
},
serviceGroups: {
backToGroups: "Back to all service groups",
Expand Down
3 changes: 2 additions & 1 deletion client/src/locale/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,8 @@ const nl = {
edit: "Goed- / afkeuren",
details: "Dienst koppelverzoek gemaakt door {{name}} op {{date}} voor samenwerking {{collaborationName}}",
requester: "Aanvrager",
name: "Samenwerking"
name: "Samenwerking",
serviceName: "Dienst"
},
serviceGroups: {
backToGroups: "Terug naar alle dienstgroepen",
Expand Down
11 changes: 8 additions & 3 deletions server/api/service_connection_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def _service_connection_request_pending_organisation_approval(service_connection

service_connection_request.pending_organisation_approval = False
db.session.merge(service_connection_request)
_do_send_mail(collaboration, service, service_connection_request, False)
user = db.session.get(User, current_user_id())
_do_send_mail(collaboration, service, service_connection_request, user, False)
return {}, 201


def _do_send_mail(collaboration, service, service_connection_request, user, pending_organisation_approval):
Expand Down Expand Up @@ -73,13 +75,16 @@ def _do_service_connection_request(approved):
service_connection_request = ServiceConnectionRequest.query.filter(ServiceConnectionRequest.id == id).one()

pending_on_org = service_connection_request.pending_organisation_approval
if pending_on_org and approved:
service = service_connection_request.service
organisation = service_connection_request.collaboration.organisation
allowed = service.automatic_connection_allowed or organisation in service.automatic_connection_allowed_organisations
if not allowed and pending_on_org and approved:
return _service_connection_request_pending_organisation_approval(service_connection_request)

service = service_connection_request.service
collaboration = service_connection_request.collaboration

if not (is_service_admin(service.id) or is_application_admin()):
if not (is_service_admin(service.id) or is_application_admin() or is_organisation_admin(organisation.id)):
raise Forbidden(f"Not allowed to approve / decline service_connection_request for service {service.entity_id}")

if approved:
Expand Down
53 changes: 42 additions & 11 deletions server/test/api/test_service_connection_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
from server.db.db import db
from server.db.domain import Collaboration, Service, ServiceConnectionRequest
from server.test.abstract_test import AbstractTest
from server.test.seed import (service_connection_request_ssh_hash, co_research_name, service_wiki_name,
co_ai_computing_name, service_ssh_name, service_storage_name, service_cloud_name,
unihard_short_name)
from server.test.seed import service_connection_request_ssh_hash, co_research_name, service_wiki_name, \
co_ai_computing_name, service_ssh_name, service_storage_name, service_cloud_name


class TestServiceConnectionRequest(AbstractTest):
Expand Down Expand Up @@ -108,12 +107,11 @@ def test_existing_service_connection_request(self):
res = self.post("/api/service_connection_requests", body=data, with_basic_auth=False, response_status_code=400)
self.assertTrue("outstanding_service_connection_request" in res["message"])

def test_approve_service_connection_request(self):
def test_approve_service_connection_request_not_allowed(self):
service = self.find_entity_by_name(Service, service_ssh_name)
service.override_access_allowed_all_connections = 0
self.save_entity(service)

pre_services_count = len(self.find_entity_by_name(Collaboration, co_research_name).services)
request_id = ServiceConnectionRequest.query \
.filter(ServiceConnectionRequest.hash == service_connection_request_ssh_hash).one().id
# CO admin not allowed to approve
Expand All @@ -122,18 +120,50 @@ def test_approve_service_connection_request(self):
body={"id": request_id},
response_status_code=403)

def test_approve_service_connection_request_pending_org(self):
service = self.find_entity_by_name(Service, service_ssh_name)
service.override_access_allowed_all_connections = 0
self.save_entity(service)

service_connection_request = ServiceConnectionRequest.query.filter(
ServiceConnectionRequest.hash == service_connection_request_ssh_hash).one()
self.assertTrue(service_connection_request.pending_organisation_approval)
request_id = service_connection_request.id
with self.app.mail.record_messages() as outbox:
# Org admin is allowed to approve
self.login("urn:jane")
self.put("/api/service_connection_requests/approve",
body={"id": request_id})

service_connection_request = ServiceConnectionRequest.query.filter(
ServiceConnectionRequest.hash == service_connection_request_ssh_hash).one()
self.assertFalse(service_connection_request.pending_organisation_approval)
mail_msg = outbox[0]
self.assertTrue(f"Request for new service {service_ssh_name} "
f"connection to collaboration {co_research_name}" in mail_msg.subject)

def test_approve_service_connection(self):
service = self.find_entity_by_name(Service, service_ssh_name)
service.override_access_allowed_all_connections = 0
self.save_entity(service)

pre_services_count = len(self.find_entity_by_name(Collaboration, co_research_name).services)
service_connection_request = ServiceConnectionRequest.query.filter(
ServiceConnectionRequest.hash == service_connection_request_ssh_hash).one()
service_connection_request.pending_organisation_approval = False
self.save_entity(service_connection_request)
with self.app.mail.record_messages() as outbox:
# Service admin is allowed to approve
self.login("urn:betty")
self.put("/api/service_connection_requests/approve/",
body={"id": request_id})
body={"id": service_connection_request.id})
post_services_count = len(self.find_entity_by_name(Collaboration, co_research_name).services)

self.assertEqual(pre_services_count + 1, post_services_count)

mail_msg = outbox[0]
self.assertEqual(f"Service {service_ssh_name} connection request for collaboration {co_research_name} "
"has been accepted (local)", mail_msg.subject)
f"has been accepted (local)", mail_msg.subject)

def test_approve_service_connection_request_with_no_email_requester(self):
service = self.find_entity_by_name(Service, service_ssh_name)
Expand All @@ -144,6 +174,7 @@ def test_approve_service_connection_request_with_no_email_requester(self):
.filter(ServiceConnectionRequest.hash == service_connection_request_ssh_hash).one()
request_id = request.id
request.requester.email = None
request.pending_organisation_approval = False
self.save_entity(request.requester)
with self.app.mail.record_messages() as outbox:
# Service admin is allowed to approve
Expand Down Expand Up @@ -172,8 +203,8 @@ def test_deny_service_connection_request(self):
self.assertEqual(pre_services_count, post_services_count)

mail_msg = outbox[0]
self.assertEqual(f"Service {service_ssh_name} connection request for collaboration {co_research_name} "
"has been declined (local)", mail_msg.subject)
self.assertEqual(f"Service {service_ssh_name} connection request for collaboration "
f"{co_research_name} has been declined (local)", mail_msg.subject)

def test_all_service_request_connections_by_service(self):
storage_id = self.find_entity_by_name(Service, service_storage_name).id
Expand Down Expand Up @@ -201,6 +232,6 @@ def test_service_approve_with_service_groups(self):

collaboration = self.find_entity_by_name(Collaboration, co_ai_computing_name)

group = list(filter(lambda group: group.global_urn == f"{unihard_short_name}:ai_computing:wiki-wiki2",
collaboration.groups))[0]
groups = collaboration.groups
group = list(filter(lambda group: group.global_urn == "uniharderwijk:ai_computing:wiki-wiki2", groups))[0]
self.assertEqual(1, len(group.invitations))

0 comments on commit 3ff9340

Please sign in to comment.