Merge pull request #900 from ZmagoD/zd_SCI_1854

adds premissions service to FE
This commit is contained in:
Zmago Devetak 2018-01-12 14:24:28 +01:00 committed by GitHub
commit ebd354e4d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 658 additions and 65 deletions

View file

@ -0,0 +1,63 @@
module ClientApi
class PermissionsController < ApplicationController
before_action :generate_permissions_object, only: :status
def status
respond_to do |format|
format.json do
render json: @permissions, status: :ok
end
end
end
private
def generate_permissions_object
sanitize_permissions!
@permissions = {}
obj = @resource.fetch(:type)
.constantize
.public_send(:find_by_id, @resource.fetch(:id) {
raise ArgumentError, 'ID must be present'
}) if @resource
@required_permissions.each do |permission|
trim_permission = permission.gsub('can_', '')
if @resource
# return false if object does not exist
result = obj ? @holder.eval(trim_permission, current_user, obj) : false
@permissions.merge!(permission => result)
else
@permissions.merge!(
permission => @holder.eval_generic(
trim_permission, current_user
)
)
end
end
end
def sanitize_permissions!
@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.gsub('can_', ''))
# this error should happen only in development
raise ArgumentError, "Method #{permission} has no related " \
"permission registered."
end
# sanitize resource, this error should happen only in development
raise ArgumentError,
"Resource #{@resource} does not exists" unless resource_valid?
end
def resource_valid?
@resource = params[:resource]
return true unless @resource
return true if Object.const_get(@resource.fetch(:type).classify)
rescue NameError
return false
end
end
end

View file

@ -103,4 +103,4 @@ MyProfile.propTypes = {
addCurrentUser: func.isRequired
};
export default connect(null, { addCurrentUser })(MyProfile);
export default connect(null, { addCurrentUser })(MyProfile)

View file

@ -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" />&nbsp;<FormattedMessage id="global_team_switch.new_team" />
</Button>
</LinkContainer>
)
return (
<Wrapper>
<FormattedPlural
@ -30,11 +38,7 @@ const TeamsPageDetails = ({ teams }) => {
/>
}
/>&nbsp;
<LinkContainer to={SETTINGS_NEW_TEAM_ROUTE}>
<Button>
<Glyphicon glyph="plus" />&nbsp;<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"]);

View file

@ -40,3 +40,6 @@ export const SETTINGS_TEAMS = "/settings/teams";
// scinote configurations
export const ABOUT_SCINOTE_PATH = "/client_api/about_scinote";
// permissions
export const PERMISSIONS_PATH = "/client_api/premissions";

View file

@ -0,0 +1,16 @@
// @flow
import axiosInstance from "./config";
import { PERMISSIONS_PATH } from "./endpoints";
export const getPermissionStatus = (
requiredPermissions: Array<string>,
resource: string
): Promise<*> => {
return axiosInstance
.post(PERMISSIONS_PATH, {
requiredPermissions,
resource
})
.then(({ data }) => data);
};

View file

@ -0,0 +1,135 @@
// @flow
/*
To use this HOC you need to import in you targeted component:
> import * as Permissions from "relative path../services/permissions"
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");
>
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 there's also the setPermissionResourceId/1 function in your component props.
This function should be used to 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 TO PASS A VALID ID and you have to check if it's present at the moment of the execution.
THE REQUEST WILL BE TRIGGERED WHEN YOU INVOKE setPermissionResourceId/1 FUNCTION!!!
Otherwise (generic permissions without the accessed object) you can just pass the permissions 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.
Finally you can access 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";
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
will be available in your component
3.) @resource: a string of reference/model name
*/
export function connect<Props: {}>(
WrappedComponent: React.ComponentType<Props>,
requiredPermissions: Array<string> = [],
resource: string
) {
const parsedPermissions: PermissionsObject = {};
requiredPermissions.forEach(el => {
parsedPermissions[el] = null;
});
return class extends React.Component<*, State> {
constructor(props: any) {
super(props);
this.state = {
permissions: parsedPermissions,
permissionsRequestDone: false
};
(this: any).getPermissions = this.getPermissions.bind(this);
(this: any).setPermissionResourceId = this.setPermissionResourceId.bind(
this
);
}
componentDidMount(): void {
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 });
});
}
}
render(): any {
return (
<WrappedComponent
permissions={this.state.permissions}
setPermissionResourceId={
resource ? this.setPermissionResourceId : undefined
}
{...this.props}
/>
);
}
};
}

View file

@ -18,6 +18,7 @@ Rails.application.routes.draw do
get '/settings/*all', to: 'client_api/settings#index'
namespace :client_api, defaults: { format: 'json' } do
post '/premissions', to: 'permissions#status'
%i(activities teams notifications users configurations).each do |path|
draw path
end

View file

@ -0,0 +1,340 @@
// flow-typed signature: 2a83afa8301f1298c0c192309eab48de
// flow-typed version: <<STUB>>/eslint-plugin-flowtype_v^2.39.1/flow_v0.56.0
/**
* This is an autogenerated libdef stub for:
*
* 'eslint-plugin-flowtype'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'eslint-plugin-flowtype' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'eslint-plugin-flowtype/bin/readmeAssertions' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/index' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/booleanStyle' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/defineFlowType' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/delimiterDangle' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/genericSpacing' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/noDupeKeys' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/noMutableArray' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/noPrimitiveConstructorTypes' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/noTypesMissingFileAnnotation' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/noUnusedExpressions' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/noWeakTypes' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireParameterType' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireReturnType' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireVariableType' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/semi' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/sortKeys' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/spaceAfterTypeColon' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeGenericBracket' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeTypeColon' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateFunctions' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeIndexer' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeProperty' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateReturnType' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypeCastExpression' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/reporter' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeIdMatch' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/useFlowType' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/rules/validSyntax' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/checkFlowFileAnnotation' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/fuzzyStringMatch' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/getParameterName' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/getTokenAfterParens' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/getTokenBeforeParens' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/index' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFile' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFileAnnotation' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/iterateFunctionNodes' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/quoteName' {
declare module.exports: any;
}
declare module 'eslint-plugin-flowtype/dist/utilities/spacingFixers' {
declare module.exports: any;
}
// Filename aliases
declare module 'eslint-plugin-flowtype/bin/readmeAssertions.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/bin/readmeAssertions'>;
}
declare module 'eslint-plugin-flowtype/dist/index.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/index'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/booleanStyle.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/booleanStyle'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/defineFlowType.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/defineFlowType'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/delimiterDangle.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/delimiterDangle'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/genericSpacing.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/genericSpacing'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/noDupeKeys.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noDupeKeys'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/noMutableArray.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noMutableArray'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/noPrimitiveConstructorTypes.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noPrimitiveConstructorTypes'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/noTypesMissingFileAnnotation.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noTypesMissingFileAnnotation'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/noUnusedExpressions.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noUnusedExpressions'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/noWeakTypes.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noWeakTypes'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireParameterType.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireParameterType'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireReturnType.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireReturnType'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/requireVariableType.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireVariableType'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/semi.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/semi'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/sortKeys.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/sortKeys'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/spaceAfterTypeColon.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/spaceAfterTypeColon'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeGenericBracket.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/spaceBeforeGenericBracket'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/spaceBeforeTypeColon.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/spaceBeforeTypeColon'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateFunctions.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateFunctions'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeIndexer.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeIndexer'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeProperty.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateObjectTypeProperty'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateReturnType.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateReturnType'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypeCastExpression.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypeCastExpression'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/reporter.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/reporter'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/typeIdMatch.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeIdMatch'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/useFlowType.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/useFlowType'>;
}
declare module 'eslint-plugin-flowtype/dist/rules/validSyntax.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/validSyntax'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/checkFlowFileAnnotation.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/checkFlowFileAnnotation'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/fuzzyStringMatch.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/fuzzyStringMatch'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/getParameterName.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/getParameterName'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/getTokenAfterParens.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/getTokenAfterParens'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/getTokenBeforeParens.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/getTokenBeforeParens'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/index.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/index'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFile.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/isFlowFile'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/isFlowFileAnnotation.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/isFlowFileAnnotation'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/iterateFunctionNodes.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/iterateFunctionNodes'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/quoteName.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/quoteName'>;
}
declare module 'eslint-plugin-flowtype/dist/utilities/spacingFixers.js' {
declare module.exports: $Exports<'eslint-plugin-flowtype/dist/utilities/spacingFixers'>;
}

View file

@ -4204,4 +4204,4 @@ declare module "lodash/toPath" {
declare module "lodash/uniqueId" {
declare module.exports: $PropertyType<$Exports<"lodash">, "uniqueId">;
}
}

View file

@ -1,5 +1,5 @@
// flow-typed signature: 33b83b6284653250e74578cf4dbe6124
// flow-typed version: e282e4128f/redux_v3.x.x/flow_>=v0.33.x
// flow-typed signature: ec7daead5cb4fec5ab25fedbedef29e8
// flow-typed version: 2c04631d20/redux_v3.x.x/flow_>=v0.55.x
declare module 'redux' {
@ -55,55 +55,5 @@ declare module 'redux' {
declare export function combineReducers<O: Object, A>(reducers: O): CombinedReducer<$ObjMap<O, <S>(r: Reducer<S, any>) => S>, A>;
declare export function compose<A, B>(ab: (a: A) => B): (a: A) => B
declare export function compose<A, B, C>(
bc: (b: B) => C,
ab: (a: A) => B
): (a: A) => C
declare export function compose<A, B, C, D>(
cd: (c: C) => D,
bc: (b: B) => C,
ab: (a: A) => B
): (a: A) => D
declare export function compose<A, B, C, D, E>(
de: (d: D) => E,
cd: (c: C) => D,
bc: (b: B) => C,
ab: (a: A) => B
): (a: A) => E
declare export function compose<A, B, C, D, E, F>(
ef: (e: E) => F,
de: (d: D) => E,
cd: (c: C) => D,
bc: (b: B) => C,
ab: (a: A) => B
): (a: A) => F
declare export function compose<A, B, C, D, E, F, G>(
fg: (f: F) => G,
ef: (e: E) => F,
de: (d: D) => E,
cd: (c: C) => D,
bc: (b: B) => C,
ab: (a: A) => B
): (a: A) => G
declare export function compose<A, B, C, D, E, F, G, H>(
gh: (g: G) => H,
fg: (f: F) => G,
ef: (e: E) => F,
de: (d: D) => E,
cd: (c: C) => D,
bc: (b: B) => C,
ab: (a: A) => B
): (a: A) => H
declare export function compose<A, B, C, D, E, F, G, H, I>(
hi: (h: H) => I,
gh: (g: G) => H,
fg: (f: F) => G,
ef: (e: E) => F,
de: (d: D) => E,
cd: (c: C) => D,
bc: (b: B) => C,
ab: (a: A) => B
): (a: A) => I
}
declare export var compose: $Compose;
}

View file

@ -27,6 +27,7 @@
"eslint-config-airbnb": "^15.1.0",
"eslint-config-google": "^0.9.1",
"eslint-config-prettier": "^2.3.0",
"eslint-plugin-flowtype": "^2.39.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-prettier": "^2.1.2",

View file

@ -0,0 +1,73 @@
require 'rails_helper'
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
{ 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
it 'raises an error if no required permissions passed' do
expect do
post :status,
format: :json,
params: { resource: { type: 'Team', id: team.id } }
end
.to raise_error(NoMethodError)
end
it 'raises an error if no required resource type invalid' do
expect do
post :status,
format: :json,
params: { requiredPermissions: ['can_read_team'],
resource: { type: 'Banana', id: team.id } }
end
.to raise_error(ArgumentError)
end
it 'raises an error if no required resource id is not present' do
expect do
post :status,
format: :json,
params: { requiredPermissions: ['can_read_team'],
resource: { type: 'Team' } }
end
.to raise_error(ArgumentError)
end
context 'raises an error if can\'t find permission invalid when resource' do
it 'is absent' do
expect do
post :status,
format: :json,
params: { requiredPermissions: ['can_throw_bananas'] }
end
.to raise_error(ArgumentError)
end
it 'is present' do
expect do
post :status,
format: :json,
params: { requiredPermissions: ['can_throw_bananas'],
resource: { type: 'Team', id: team.id } }
end
.to raise_error(ArgumentError)
end
end
end
end

View file

@ -2119,6 +2119,12 @@ eslint-module-utils@^2.1.1:
debug "^2.6.8"
pkg-dir "^1.0.0"
eslint-plugin-flowtype@^2.39.1:
version "2.39.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.39.1.tgz#b5624622a0388bcd969f4351131232dcb9649cd5"
dependencies:
lodash "^4.15.0"
eslint-plugin-import@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f"
@ -3711,7 +3717,7 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.4:
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"