-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(authorize): created custom hook for getting authorized state
- Loading branch information
1 parent
0de9c99
commit cce12f3
Showing
2 changed files
with
170 additions
and
182 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | ||
}; |