Skip to content

Commit

Permalink
Merge pull request #152 from chicagopcdc/feat/drop-arborist-ui-support
Browse files Browse the repository at this point in the history
Drop Arborist UI support PEDS-161
  • Loading branch information
bobaekang authored May 20, 2021
2 parents 39e8b60 + b0c2ae3 commit 0f47d9c
Show file tree
Hide file tree
Showing 29 changed files with 601 additions and 1,141 deletions.
2 changes: 0 additions & 2 deletions data/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,6 @@
]
}
},
"useArboristUI": false,
"showArboristAuthzOnProfile": false,
"showFenceAuthzOnProfile": true,
"componentToResourceMapping": {}
}
2 changes: 0 additions & 2 deletions docs/portal_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,6 @@ Below is an example, with inline comments describing what each JSON block config
],
"dropdowns": {} // optional; dropdown groupings for buttons
},
"useArboristUI": false, // optional; set true to enable arborist UI; defaults to false if absent
"showArboristAuthzOnProfile": false, // optional; set true to list arborist resources on profile page
"showFenceAuthzOnProfile": true, // optional; set false to not list fence project access on profile page
"componentToResourceMapping": { // optional; configure some parts of arborist UI
"Workspace": { // name of component as defined in this file
Expand Down
241 changes: 111 additions & 130 deletions src/CoreMetadata/CoreMetadataHeader.jsx
Original file line number Diff line number Diff line change
@@ -1,153 +1,134 @@
import Button from '../gen3-ui-component/components/Button';
import React from 'react';
import PropTypes from 'prop-types';
import copy from 'clipboard-plus';
import React, { Component } from 'react';
import Button from '../gen3-ui-component/components/Button';
import Popup from '../components/Popup';
import { userapiPath, useArboristUI } from '../localconf';
import { userapiPath } from '../localconf';
import isEnabled from '../helpers/featureFlags';

import { userHasMethodOnProject } from '../authMappingUtils';

const DOWNLOAD_BTN_CAPTION = 'Download';
const SIGNED_URL_BTN_CAPTION = 'Generate Signed URL';
const SIGNED_URL_MSG =
'Please copy your signed URL below (this generated signed URL will expire in an hour):';
const SIGNED_URL_ERROR_MSG =
'An error has occurred when generating signed URL:';

function fileTypeTransform(type) {
let t = type.replace(/_/g, ' '); // '-' to ' '
t = t.replace(/\b\w/g, (l) => l.toUpperCase()); // capitalize words
return `| ${t} |`;
}

function fileSizeTransform(size) {
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
const sizeStr = (size / 1024 ** i).toFixed(2) * 1;
const suffix = ['B', 'KB', 'MB', 'GB', 'TB'][i];
return `${sizeStr} ${suffix}`;
}

function projectIsOpenData(projectAvail, projectID) {
return projectID in projectAvail && projectAvail[projectID] === 'Open';
}

class CoreMetadataHeader extends Component {
onGenerateSignedURL = () => {
this.props.onGenerateSignedURL(this.props.metadata.object_id);
};

onSignedURLPopupClose = () => {
this.props.onUpdatePopup({ signedURLPopup: false });
this.props.onClearSignedURL();
};

dateTransform = (date) => `Updated on ${date.substr(0, 10)}`;

render() {
if (this.props.metadata) {
const { projectAvail } = this.props;
const projectId = this.props.metadata.project_id;
let downloadButton = null;
let signedURLButton = null;

// downloadButton should always render if useArboristUI false. Otherwise according to authz.
if (
!useArboristUI ||
userHasMethodOnProject(
'read-storage',
projectId,
this.props.userAuthMapping
) ||
projectIsOpenData(projectAvail, projectId)
) {
const downloadLink = `${userapiPath}/data/download/${this.props.metadata.object_id}?expires_in=900&redirect`;

downloadButton = (
<a href={downloadLink}>
<button className='button-primary-orange'>
{DOWNLOAD_BTN_CAPTION}
</button>
</a>
);

if (isEnabled('signedURLButton')) {
signedURLButton = (
<Button
onClick={() => this.onGenerateSignedURL()}
label={SIGNED_URL_BTN_CAPTION}
className='core-metadata-page__column--right--signed-url-button'
buttonType='primary'
/>
);
}
}

if (!this.props.metadata.data_format) {
/* eslint no-console: ["error", { allow: ["error"] }] */
console.error(
"WARNING: null value found for mandatory field 'data_format', please verify the correctness of metadata"
);
}
const properties = `${
this.props.metadata.data_format
} | ${fileSizeTransform(this.props.metadata.file_size)} | ${
this.props.metadata.object_id
} | ${this.dateTransform(this.props.metadata.updated_datetime)}`;

return (
<div className='body-typo'>
<p className='h3-typo'>
{this.props.metadata.file_name}
<br />
{fileTypeTransform(this.props.metadata.type)}
</p>
<p className='body-typo'>{this.props.metadata.description}</p>
{downloadButton}
{signedURLButton}
{this.props.signedURLPopup === true && (
<Popup
message={
!this.props.error ? SIGNED_URL_MSG : SIGNED_URL_ERROR_MSG
}
error={this.props.error}
lines={!this.props.error ? [{ code: this.props.signedURL }] : []}
title='Generated Signed URL'
leftButtons={[
{
caption: 'Close',
fn: () => this.onSignedURLPopupClose(),
},
]}
rightButtons={[
{
caption: 'Copy',
fn: () => copy(this.props.signedURL),
icon: 'copy',
enabled: !this.props.error,
},
]}
onClose={() => this.onSignedURLPopupClose()}
/>
)}
<div className='body-typo'>{properties}</div>
</div>
);
}

// if there is no core metadata to display

return <p className='body-typo'>Error: {this.props.error}</p>;
/**
* @typedef {Object} CoreMetadata
* @property {string} data_format
* @property {string} file_name
* @property {number} file_size
* @property {string} description
* @property {string} updated_datetime
* @property {string} object_id
* @property {string} type
*/

/**
* @param {Object} props
* @param {CoreMetadata} props.metadata
* @param {string} props.signedURL
* @param {boolean} props.signedURLPopup
* @param {string} props.error
* @param {(object_id: string) => void} props.onGenerateSignedURL
* @param {({ singedURLPopup: boolean }) => void} props.onUpdatePopup
* @param {() => void} props.onClearSignedURL
*/
function CoreMetadataHeader({
metadata,
signedURL,
error,
signedURLPopup,
onGenerateSignedURL,
onUpdatePopup,
onClearSignedURL,
}) {
if (!metadata) return <p className='body-typo'>Error: {error}</p>;

if (!metadata.data_format)
/* eslint no-console: ["error", { allow: ["error"] }] */
console.error(
"WARNING: null value found for mandatory field 'data_format', please verify the correctness of metadata"
);

function onSignedURLPopupClose() {
onUpdatePopup({ signedURLPopup: false });
onClearSignedURL();
}

const fileTypeToDisplay = type
.replace(/_/g, ' ')
.replace(/\b\w/g, (l) => l.toUpperCase());

const fileSizeOrder =
metadata.file_size === 0
? 0
: Math.floor(Math.log(metadata.file_size) / Math.log(1024));
const fileSizeStr =
(metadata.file_size / 1024 ** fileSizeOrder).toFixed(2) * 1;
const fileSizeUnit = ['B', 'KB', 'MB', 'GB', 'TB'][fileSizeOrder];
const fileSizeToDisplay = `${fileSizeStr} ${fileSizeUnit}`;

return (
<div className='body-typo'>
<p className='h3-typo'>
{metadata.file_name}
<br />
{`| ${fileTypeToDisplay} |`}
</p>
<p className='body-typo'>{metadata.description}</p>
<a
href={`${userapiPath}/data/download/${metadata.object_id}?expires_in=900&redirect`}
>
<button className='button-primary-orange'>
{DOWNLOAD_BTN_CAPTION}
</button>
</a>
{isEnabled('signedURLButton') && (
<Button
onClick={() => onGenerateSignedURL(metadata.object_id)}
label={SIGNED_URL_BTN_CAPTION}
className='core-metadata-page__column--right--signed-url-button'
buttonType='primary'
/>
)}
{signedURLPopup && (
<Popup
message={error ? SIGNED_URL_ERROR_MSG : SIGNED_URL_MSG}
error={error}
lines={error ? [] : [{ code: signedURL }]}
title='Generated Signed URL'
leftButtons={[
{
caption: 'Close',
fn: onSignedURLPopupClose,
},
]}
rightButtons={[
{
caption: 'Copy',
fn: () => copy(signedURL),
icon: 'copy',
enabled: !error,
},
]}
onClose={onSignedURLPopupClose}
/>
)}
<div className='body-typo'>{`${
metadata.data_format
} | ${fileSizeToDisplay} | ${
metadata.object_id
} | Updated on ${metadata.updated_datetime.substr(0, 10)}`}</div>
</div>
);
}

CoreMetadataHeader.propTypes = {
metadata: PropTypes.object,
signedURL: PropTypes.string,
signedURLPopup: PropTypes.bool,
error: PropTypes.string,
projectAvail: PropTypes.object.isRequired,
userAuthMapping: PropTypes.object.isRequired,
onGenerateSignedURL: PropTypes.func.isRequired,
onUpdatePopup: PropTypes.func.isRequired,
onClearSignedURL: PropTypes.func.isRequired,
Expand Down
1 change: 0 additions & 1 deletion src/CoreMetadata/reduxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export const ReduxCoreMetadataHeader = (() => {
signedURL: state.coreMetadata.url,
signedURLPopup: state.popups.signedURLPopup,
error: state.coreMetadata.error,
userAuthMapping: state.userAuthMapping,
projectAvail: state.project.projectAvail,
});

Expand Down
8 changes: 1 addition & 7 deletions src/Index/reduxer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,8 @@ export const ReduxIndexButtonBar = (() => {
export const ReduxIntroduction = (() => {
const mapStateToProps = (state) => {
const resourcePath = '/services/sheepdog/submission/project';
const isAdminUser =
state.user.authz &&
state.user.authz.hasOwnProperty(resourcePath) &&
state.user.authz[resourcePath][0].method === '*';

return {
isAdminUser,
userAuthMapping: state.userAuthMapping,
isAdminUser: state.user.authz?.[resourcePath]?.[0].method === '*',
};
};

Expand Down
7 changes: 4 additions & 3 deletions src/Layout/reduxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ export const ReduxNavBar = (() => {
})();

export const ReduxTopBar = (() => {
const resourcePath = '/services/sheepdog/submission/project';

const mapStateToProps = (state) => ({
navTitle: components.navigation.title,
topItems: components.topBar.items,
user: state.user,
userAuthMapping: state.userAuthMapping,
username: state.user.username,
isAdminUser: state.user.authz?.[resourcePath]?.[0].method === '*',
});

// Bar chart does not dispatch anything
Expand Down
Loading

0 comments on commit 0f47d9c

Please sign in to comment.