Skip to content

Commit

Permalink
UI: New attribute details view (#369)
Browse files Browse the repository at this point in the history
  • Loading branch information
KWMORALE committed May 13, 2021
1 parent a50087c commit 85b2a39
Show file tree
Hide file tree
Showing 8 changed files with 423 additions and 309 deletions.
87 changes: 82 additions & 5 deletions mwdb/resources/metakey.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@
from mwdb.schema.metakey import (
MetakeyDefinitionItemRequestArgsSchema,
MetakeyDefinitionItemRequestBodySchema,
MetakeyDefinitionItemResponseSchema,
MetakeyDefinitionListResponseSchema,
MetakeyDefinitionManageItemResponseSchema,
MetakeyDefinitionManageListResponseSchema,
MetakeyItemRequestSchema,
MetakeyKeySchema,
MetakeyListRequestSchema,
MetakeyListResponseSchema,
MetakeyPermissionSetRequestArgsSchema,
MetakeyPermissionSetRequestBodySchema,
MetakeyUpdateRequestSchema,
)

from . import (
access_object,
load_schema,
loads_schema,
logger,
requires_authorization,
requires_capabilities,
)
Expand Down Expand Up @@ -340,12 +344,12 @@ def get(self, key):

@requires_authorization
@requires_capabilities(Capabilities.managing_attributes)
def put(self, key):
def post(self, key):
"""
---
summary: Create/update attribute key
summary: Create attribute key
description: |
Creates or updates attribute key definition.
Creates attribute key definition.
Requires `managing_attributes` capability.
security:
Expand All @@ -368,7 +372,7 @@ def put(self, key):
description: When metakey definition is successfully added
content:
application/json:
schema: MetakeyDefinitionManageItemResponseSchema
schema: MetakeyDefinitionItemResponseSchema
400:
description: |
When one of attribute definition fields is missing or incorrect.
Expand All @@ -391,9 +395,82 @@ def put(self, key):
metakey_definition = db.session.merge(metakey_definition)
db.session.commit()

schema = MetakeyDefinitionManageItemResponseSchema()
schema = MetakeyDefinitionItemResponseSchema()
return schema.dump(metakey_definition)

@requires_authorization
@requires_capabilities(Capabilities.managing_attributes)
def put(self, key):
"""
---
summary: Update attribute key
description: |
Update attribute key definition.
Requires `managing_attributes` capability.
security:
- bearerAuth: []
tags:
- attribute
parameters:
- in: path
name: key
schema:
type: string
description: Attribute key
requestBody:
description: Attribute definition to update
content:
application/json:
schema: MetakeyUpdateRequestSchema
responses:
200:
description: When metakey definition is successfully updated
content:
application/json:
schema: MetakeyDefinitionItemResponseSchema
400:
description: |
When one of attribute definition fields is missing or incorrect.
403:
description: When user doesn't have `managing_attributes` capability.
404:
description: When metakey doesn't exist.
"""
schema = MetakeyUpdateRequestSchema()
obj = loads_schema(request.get_data(as_text=True), schema)

metakey_obj = load_schema({"key": key}, MetakeyKeySchema())
metakey = (
db.session.query(MetakeyDefinition)
.filter(MetakeyDefinition.key == metakey_obj["key"])
.first()
)
if metakey is None:
raise NotFound("No such metakey")

label = obj["label"]
if label is not None:
metakey.label = label

description = obj["description"]
if description is not None:
metakey.description = description

url_template = obj["template"]
if url_template is not None:
metakey.url_template = url_template

hidden = obj["hidden"]
if hidden is not None:
metakey.hidden = obj["hidden"]

db.session.commit()
logger.info("Metakey updated", extra=obj)

schema = MetakeyDefinitionItemResponseSchema()
return schema.dump(metakey)

@requires_authorization
@requires_capabilities(Capabilities.managing_attributes)
def delete(self, key):
Expand Down
7 changes: 7 additions & 0 deletions mwdb/schema/metakey.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ class MetakeyDefinitionItemRequestArgsSchema(MetakeyKeySchema):
pass


class MetakeyUpdateRequestSchema(Schema):
label = fields.Str(missing=None)
description = fields.Str(missing=None)
template = fields.Str(missing=None)
hidden = fields.Boolean(missing=None)


class MetakeyDefinitionItemRequestBodySchema(Schema):
template = fields.Str(attribute="url_template", required=True, allow_none=False)
label = fields.Str(required=True, allow_none=False)
Expand Down
15 changes: 14 additions & 1 deletion mwdb/web/src/commons/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ function removeMetakeyDefinition(key) {
}

function addMetakeyDefinition(key, label, description, template, hidden) {
return axios.put(`/meta/manage/${key}`, {
return axios.post(`/meta/manage/${key}`, {
key,
label,
description,
Expand All @@ -331,6 +331,18 @@ function addMetakeyDefinition(key, label, description, template, hidden) {
});
}

function updateMetakeyDefinition(
key,
{ label, description, template, hidden }
) {
return axios.put(`/meta/manage/${key}`, {
label,
description,
template,
hidden,
});
}

function setMetakeyPermission(key, group_name, can_read, can_set) {
return axios.put(`/meta/manage/${key}/permissions/${group_name}`, {
group_name,
Expand Down Expand Up @@ -526,6 +538,7 @@ export default {
getMetakeyDefinition,
removeMetakeyDefinition,
addMetakeyDefinition,
updateMetakeyDefinition,
setMetakeyPermission,
deleteMetakeyPermission,
downloadFile,
Expand Down
9 changes: 6 additions & 3 deletions mwdb/web/src/components/Settings/SettingsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import ShowGroups from "./Views/ShowGroups";
import ShowUsers from "./Views/ShowUsers";
import ShowPendingUsers from "./Views/ShowPendingUsers";
import ManageAttributes from "./Views/ManageAttributes";
import AttributeUpdate from "./Views/AttributeUpdate";
import UserCreate from "./Views/UserCreate";
import GroupRegister from "./Views/GroupRegister";
import AttributeDefine from "./Views/AttributeDefine";
import UserView from "./Views/UserView";
import AccessControl from "./Views/AccessControl";
import GroupView from "./Views/GroupView";
import AttributeView from "./Views/AttributeView";
import SettingsOverview from "./Views/SettingsOverview";

function SettingsNav() {
Expand Down Expand Up @@ -190,9 +190,12 @@ export default function SettingsView(props) {
</AttributeRoute>
<AttributeRoute
exact
path="/admin/attribute/:metakey"
path={[
"/admin/attribute/:metakey",
"/admin/attribute/:metakey/permissions",
]}
>
<AttributeUpdate />
<AttributeView />
</AttributeRoute>
</Switch>
</div>
Expand Down
137 changes: 137 additions & 0 deletions mwdb/web/src/components/Settings/Views/AttributeDetails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { useState } from "react";
import { Link, useHistory } from "react-router-dom";
import api from "@mwdb-web/commons/api";
import {
getErrorMessage,
ConfirmationModal,
EditableItem,
} from "@mwdb-web/commons/ui";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

function AttributeItem(props) {
let value = props.value ? props.value : "never";
return (
<tr className="d-flex">
<th className="col-3">{props.label}</th>
<td className="col-9">{props.children || value}</td>
</tr>
);
}

export function AttributeDetails({ attribute, getAttribute }) {
const history = useHistory();
const [isDeleteModalOpen, setDeleteModalOpen] = useState(false);
const [isDeleteModalDisabled, setDeleteModalDisabled] = useState(false);

async function handleSubmit(newValue) {
if (newValue.hidden === "Enabled") {
newValue.hidden = true;
} else if (newValue.hidden === "Disabled") {
newValue.hidden = false;
}
try {
await api.updateMetakeyDefinition(attribute.key, newValue);
} catch (error) {
history.push({
pathname: `/admin/attribute/${attribute.key}`,
state: { error: getErrorMessage(error) },
});
} finally {
getAttribute();
}
}

async function handleRemoveAttribute() {
try {
setDeleteModalDisabled(true);
await api.removeMetakeyDefinition(attribute.key);
history.push("/admin/attributes");
} catch (error) {
history.push({
pathname: `/admin/attribute/${attribute.key}`,
state: { error: getErrorMessage(error) },
});
}
}

return (
<div className="container">
<table className="table table-striped table-bordered wrap-table">
<tbody>
<AttributeItem label="Label">
<EditableItem
name="label"
defaultValue={attribute.label}
onSubmit={handleSubmit}
/>
</AttributeItem>
<AttributeItem label="Description">
<EditableItem
name="description"
defaultValue={attribute.description}
onSubmit={handleSubmit}
/>
</AttributeItem>
<AttributeItem label="URL Template">
<EditableItem
name="template"
defaultValue={attribute.template}
onSubmit={handleSubmit}
/>
</AttributeItem>
<AttributeItem label="Hidden">
<EditableItem
name="hidden"
defaultValue={
attribute.hidden ? "Enabled" : "Disabled"
}
onSubmit={handleSubmit}
badge
selective
>
<option value="Enabled">Enabled</option>
<option value="Disabled">Disabled</option>
</EditableItem>
</AttributeItem>
<tr className="d-flex">
<div className="form-hint">
Hidden attributes have protected values. Attribute
values are not visible for users without
reading_all_attributes capability and explicit
request for reading them. Also only exact search is
allowed. User still must have permission to read key
to use it in query.
</div>
</tr>
</tbody>
</table>
<b>Actions:</b>
<ul className="nav">
<li className="nav-item">
<Link
className="nav-link"
to={`/admin/attribute/${attribute.key}/permissions`}
>
Show attribute permissions
</Link>
<a
href="#remove-user"
className="nav-link text-danger"
onClick={() => setDeleteModalOpen(true)}
>
<FontAwesomeIcon icon="trash" />
Remove attribute
</a>
</li>
</ul>
<ConfirmationModal
isOpen={isDeleteModalOpen}
disabled={isDeleteModalDisabled}
onRequestClose={() => setDeleteModalOpen(false)}
onConfirm={handleRemoveAttribute}
message={`Are you sure you want to delete ${attribute.key} attribute`}
buttonStyle="btn-danger"
/>
</div>
);
}

0 comments on commit 85b2a39

Please sign in to comment.