item level permission in casl - javascript

In my case, the permission is not based on role and also there won't be one central permission object which I can put after login. The permission happens on item level. By permission happens on item level, I mean to say if I have an object of todos which has around 4 items then each item might have different permission levied as below
const todos = {
data: [
{
id: 1,
title: "Todo 1",
sub: "Let's start!",
permissions: [
{
action: "read",
subject: "todo"
},
{
action: "delete",
subject: "todo"
}
],
__typename: "Todo"
},
{ id: 2, title: "Todo 2", sub: "Let's start 2!", __typename: "Todo" },
{
id: 3,
title: "Todo 3",
sub: "Let's start 3!",
permissions: [
{
action: "read",
subject: "todo"
}
],
__typename: "Todo"
},
{ id: 4, title: "Todo 4", sub: "Let's start 4!", __typename: "Todo" }
]
};
For such case, how do i handle permission using casl?
This is what I was doing
ability.js
import { Ability } from "#casl/ability";
export default new Ability([
{
action: "read",
subject: "todo"
},
{
action: "delete",
subject: "todo"
}
]);
can.js
import { createContext } from "react";
import { createContextualCan } from "#casl/react";
export const AbilityContext = createContext();
export const Can = createContextualCan(AbilityContext.Consumer);
index.js
import React from "react";
import { Ability } from "#casl/ability";
import { AbilityContext, Can } from "./can";
const todos = {
data: [
{
id: 1,
title: "Todo 1",
sub: "Let's start!",
permissions: [
{
action: "read",
subject: "todo"
},
{
action: "delete",
subject: "todo"
}
],
__typename: "Todo"
},
{ id: 2, title: "Todo 2", sub: "Let's start 2!", __typename: "Todo" },
{
id: 3,
title: "Todo 3",
sub: "Let's start 3!",
permissions: [
{
action: "read",
subject: "todo"
}
],
__typename: "Todo"
},
{ id: 4, title: "Todo 4", sub: "Let's start 4!", __typename: "Todo" }
]
};
const Permission = () => {
const ability = React.useContext(AbilityContext);
return (
<>
<h1>Permission Based System</h1>
{todos.data.map((todo) => (
<div key={todo.id}>
<Can I="read" a="todo">
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ display: "flex", flexDirection: "column" }}>
<h1 style={{ margin: 0 }}>{todo.title}</h1>
<h3>{todo.sub}</h3>
</div>
<Can I="delete" a="todo">
<span>Delete</span>
</Can>
</div>
</Can>
</div>
))}
</>
);
};
export default Permission;
I have created a sandbox as well
https://codesandbox.io/s/mystifying-leaf-i968u?file=/src/permission/index.js:0-1472

For case, when you have permissions embedded into entities, you don't need CASL. You can use it but it won't bring any benefits. Why?
You duplicate subject Todo in response for every todo item in an array for no purpose. You know that you requested Todo and even GraphQLs __typename property tells you this.
It requires to create an Ability instance per subject type (in this case Todo), iterate over all todos, generate an object of conditions by id (so we can later check ability.can('read', todo)).
All this are useless, brings additional complexity and not benefits at all.
A better structure for your case would be this:
permissions: {
canRead: true,
canDelete: true
}
So, you can easily check permissions on UI - todo.permissions.canRead

Related

how to console.log in react-simple-chatbot

i try to do a bot who works as i want but i would like to implement some features (send mails, popups etc..) but i don't understand how can i console.log any elements .
there is my code:
import React from "react";
import ChatBot from "react-simple-chatbot";
import props from 'prop-types';
function Sendemail() {
console.log("test")
}
function CustomChatbot(props) {
const config = {
width: "300px",
height: "400px",
floating: true
};
return <ChatBot steps={steps} {...config} />;
}
export default CustomChatbot;
const steps = [
{
id: "Greet",
message: "Hello, Welcome to our website",
trigger: "Ask Name",
},
{
id: "Ask Name",
message: "Please type your name?",
trigger: "Waiting user input for name"
},
{
id: "Waiting user input for name",
user: true,
trigger: "ask questions"
},
{
id: "ask questions",
message: "Hi {previousValue}, I am Tony the bot, How can i help you!",
trigger: "informations"
},
{
id: "informations",
options: [
{
value: "Who founded Babycare",
label: "Who founded Babycare",
trigger: "Who founded"
},
{
value: "Contact",
label: "Contact",
trigger: "Contact"
}
]
},
{
id: "Who founded",
message: "ERWAN",
trigger: "informations"
},
{
id: "Contact",
message: "Ok, first, enter your adress-mail",
trigger: "Ask email"
},
{
id: "Ask email",
user: true,
trigger: "get-email"
},
{
id: "get-email",
message: "ok, {previousValue} !! Type your value now, please be fair",
trigger: "your question"
},
// {
// id: "your question",
// message: "Ok. Now, enter your message to the staff. Please be fair !!",
// trigger: "your message"
// },
{
id: "your question",
user: true,
trigger: "send mail"
},
{
id: "send mail",
message: "ok, there is your message : {previousValue} !! The support team will answer you as possible",
trigger: "thelast"
},
{
id: "thelast",
component: (
<div> {Sendemail()} </div>
),
end: true
},
];
THis code is my last try, on the last proposition, i just tried to put my function Sendmail() in the id:"the last". But i can't call function in array. I am quite lost, how can i do that?
Thanks for yours answers!
I am not sure what you are trying to achieve with Sendemail. But maybe this code will work
function SendEmail()
{
console.log('test')
return null
}
// in steps
{
id: "thelast",
component: (
<SendEmail/>
),
end: true
}

How do I get hover effect on rows with material-table react?

I have used material-table with react to render my data. I want to show the hover effect and cursor pointer effect when I hover on rows. But I couldn't find this even in the documentation.
IN SHORT- I want to highlight some colors on a row when the cursor hovers to that row.
Note: I also found a similar question and answer here, but they used another state just to hover which downgrades the performance if I increase the data like thousands rows. Hence it's bad practice so here I am asking for alternate solutions for the same.
Here is my codesandbox link
Below I also pasted my code.
App.js
import React, { useState } from 'react';
import './App.css';
import MaterialTable from 'material-table'
const empList = [
{ id: 1, name: "Neeraj", email: 'neeraj#gmail.com', phone: 9876543210, city: "Bangalore" },
{ id: 2, name: "Raj", email: 'raj#gmail.com', phone: 9812345678, city: "Chennai" },
{ id: 3, name: "David", email: 'david342#gmail.com', phone: 7896536289, city: "Jaipur" },
{ id: 4, name: "Vikas", email: 'vikas75#gmail.com', phone: 9087654321, city: "Hyderabad" },
]
function App() {
const [data, setData] = useState(empList)
const columns = [
{ title: "ID", field: "id", editable: false },
{ title: "Name", field: "name" },
{ title: "Email", field: "email" },
{ title: "Phone Number", field: 'phone', },
{ title: "City", field: "city", }
]
return (
<div className="App">
<h1 align="center">React-App</h1>
<h4 align='center'>Material Table with CRUD operation</h4>
<MaterialTable
title="Employee Data"
data={data}
columns={columns}
editable={{
onRowAdd: (newRow) => new Promise((resolve, reject) => {
const updatedRows = [...data, { id: Math.floor(Math.random() * 100), ...newRow }]
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowDelete: selectedRow => new Promise((resolve, reject) => {
const index = selectedRow.tableData.id;
const updatedRows = [...data]
updatedRows.splice(index, 1)
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowUpdate:(updatedRow,oldRow)=>new Promise((resolve,reject)=>{
const index=oldRow.tableData.id;
const updatedRows=[...data]
updatedRows[index]=updatedRow
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
})
}}
options={{
actionsColumnIndex: -1, addRowPosition: "first"
}}
/>
</div>
);
}
export default App;
add in your inside of your CSS
tbody tr:hover {
background-color: blue;
color: white;
cursor: pointer;
}

Cannot read property 'columns' of undefined

I am trying to read json data from server and map. When I enter the same data with hand as a list to the code. It works. But when I am trying to read data from the server it does not work. I know problem is in mapping but I could not figure out. What can be the problem? This is code and json data. Thanks
class App extends Component {
constructor(props) {
super(props);
this.state = {
boards: {
id: null,
name: null,
owner: null,
columns:[]
}
};
}
getBoard() {
fetch("http://localhost:3001/boards/2")
.then(res => res.json())
.then(list => console.log(list))
.then(list => this.setState({boards: list}));
}
componentWillMount() {
this.getBoard();
}
{this.state.boards.columns.map((column, columnIndex) => (<Column
status={this.state.addModalShow}
onModalShow={this.onModalShow}
onHide={addModalClose}
addCard={this.addTask}
column={column}
columnIndex={columnIndex}
key={columnIndex}
onMoveLeft={cardIndex => this.handleMove(columnIndex, cardIndex, DIRECTION_LEFT)}
onMoveRight={cardIndex => this.handleMove(columnIndex, cardIndex, DIRECTION_RIGHT)}
onAddCard={() => this.handleAdd(columnIndex)}
deleteColumn={() => this.deleteColumn(columnIndex)}
addColumn={() => this.handleAdd()}
deleteTask={cardIndex => this.deleteTask(columnIndex, cardIndex)}/>))}
{
id: 2,
name: "Board3",
owner: "Ali",
-columns: [
-{
id: 5,
name: "eee",
cards: [
-{
id: 5,
name: "TestA",
description: "Desc",
link: "google.com",
deadline: "2013-04-06"
},
-{
id: 8,
name: "testB",
description: "Desc",
link: "google.com",
deadline: null
}
]
},
-{
id: 6,
name: "ff",
cards: []
}
]
}
But you don't return anything inside the function that prints the response to the console. Please read about Promise chaining.
Either return list in your second 'then' block or remove it completely.

Updating a specific object in an array

I'm writing an SPA in Svelte. Now, I'm fairly new to the concepts of ES6 so I'm having difficulties wrapping my head around some basic concepts.
I have a store:
import { writable } from "svelte/store";
function selectedOptions() {
const selection = writable([
{
id: 1,
title: "Title 1",
selections: []
},
{
id: 2,
title: "Title 2",
selections: []
},
{
id: 3,
title: "Title 3",
selections: []
},
{
id: 4,
title: "Title 4",
selections: []
},
{
id: 5,
title: "Title 5",
selections: []
},
{
id: 6,
title: "Title 6",
selections: []
}
]);
return {
subscribe: selection.subscribe,
updateSelection: item => {
selection.update((items) => {
//I want to update the object with the same id as the object
//I'm passing in to the method.
});
};
}
}
export default selectedOptions();
In my component, I want to pass an object and update the corresponding object in my array with provided values:
function handleChange(e) {
selectedOptions.updateSelection({
id: 1, title: "Title 1", selections: ["Option 1, Option 2"]
});
}
How do I "replace" an existing object with a new one thus triggering an update to all components that are subscribing to the store?
Use the spread syntax to copy all the original keys, then add the one you want to modify:
selection.update(items => {
return {
...items,
[item.id]: item
}
});
You could use the array method map and merge the new and old object if the id matches or just return the old object as-is if the id doesn't match.
updateSelection: item => {
selection.update(items => {
return items.map(i => (i.id === item.id ? { ...i, ...item } : i));
});
};

What is the best implementation for accessing single Redux's store in deeper container component?

Creating a container component by connect() function with defined mapStateToProps gives access to state (store.getState()) of entire tree state.
For example, I have combined state tree:
{
loaded: true,
threads: [
{
id: 1,
title: "Thread 1",
messages: [
{
id: 1,
text: "Message 1"
}
]
},
{
id: 1,
title: "Thread 2",
messages: [
{
id: 2,
text: "Message 2"
},
{
id: 3,
text: "Message 3"
}
]
}
]
}
How should I access particulary messages of given thread?
const mapStateToProps = (state) => {
return {
messages: getMessagesByThread(state.threads, <threadId>)
}
}
In this case lets assume that I don't want to have 'activeThreadId' param in store state tree (store.getState().activeThreadId).
What is the best other possibility to provide threadId?
If you have threadId as a prop on your component, you could pass it over via the ownProps param:
const mapStateToProps = (state, ownProps) => {
return {
messages: getMessagesByThread(state.threads, ownProps.threadId)
}
}
This is the best way to go I think.

Categories