mercredi 15 décembre 2021

A design pattern or architecture related to rendering components based on roles and permissions?

I want to know if there's a software design pattern that can solve a challenge I'm facing. I have a web application that works with several different user role types like SuperAdmin, Admin, Reseller, Dealer, Customer, Volunteer, etc...

And our React code is littered with if statements that check a person's role as well as other conditions to decide "what type of ui component" to present them. Here's a very simplified example:

// Container.js
<div>
   <h1>Something</h1>
   {['SuperAdmin', 'Admin'].includes(user.role) && (<input type="text" value="{record.first_name}" />)}
   {['Reseller', 'Dealer'].includes(user.role) && (<span className="border-2">{record.first_name}</span>)}
   {['Customer', 'Volunteer'].includes(user.role) && process.env.NETWORK_MODE === 'intranet' && (
    <div className="bg-grey">
        <span>Created at {record.create_at}</span>
        <span>This customer wants to be anonymous</span>
    </div>
   )}
   {['Reseller'].includes(user.role) && user.accountBalance > 0 && user.statementDate > (new Date()) && (
     <div>Please pay your bills</div>
   )}
</div>

The above example is very difficult for us to manage because there are over 100 components . When we need to change the access permissions for a role type or add other conditions, we have to go through every component and look for any stray if statements. We were thinking of writing some automated tests, but feel like we need to refactor the UI code before that's practical.

Are there some software engineering design patterns that would be relevant to my situation? If so, just state the names and i'l read up on it.


Right now, I'm contemplating an approach like this:

// Container.js
<div>
   <h1>Something</h1>
   <Customer user={user} />
   <BillNotice user={user} />
</div>

// Customer.js
const Customer = ({user}) => {
   if(['Admin', 'SuperAdmin'].includes(user.role)) return <CustomerVersion1 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <CustomerVersion2 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <CustomerVersion3 />;
   ...etc...
}

const CustomerVersion1 = () => <Component />;
const CustomerVersion2 = () => <Component />;
const CustomerVersion3 = () => <Component />;

...etc...

// BillNotice.js
const BillNotice = ({user}) => {
   if(['Admin', 'SuperAdmin'].includes(user.role)) return <BillNotice1 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <BillNotice2 />;
   if(['Role', 'Role'].includes(user.role) && condition1 && condition2 ...etc...) return <BillNotice3 />;
   ...etc...
}

const BillNotice1 = () => <Component />;
const BillNotice2 = () => <Component />;
const BillNotice3 = () => <Component />;

Basically I'm making each component responsible for deciding how it should present itself. Then I can write unit test for each component by mocking the user argument and see if the appropriate component is returned. If this approach actually has a name, that would be great and I can read up more on it!

Aucun commentaire:

Enregistrer un commentaire