synapse-react-client
Version:
[](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [](https://badge.fury.io/js/synaps
354 lines (351 loc) • 20.1 kB
JavaScript
"use strict";
/**
* To be reworked so that HasAccess component doesn't need to request file handle information
* because that information can be obtained in the parent component
*
* For example in SynapseTable.tsx, the variable "fileEntityHandle" is available
* for each row Synapse id to store file handle information.
* If the row Synapse id doesn't return a file handle, "fileEntityHandle" will contain
* an object { success:boolean = false , message:string }
* If the row Synapse id returns a file handle, "fileEntityHandle" will contain
* an object { success:boolean = true, data: {fileEntity, fileHandle} }
*
* To set up "fileEntityHandle", the parent component should import "FileEntityHandleQueryWrapper"
* and pass a callback function to "FileEntityHandleQueryWrapper" so that once the file handle information
* is returned, the success for failure result can be saved in the parent state.
* See SynapseTable.tsx as an example.
*/
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDownloadTypeForFileHandle = exports.FileHandleDownloadTypeEnum = exports.GIGABYTE_SIZE = void 0;
var tslib_1 = require("tslib");
var fontawesome_svg_core_1 = require("@fortawesome/fontawesome-svg-core");
var free_solid_svg_icons_1 = require("@fortawesome/free-solid-svg-icons");
var react_fontawesome_1 = require("@fortawesome/react-fontawesome");
var React = (0, tslib_1.__importStar)(require("react"));
var react_tooltip_1 = (0, tslib_1.__importDefault)(require("react-tooltip"));
var utils_1 = require("../utils");
var getEndpoint_1 = require("../utils/functions/getEndpoint");
var synapseTypes_1 = require("../utils/synapseTypes/");
var CloudProviderFileHandle_1 = require("../utils/synapseTypes/CloudProviderFileHandle");
var SynapseTableConstants_1 = require("./table/SynapseTableConstants");
var AccessRequirementList_1 = (0, tslib_1.__importStar)(require("./access_requirement_list/AccessRequirementList"));
var SynapseConstants_1 = require("../utils/SynapseConstants");
var SynapseContext_1 = require("../utils/SynapseContext");
fontawesome_svg_core_1.library.add(free_solid_svg_icons_1.faUnlockAlt);
fontawesome_svg_core_1.library.add(free_solid_svg_icons_1.faDatabase);
fontawesome_svg_core_1.library.add(free_solid_svg_icons_1.faCircle);
exports.GIGABYTE_SIZE = Math.pow(2, 30);
var FileHandleDownloadTypeEnum;
(function (FileHandleDownloadTypeEnum) {
FileHandleDownloadTypeEnum["ExternalCloudFile"] = "ExternalCloudFile";
FileHandleDownloadTypeEnum["ExternalFileLink"] = "ExternalFileLink";
FileHandleDownloadTypeEnum["TooLarge"] = "TooLarge";
FileHandleDownloadTypeEnum["Accessible"] = "Accessible";
FileHandleDownloadTypeEnum["AccessBlockedByRestriction"] = "AccessBlockedByRestriction";
FileHandleDownloadTypeEnum["AccessBlockedByACL"] = "AccessBlockedByACL";
FileHandleDownloadTypeEnum["AccessBlockedToAnonymous"] = "AccessBlockedToAnonymous";
FileHandleDownloadTypeEnum["NoFileHandle"] = "NoFileHandle";
})(FileHandleDownloadTypeEnum = exports.FileHandleDownloadTypeEnum || (exports.FileHandleDownloadTypeEnum = {}));
var getDownloadTypeForFileHandle = function (fileHandle, isInDownloadList) {
if (fileHandle && !isInDownloadList) {
return FileHandleDownloadTypeEnum.Accessible;
}
var concreteType = fileHandle.concreteType, contentSize = fileHandle.contentSize;
// check if it's too large
if (contentSize >= exports.GIGABYTE_SIZE) {
return FileHandleDownloadTypeEnum.TooLarge;
}
// check if it's a google cloud file handle
if (concreteType ===
CloudProviderFileHandle_1.CloudProviderFileHandleConcreteTypeEnum.GoogleCloudFileHandle) {
return FileHandleDownloadTypeEnum.ExternalCloudFile;
}
// check if it's an external file handle
if ((0, synapseTypes_1.implementsExternalFileHandleInterface)(fileHandle)) {
return FileHandleDownloadTypeEnum.ExternalFileLink;
}
// otherwise its available
return FileHandleDownloadTypeEnum.Accessible;
};
exports.getDownloadTypeForFileHandle = getDownloadTypeForFileHandle;
/**
* HasAccess shows if the user has access to the file or not.
*
* The component's behavior changes whether it's passed in a FileHandle or not.
* If passed a file handle then it will give more detailed information about the download.
*
* @export
* @class HasAccess
* @extends {React.Component<HasAccessProps, HasAccessState>}
*/
var HasAccess = /** @class */ (function (_super) {
(0, tslib_1.__extends)(HasAccess, _super);
function HasAccess(props) {
var _this = _super.call(this, props) || this;
_this.refresh = function (forceRefresh) {
if (_this.state.isGettingEntityInformation ||
_this.state.isGettingRestrictionInformation ||
_this.state.errorOnGetRestrictionInformation) {
return;
}
_this.getRestrictionInformation(forceRefresh);
_this.getFileEntityFileHandle(forceRefresh);
};
_this.updateStateFileHandleAccessBlocked = function () {
var fileHandleDownloadType = _this.context.accessToken
? FileHandleDownloadTypeEnum.AccessBlockedByACL
: FileHandleDownloadTypeEnum.AccessBlockedToAnonymous;
_this.setState({
fileHandleDownloadType: fileHandleDownloadType,
});
};
_this.getFileEntityFileHandle = function (forceRefresh) {
var _a = _this.props, entityId = _a.entityId, entityVersionNumber = _a.entityVersionNumber, isInDownloadList = _a.isInDownloadList, fileHandle = _a.fileHandle;
if (_this.state.fileHandleDownloadType && !forceRefresh) {
// already know the downloadType
return;
}
if (fileHandle) {
var fileHandleDownloadType = (0, exports.getDownloadTypeForFileHandle)(fileHandle, isInDownloadList);
_this.setState({
fileHandleDownloadType: fileHandleDownloadType,
});
return;
}
_this.setState({
isGettingEntityInformation: true,
});
// fileHandle was not passed to us, ask for it.
// is this a FileEntity?
return utils_1.SynapseClient.getEntity(_this.context.accessToken, entityId, entityVersionNumber)
.then(function (entity) {
if (entity.hasOwnProperty('dataFileHandleId')) {
// looks like a FileEntity, get the FileHandle
return utils_1.SynapseClient.getFileResult(entity, _this.context.accessToken, true).then(function (fileHandle) {
var fileHandleDownloadType = (0, exports.getDownloadTypeForFileHandle)(fileHandle.fileHandle, isInDownloadList);
_this.setState({
fileHandleDownloadType: fileHandleDownloadType,
isGettingEntityInformation: false,
});
});
}
else {
// entity looks like something else.
_this.setState({
fileHandleDownloadType: FileHandleDownloadTypeEnum.NoFileHandle,
isGettingEntityInformation: false,
});
return Promise.resolve();
}
})
.catch(function (err) {
// this could be a self-imposed error or one from the backend, only log the latter
if (err.reason) {
console.error('Error on get Entity = ', err);
}
// could not get entity
_this.updateStateFileHandleAccessBlocked();
_this.setState({
isGettingEntityInformation: false,
});
});
};
_this.getRestrictionInformation = function (forceRefresh) {
var entityId = _this.props.entityId;
if (_this.state.restrictionInformation && !forceRefresh) {
return;
}
_this.setState({
isGettingRestrictionInformation: true,
});
var request = {
restrictableObjectType: synapseTypes_1.RestrictableObjectType.ENTITY,
objectId: entityId,
};
return utils_1.SynapseClient.getRestrictionInformation(request, _this.context.accessToken)
.then(function (restrictionInformation) {
_this.setState({
restrictionInformation: restrictionInformation,
});
})
.catch(function (err) {
console.error('Error on getRestrictionInformation. This should not happen: ', err);
_this.setState({
errorOnGetRestrictionInformation: true,
});
})
.finally(function () {
_this.setState({
isGettingRestrictionInformation: false,
});
});
};
_this.renderIconHelper = function (iconProp, classColor) {
return (React.createElement("span", { className: "fa-layers fa-fw" },
React.createElement(react_fontawesome_1.FontAwesomeIcon, { icon: free_solid_svg_icons_1.faCircle, className: classColor, size: "1x", style: { fontSize: '24px' } }),
React.createElement(react_fontawesome_1.FontAwesomeIcon, { icon: iconProp, className: "SRC-whiteText", size: "1x", transform: { x: 5 }, style: { fontSize: '13px' } })));
};
_this.renderIcon = function (downloadType, restrictionInformation) {
// if there are any access restrictions
if (restrictionInformation === null || restrictionInformation === void 0 ? void 0 : restrictionInformation.hasUnmetAccessRequirement) {
return _this.renderIconHelper(free_solid_svg_icons_1.faLock, 'SRC-warning-color');
}
switch (downloadType) {
// fileHandle available
case FileHandleDownloadTypeEnum.ExternalFileLink:
case FileHandleDownloadTypeEnum.ExternalCloudFile:
return _this.renderIconHelper(free_solid_svg_icons_1.faLink, 'SRC-warning-color');
case FileHandleDownloadTypeEnum.TooLarge:
return _this.renderIconHelper(free_solid_svg_icons_1.faDatabase, 'SRC-danger-color');
// was FileEntity, but no fileHandle was available
case FileHandleDownloadTypeEnum.AccessBlockedByACL:
case FileHandleDownloadTypeEnum.AccessBlockedToAnonymous:
return _this.renderIconHelper(free_solid_svg_icons_1.faLock, 'SRC-warning-color');
// was a FileEntity, and fileHandle was available
case FileHandleDownloadTypeEnum.Accessible:
// or was not a FileEntity, but no unmet access restrictions
case FileHandleDownloadTypeEnum.NoFileHandle:
return _this.renderIconHelper(free_solid_svg_icons_1.faUnlockAlt, 'SRC-success-color');
default:
// nothing is rendered until access requirement is loaded
return React.createElement(React.Fragment, null);
}
};
_this.handleGetAccess = function () {
var _a = _this.props, entityId = _a.entityId, set_arPropsFromHasAccess = _a.set_arPropsFromHasAccess;
utils_1.SynapseClient.getAllAccessRequirements(_this.context.accessToken, entityId).then(function (requirements) {
if ((0, AccessRequirementList_1.checkHasUnsportedRequirement)(requirements)) {
window.open((0, getEndpoint_1.getEndpoint)(getEndpoint_1.BackendDestinationEnum.PORTAL_ENDPOINT) + "#!AccessRequirements:ID=" + entityId + "&TYPE=ENTITY", '_blank');
}
else {
if (set_arPropsFromHasAccess) {
set_arPropsFromHasAccess({
accessRequirementFromProps: requirements,
entityId: entityId,
});
}
else {
_this.setState({
accessRequirements: requirements,
displayAccessRequirement: true,
});
}
}
});
};
// Show Access Requirements
_this.renderARsLink = function () {
var entityId = _this.props.entityId;
var _a = _this.state, restrictionInformation = _a.restrictionInformation, displayAccessRequirement = _a.displayAccessRequirement, accessRequirements = _a.accessRequirements;
if (!restrictionInformation) {
// loading
return React.createElement(React.Fragment, null);
}
var hasUnmetAccessRequirement = restrictionInformation === null || restrictionInformation === void 0 ? void 0 : restrictionInformation.hasUnmetAccessRequirement;
var restrictionLevel = restrictionInformation === null || restrictionInformation === void 0 ? void 0 : restrictionInformation.restrictionLevel;
var linkText = '';
if (hasUnmetAccessRequirement) {
linkText = 'Request Access';
}
else if (synapseTypes_1.RestrictionLevel.OPEN === restrictionLevel) {
// they need to sign in
return React.createElement(React.Fragment, null);
}
else {
linkText = 'View Terms';
}
return (React.createElement(React.Fragment, null,
React.createElement("a", { style: {
fontSize: '14px',
cursor: 'pointer',
marginLeft: '10px',
}, className: _this.props.className, onClick: _this.handleGetAccess }, linkText),
displayAccessRequirement && (React.createElement(AccessRequirementList_1.default, { entityId: entityId, accessRequirementFromProps: accessRequirements, renderAsModal: true, onHide: function () {
_this.setState({ displayAccessRequirement: false });
_this.refresh();
} }))));
};
_this.getRestrictionInformation = _this.getRestrictionInformation.bind(_this);
_this.getFileEntityFileHandle = _this.getFileEntityFileHandle.bind(_this);
_this.updateStateFileHandleAccessBlocked = _this.updateStateFileHandleAccessBlocked.bind(_this);
_this.state = {
fileHandleDownloadType: undefined,
displayAccessRequirement: false,
accessRequirements: [],
isGettingEntityInformation: false,
isGettingRestrictionInformation: false,
errorOnGetRestrictionInformation: false,
};
return _this;
}
HasAccess.prototype.componentDidUpdate = function (prevProps) {
// SWC-5821: If the entity ID prop changes, force refresh
if (prevProps.entityId != this.props.entityId) {
this.refresh(true);
}
};
HasAccess.prototype.componentDidMount = function () {
this.refresh();
};
HasAccess.prototype.render = function () {
var _a = this.state, restrictionInformation = _a.restrictionInformation, fileHandleDownloadType = _a.fileHandleDownloadType;
if (typeof fileHandleDownloadType === 'undefined') {
// note, this can't be "if (!downloadType)" since DownloadTypeEnum has a 0 value (which is falsy)
// loading
return React.createElement(React.Fragment, null);
}
var tooltipText = HasAccess.tooltipText[fileHandleDownloadType];
if (fileHandleDownloadType ===
FileHandleDownloadTypeEnum.AccessBlockedByACL &&
(restrictionInformation === null || restrictionInformation === void 0 ? void 0 : restrictionInformation.hasUnmetAccessRequirement)) {
// If blocked by ACL check if blocked by Access Restrictions, those can be taken care of
// though they will then be blocked by ACL afterwards.
tooltipText =
HasAccess.tooltipText[FileHandleDownloadTypeEnum.AccessBlockedByRestriction];
}
var entityId = this.props.entityId;
var icon = this.renderIcon(fileHandleDownloadType, restrictionInformation);
var viewARsLink = this.renderARsLink();
var iconContainer = fileHandleDownloadType ===
FileHandleDownloadTypeEnum.AccessBlockedToAnonymous ? (React.createElement("button", { type: "button", className: SynapseConstants_1.SRC_SIGN_IN_CLASS, onClick: function (ev) {
if (ev.isTrusted) {
/*
There is a tricky problem -
The portals listens to click events for buttons with the class SRC_SIGN_IN_CLASS set, it listens to this event
so that it can display the login modal.
This button has an svg inside of it which is problematic because more often than not clicking this button will
instead click that svg. The event listener in the portals will break as a result.
Though the svg may get the actual click event, because of event bubbling this button will get its onClick called.
Once onClick is called we can manually dispatch an event off of this button. This does pose a problem, we end up in a
infinite loop because this button keeps disptaching click events, so we can use the isTrusted to recognize if onClick was
triggered programmatically or by user click. Lastly, using { bubbles: true } ensures the event bubbles up to the document level.
*/
var clickEvent = new MouseEvent('click', { bubbles: true });
ev.currentTarget.dispatchEvent(clickEvent);
}
} }, icon)) : (React.createElement("span", { tabIndex: 0, "data-for": entityId, "data-tip": tooltipText }, icon));
return (React.createElement("span", { style: { whiteSpace: 'nowrap' } },
tooltipText && (React.createElement(React.Fragment, null,
iconContainer,
React.createElement(react_tooltip_1.default, { delayShow: SynapseTableConstants_1.TOOLTIP_DELAY_SHOW, place: "top", type: "dark", effect: "solid", id: entityId, className: "has-access-tooltip-width" }),
viewARsLink)),
!tooltipText && (React.createElement(React.Fragment, null,
icon,
" ",
viewARsLink))));
};
HasAccess.tooltipText = (_a = {},
_a[FileHandleDownloadTypeEnum.AccessBlockedToAnonymous] = 'You must sign in to access this file.',
// Note AccessBlockedByRestriction is never explicitly set, its value is calculated
_a[FileHandleDownloadTypeEnum.AccessBlockedByRestriction] = 'You must request access to this restricted file.',
_a[FileHandleDownloadTypeEnum.AccessBlockedByACL] = 'You do not have download access for this item.',
_a[FileHandleDownloadTypeEnum.TooLarge] = 'This file is too large to download as a package and must be downloaded manually.',
_a[FileHandleDownloadTypeEnum.ExternalFileLink] = 'This is an external link, which must be downloaded manually.',
_a[FileHandleDownloadTypeEnum.ExternalCloudFile] = 'This file must be downloaded manually (e.g. a file in Google Cloud).',
_a);
HasAccess.contextType = SynapseContext_1.SynapseContext;
return HasAccess;
}(React.Component));
exports.default = HasAccess;
//# sourceMappingURL=HasAccess.js.map