Skip to content

Commit

Permalink
feat(authorize): created custom hook for getting authorized state
Browse files Browse the repository at this point in the history
  • Loading branch information
availity-bot committed Jul 19, 2019
1 parent 0de9c99 commit cce12f3
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 182 deletions.
238 changes: 56 additions & 182 deletions packages/authorize/Authorize.js
Original file line number Diff line number Diff line change
@@ -1,191 +1,65 @@
import React, { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { avUserPermissionsApi, avRegionsApi } from '@availity/api-axios';
import useAuthorize from './useAuthorize';
import BlockUi from 'react-block-ui';
import 'react-block-ui/style.css';

const watching = ['region', 'organizationId', 'customerId'];

const warned = {};

function warnOnce(message) {
if (!warned[message]) {
// eslint-disable-next-line no-console
if (typeof console !== 'undefined' && typeof console.error === 'function') {
console.error(message); // eslint-disable-line no-console
}
warned[message] = true;
}
}

class Authorize extends Component {
static propTypes = {
permissions: PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
),
PropTypes.string,
PropTypes.number,
])
),
PropTypes.string,
PropTypes.number,
]).isRequired,
region: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
loader: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
organizationId: PropTypes.string,
customerId: PropTypes.string,
unauthorized: PropTypes.node,
children: PropTypes.node,
negate: PropTypes.bool,
};

static defaultProps = {
region: true,
unauthorized: null,
children: null,
};

state = {
loading: true,
};

async getRegion() {
const { region } = this.props;

if (region === true) {
const resp = await avRegionsApi.getCurrentRegion();
return (
(resp &&
resp.data &&
resp.data.regions &&
resp.data.regions[0] &&
resp.data.regions[0].id) ||
undefined
);
}
if (region) {
return region;
}
return undefined;
const Authorize = ({
permissions,
customerId,
organizationId,
region,
loader,
negate,
children,
unauthorized,
}) => {
const [authorized, loading] = useAuthorize({
permissions,
customerId,
organizationId,
region,
});

if (loading) {
if (loader) return loader === true ? <BlockUi blocking /> : loader;
return null;
}

checkPermission(permission) {
const { organizationId, customerId } = this.props;
if (!permission) return false;

if (organizationId) {
if (customerId) {
warnOnce(
'You provided both `organizationId` and `customerId` to Authorize but both cannot be used together; `organizationId` will be used and `customerId` will be ignored. If you want to use `customerId` do not provide `organizationId`.'
);
}
return (
permission.organizations.filter(
({ id: orgId }) => orgId === organizationId
).length > 0
);
}

if (customerId) {
return (
permission.organizations.filter(
({ customerId: orgCustomerId }) => orgCustomerId === customerId
).length > 0
);
}

return true;
const showChildren = authorized ^ negate; // eslint-disable-line no-bitwise
if (showChildren) {
return children;
}

// TODO: Move most of this logic to avUserPermissionsApi or something more common.
async checkPermissions() {
const { loading } = this.state;
const { permissions } = this.props;

if (!loading) this.setState({ loading: true });
const permissionsSets = Array.isArray(permissions)
? permissions
: [permissions];
const permissionsList = [].concat(...permissionsSets);
const newPermissions = (await avUserPermissionsApi.getPermissions(
permissionsList,
await this.getRegion()
)).reduce((prev, cur) => {
prev[cur.id] = cur;
return prev;
}, {});

const authorized = permissionsSets.some(permissionSet => {
if (Array.isArray(permissionSet)) {
return permissionSet.every(permission =>
this.checkPermission(newPermissions[permission])
);
}
return this.checkPermission(newPermissions[permissionSet]);
});
if (
permissionsList.join() ===
[]
.concat(...(Array.isArray(permissions) ? permissions : [permissions]))
.join()
) {
this.setState({ authorized, loading: false });
}
}

componentDidMount() {
this.checkPermissions();
}

componentDidUpdate(prevProps) {
const { permissions } = this.props;

if (
// eslint-disable-next-line react/destructuring-assignment
watching.some(propsName => prevProps[propsName] !== this.props[propsName])
) {
return this.checkPermissions();
}

if (prevProps.permissions !== permissions) {
if (
typeof prevProps.permissions === 'string' ||
typeof permissions === 'string'
) {
return this.checkPermissions();
}

const prevPermissions = [].concat(...prevProps.permissions);
const currentPermissions = [].concat(...permissions);
if (
prevPermissions.length !== currentPermissions.length ||
prevPermissions.join() !== currentPermissions.join()
) {
return this.checkPermissions();
}
}

return false;
}

render() {
const { loader, children, unauthorized, negate } = this.props;
const { loading, authorized } = this.state;

if (loading) {
if (loader) return loader === true ? <BlockUi blocking /> : loader;
return null;
}
const showChildren = authorized ^ negate; // eslint-disable-line no-bitwise
if (showChildren) {
return children;
}

return unauthorized;
}
}
return unauthorized;
};

Authorize.propTypes = {
permissions: PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
),
PropTypes.string,
PropTypes.number,
])
),
PropTypes.string,
PropTypes.number,
]).isRequired,
region: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
loader: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
organizationId: PropTypes.string,
customerId: PropTypes.string,
unauthorized: PropTypes.node,
children: PropTypes.node,
negate: PropTypes.bool,
};

Authorize.defaultProps = {
region: true,
unauthorized: null,
children: null,
};

export default Authorize;
114 changes: 114 additions & 0 deletions packages/authorize/useAuthorize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useState, useEffect } from 'react';
import { avUserPermissionsApi, avRegionsApi } from '@availity/api-axios';


const warned = {};

function warnOnce(message) {
if (!warned[message]) {
// eslint-disable-next-line no-console
if (typeof console !== 'undefined' && typeof console.error === 'function') {
console.error(message); // eslint-disable-line no-console
}
warned[message] = true;
}
}


export default ({
permissions,
organizationId,
customerId,
region = true,
}) => {
const [authorized, setAuthorized] = useState(false);
const [loading, setLoading] = useState(true);

const getRegion = async () => {

if (region === true) {
const resp = await avRegionsApi.getCurrentRegion();
return (
(resp &&
resp.data &&
resp.data.regions &&
resp.data.regions[0] &&
resp.data.regions[0].id) ||
undefined
);
}
if (region) {
return region;
}
return undefined;
};

const checkPermission = permission => {
if (!permission) return false;

if (organizationId) {
if (customerId) {
warnOnce(
'You provided both `organizationId` and `customerId` to Authorize but both cannot be used together; `organizationId` will be used and `customerId` will be ignored. If you want to use `customerId` do not provide `organizationId`.'
);
}
return (
permission.organizations.filter(
({ id: orgId }) => orgId === organizationId
).length > 0
);
}

if (customerId) {
return (
permission.organizations.filter(
({ customerId: orgCustomerId }) => orgCustomerId === customerId
).length > 0
);
}

return true;
};

// TODO: Move most of this logic to avUserPermissionsApi or something more common.
const checkPermissions = async () => {
const permissionsSets = Array.isArray(permissions)
? permissions
: [permissions];
const permissionsList = [].concat(...permissionsSets);
const newPermissions = (await avUserPermissionsApi.getPermissions(
permissionsList,
await getRegion()
)).reduce((prev, cur) => {
prev[cur.id] = cur;
return prev;
}, {});

const authorized = permissionsSets.some(permissionSet => {
if (Array.isArray(permissionSet)) {
return permissionSet.every(permission =>
checkPermission(newPermissions[permission])
);
}
return checkPermission(newPermissions[permissionSet]);
});
if (
permissionsList.join() ===
[]
.concat(...(Array.isArray(permissions) ? permissions : [permissions]))
.join()
) {
setLoading(false);
setAuthorized(authorized);
}
};

useEffect(() => {
if (!loading) setLoading(true);
checkPermissions();
// todo - optimize this so we only have a permissions effect for fetching
// and the others are just filters
}, [organizationId, region, customerId, permissions]);

return [authorized,loading];
};

0 comments on commit cce12f3

Please sign in to comment.