mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-27 23:45:52 +08:00
fix HOC to recive objects
This commit is contained in:
parent
c07f54a0c7
commit
c5379896c0
5 changed files with 124 additions and 41 deletions
|
@ -15,28 +15,34 @@ module ClientApi
|
|||
def generate_permissions_object
|
||||
sanitize_permissions!
|
||||
@permissions = {}
|
||||
if @resource
|
||||
@required_permissions.collect do |permission|
|
||||
@permissions.merge!("#{permission}?" => @holder.eval(permission,
|
||||
current_user,
|
||||
@resource))
|
||||
end
|
||||
else
|
||||
@required_permissions.collect do |permission|
|
||||
obj = @resource.fetch(:type)
|
||||
.constantize
|
||||
.public_send(:find_by_id, @resource.fetch(:id) {
|
||||
raise ArgumentError, 'ID must be present'
|
||||
}) if @resource
|
||||
@required_permissions.collect do |permission|
|
||||
parsed_permision = permission.gsub('can_', '')
|
||||
if @resource
|
||||
# return false if object does not exist
|
||||
result = obj ? @holder.eval('read_team', current_user, obj) : false
|
||||
@permissions.merge!(permission => result)
|
||||
else
|
||||
@permissions.merge!(
|
||||
"#{permission}?" => @holder.eval_generic(permission, current_user)
|
||||
permission => @holder.eval_generic(
|
||||
parsed_permision, current_user
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sanitize_permissions!
|
||||
@required_permissions = params.fetch(:parsePermission) do
|
||||
@required_permissions = params.fetch(:requiredPermissions) do
|
||||
:permissions_array_missing
|
||||
end
|
||||
@holder = Canaid::PermissionsHolder.instance
|
||||
@required_permissions.each do |permission|
|
||||
next if @holder.has_permission?(permission)
|
||||
next if @holder.has_permission?(permission.gsub('can_', ''))
|
||||
# this error should happen only in development
|
||||
raise ArgumentError, "Method #{permission} has no related " \
|
||||
"permission registered."
|
||||
|
@ -49,7 +55,7 @@ module ClientApi
|
|||
def resource_valid?
|
||||
@resource = params[:resource]
|
||||
return true unless @resource
|
||||
return true if Object.const_get(@resource.classify)
|
||||
return true if Object.const_get(@resource.fetch(:type).classify)
|
||||
rescue NameError
|
||||
return false
|
||||
end
|
||||
|
|
|
@ -5,10 +5,18 @@ import styled from "styled-components";
|
|||
import { FormattedMessage, FormattedPlural } from "react-intl";
|
||||
import { Button, Glyphicon } from "react-bootstrap";
|
||||
import { SETTINGS_NEW_TEAM_ROUTE } from "../../../../../config/routes";
|
||||
import * as Permissions from "../../../../../services/permissions"
|
||||
|
||||
const Wrapper = styled.div`margin: 15px 0;`;
|
||||
const TeamsPageDetails = ({ teams }) => {
|
||||
const TeamsPageDetails = ({ teams, permissions }) => {
|
||||
const teamsNumber = teams.length;
|
||||
const newTeamButton = (
|
||||
<LinkContainer to={SETTINGS_NEW_TEAM_ROUTE}>
|
||||
<Button>
|
||||
<Glyphicon glyph="plus" /> <FormattedMessage id="global_team_switch.new_team" />
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
)
|
||||
return (
|
||||
<Wrapper>
|
||||
<FormattedPlural
|
||||
|
@ -30,11 +38,7 @@ const TeamsPageDetails = ({ teams }) => {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
<LinkContainer to={SETTINGS_NEW_TEAM_ROUTE}>
|
||||
<Button>
|
||||
<Glyphicon glyph="plus" /> <FormattedMessage id="global_team_switch.new_team" />
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
{permissions.can_create_teams ? newTeamButton : ''}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
@ -53,7 +57,8 @@ TeamsPageDetails.propTypes = {
|
|||
};
|
||||
|
||||
TeamsPageDetails.defaultProps = {
|
||||
teams: []
|
||||
teams: [],
|
||||
permissions: {}
|
||||
};
|
||||
|
||||
export default TeamsPageDetails;
|
||||
export default Permissions.connect(TeamsPageDetails, ["can_create_teams"]);
|
||||
|
|
|
@ -7,10 +7,9 @@ export const getPermissionStatus = (
|
|||
requiredPermissions: Array<string>,
|
||||
resource: string
|
||||
): Promise<*> => {
|
||||
const parsePermission = requiredPermissions.map(el => el.replace("?", ""));
|
||||
return axiosInstance
|
||||
.post(PERMISSIONS_PATH, {
|
||||
parsePermission,
|
||||
requiredPermissions,
|
||||
resource
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
|
|
|
@ -7,14 +7,48 @@
|
|||
Than you use the Permissions.connect method to wrap your component and
|
||||
pass this "permissions helper methods" in your component params:
|
||||
|
||||
If you need to specific model you have to specify it in the connect method
|
||||
like the example below
|
||||
>
|
||||
> Permissions.connect(MyComponent, ["can_update_team?", "can_read_team?"], "Team");
|
||||
> Permissions.connect(MyComponent, ["can_update_team", "can_read_team"], "Team");
|
||||
>
|
||||
|
||||
In case your component is connected to Redux or some other HOC you can simply
|
||||
chain the HOC's
|
||||
>
|
||||
> const mapStateToProps = ({ current_team }) => ({ current_team });
|
||||
> const MyComponentWithPermissions = Permissions.connect(MyComponent, ["can_read_team"], "Team");
|
||||
> export default connect(mapStateToProps)(MyComponentWithPermissions);
|
||||
>
|
||||
beside the permissions object you get the setPermissionResourceId/1 function
|
||||
in your component props. Than you can pass the id of the resource when you have it.
|
||||
|
||||
Example: you need the current team for whatever reason you can call it in the
|
||||
shouldComponentUpdate function...
|
||||
|
||||
> shouldComponentUpdate(nextProps: any, nextState: any) {
|
||||
> this.props.setPermissionResourceId(this.props.current_team.id);
|
||||
> return true; // remember to return true!
|
||||
> }
|
||||
|
||||
JUST REMEMBER THAT YOU HAVE PASS A VALID ID and you have to check if is present
|
||||
at the moment of execution. THE REQUEST WILL BE TRIGGERED WHEN YOU INVOKE
|
||||
setPermissionResourceId/1 FUNCTION!!!!
|
||||
|
||||
else you can pass just the perrmissions on the user
|
||||
>
|
||||
> Permissions.connect(MyComponent, ["can_update"])
|
||||
>
|
||||
THE REQUEST WILL BE TRIGGERED WHEN THE COMPONENT MOUNTS!!!
|
||||
|
||||
Now you can access your permissions through component params. The permissions
|
||||
you required have 3 states [true, false, null]. Null is when you are waiting for server response.
|
||||
You can use methods params.can_update_team? or whatever permissions you declare
|
||||
|
||||
Finally you can access to your permissions in the component using
|
||||
props.permissions.can_.... whatever you specify. The props.permissions is
|
||||
basically an object that holds all required permissions.
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import { getPermissionStatus } from "../api/permissions_api";
|
||||
|
||||
|
@ -22,15 +56,20 @@ type State = {
|
|||
permissions: any
|
||||
};
|
||||
|
||||
type ResourceObject = {
|
||||
type: string,
|
||||
id: number
|
||||
};
|
||||
|
||||
type PermissionsObject = {
|
||||
[string]: boolean | null
|
||||
}
|
||||
};
|
||||
/*
|
||||
This function accepts 3 arguments which are REQUIRED
|
||||
1.) WrappedComponent: Component that you want to have permissions
|
||||
2.) requiredPermissions: an array of strings with permissions methods that
|
||||
1.) @WrappedComponent: Component that you want to have permissions
|
||||
2.) @requiredPermissions: an array of strings with permissions methods that
|
||||
will be available in your component
|
||||
3.) resource: a string of reference/model name
|
||||
3.) @resource: a string of reference/model name
|
||||
*/
|
||||
export function connect<Props: {}>(
|
||||
WrappedComponent: React.ComponentType<Props>,
|
||||
|
@ -41,31 +80,55 @@ export function connect<Props: {}>(
|
|||
requiredPermissions.forEach(el => {
|
||||
parsedPermissions[el] = null;
|
||||
});
|
||||
|
||||
return class extends React.Component<*, State> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = { permissions: parsedPermissions };
|
||||
this.state = {
|
||||
permissions: parsedPermissions,
|
||||
permissionsRequestDone: false
|
||||
};
|
||||
(this: any).getPermissions = this.getPermissions.bind(this);
|
||||
(this: any).setPermissionResourceId = this.setPermissionResourceId.bind(
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
getPermissionStatus(requiredPermissions, resource)
|
||||
.then(data => {
|
||||
this.setState({ permissions: data });
|
||||
})
|
||||
.catch(() => {
|
||||
const permissions: PermissionsObject = {};
|
||||
requiredPermissions.forEach(el => {
|
||||
permissions[el] = false;
|
||||
if (!resource) {
|
||||
this.getPermissions(requiredPermissions, resource);
|
||||
}
|
||||
}
|
||||
|
||||
setPermissionResourceId(id: number): void {
|
||||
this.getPermissions(requiredPermissions, { type: resource, id });
|
||||
}
|
||||
|
||||
getPermissions(
|
||||
permissionsArray: Array<string>,
|
||||
resourceObject: ResourceObject | undefined
|
||||
): void {
|
||||
if (!this.state.permissionsRequestDone) {
|
||||
getPermissionStatus(permissionsArray, resourceObject)
|
||||
.then(data => {
|
||||
this.setState({ permissions: data, permissionsRequestDone: true });
|
||||
})
|
||||
.catch(() => {
|
||||
const permissions: PermissionsObject = {};
|
||||
permissionsArray.forEach(el => {
|
||||
permissions[el] = false;
|
||||
});
|
||||
this.setState({ permissions });
|
||||
});
|
||||
this.setState({ permissions });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render(): any {
|
||||
return (
|
||||
<WrappedComponent
|
||||
permissions={this.state.permissions}
|
||||
setPermissionResourceId={
|
||||
resource ? this.setPermissionResourceId : undefined
|
||||
}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -4,10 +4,20 @@ describe ClientApi::PermissionsController, type: :controller do
|
|||
login_user
|
||||
|
||||
describe '#status' do
|
||||
let!(:user) { User.first || create(:user) }
|
||||
let!(:team) { create :team, created_by: user }
|
||||
let!(:user_team) { create :user_team, user: user, team: team, role: 2 }
|
||||
let(:params) do
|
||||
{ parsePermission: ['can_view_team'], resource: 'UserTeam' }
|
||||
{ requiredPermissions: ['can_read_team'],
|
||||
resource: { type: 'Team', id: team.id } }
|
||||
end
|
||||
|
||||
let(:subject) { post :status, format: :json, params: params }
|
||||
it { is_expected.to be_success }
|
||||
|
||||
it 'returns an object with the permission' do
|
||||
body = JSON.parse(subject.body)
|
||||
expect(body).to eq('can_read_team' => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue