Во-первых, не думая о рекурсивных вещах, убедитесь, что у вас правильно определена логика правил.
Я попытался написать функцию проверки с использованием требуемого API, но не думаю, что она очень удобочитаема. Возможно, вы захотите изменить его позже. (Совет: напишите несколько модульных тестов!)
Пример ниже берет объект конфигурации правила и узел из вашего дерева. Он возвращает логическое значение, указывающее, соответствует ли узел требованиям.
const includedIn = xs => x => xs.includes(x);
// RuleSet -> Path -> bool
const isAllowed = ({ shouldBeLoggedIn = false, permissions = [] }) =>
({ RESTRICTIONS }) => (
(shouldBeLoggedIn ? RESTRICTIONS.shouldBeLoggedIn : true) &&
RESTRICTIONS.permissions.every(includedIn(permissions))
);
console.log(
[
{ RESTRICTIONS: { shouldBeLoggedIn: true, permissions: [ ] } },
{ RESTRICTIONS: { shouldBeLoggedIn: true, permissions: [ 'EMAIL' ] } },
{ RESTRICTIONS: { shouldBeLoggedIn: true, permissions: [ 'EMAIL', 'ADMIN' ] } }
].map(
isAllowed({ shouldBeLoggedIn: true, permissions: [ 'EMAIL'] })
)
)
С этим отсортированным фрагментом кода вы можете начать думать о том, как пройти по дереву. То, что вы в основном определяете, это как циклически проходить по каждому пути и когда возвращаться.
Если мы просто хотим войти в систему, это вопрос (1) проверки ROUTES
и (2) зацикливания на записей внутри объекта v.ROUTES
.
const traverse = obj => {
Object
.entries(obj)
.forEach(
([k, v]) => {
console.log(v.TO);
if (v.ROUTES) traverse(v.ROUTES)
}
)
};
traverse(getRoutes());
function getRoutes() {
return {
ACCOUNT: {
TO: '/account',
RESTRICTIONS: {
shouldBeLoggedIn: true,
},
ROUTES: {
PROFILE: {
TO: '/account/profile',
RESTRICTIONS: {
shouldBeLoggedIn: true,
},
ROUTES: {
INFORMATION: {
TO: '/account/profile/information',
RESTRICTIONS: {
shouldBeLoggedIn: true,
permissions: ['EMAIL'],
},
},
PASSWORD: {
TO: '/account/profile/password',
RESTRICTIONS: {
shouldBeLoggedIn: true,
permissions: ['EMAIL', 'ADMIN'],
},
},
},
},
COLLECTIONS: {
TO: '/account/collections',
RESTRICTIONS: {
shouldBeLoggedIn: true,
permissions: ['ADMIN'],
},
},
LIKES: {
TO: '/account/likes',
RESTRICTIONS: {
shouldBeLoggedIn: true,
},
},
},
},
};
};
Затем наступает самое сложное: создание новой древовидной структуры.
Я решил сделать два шага:
- Сначала, мы
filter
выводим значения, которые не проходят проверку, - Во-вторых, мы проверяем, нужно ли нам беспокоиться о каких-либо дочерних маршрутах.
Если есть дочерние маршруты,мы создаем новый объект пути с отфильтрованным значением ROUTES.
const traverse = (obj, pred) => Object
.fromEntries(
Object
.entries(obj)
.filter(
([k, v]) => pred(v) // Get rid of the paths that don't match restrictions
)
.map(
([k, v]) => [
k, v.ROUTES
// If there are child paths, filter those as well (i.e. recurse)
? Object.assign({}, v, { ROUTES: traverse(v.ROUTES, pred) })
: v
]
)
);
const includedIn = xs => x => xs.includes(x);
const isAllowed = ({ shouldBeLoggedIn = false, permissions = [] }) =>
({ RESTRICTIONS }) => (
(shouldBeLoggedIn ? RESTRICTIONS.shouldBeLoggedIn : true) &&
(RESTRICTIONS.permissions || []).every(includedIn(permissions))
);
console.log(
traverse(
getRoutes(),
isAllowed({ shouldBeLoggedIn: true, permissions: [ 'EMAIL'] })
)
)
function getRoutes() {
return {
ACCOUNT: {
TO: '/account',
RESTRICTIONS: {
shouldBeLoggedIn: true,
},
ROUTES: {
PROFILE: {
TO: '/account/profile',
RESTRICTIONS: {
shouldBeLoggedIn: true,
},
ROUTES: {
INFORMATION: {
TO: '/account/profile/information',
RESTRICTIONS: {
shouldBeLoggedIn: true,
permissions: ['EMAIL'],
},
},
PASSWORD: {
TO: '/account/profile/password',
RESTRICTIONS: {
shouldBeLoggedIn: true,
permissions: ['EMAIL', 'ADMIN'],
},
},
},
},
COLLECTIONS: {
TO: '/account/collections',
RESTRICTIONS: {
shouldBeLoggedIn: true,
permissions: ['ADMIN'],
},
},
LIKES: {
TO: '/account/likes',
RESTRICTIONS: {
shouldBeLoggedIn: true,
},
},
},
},
};
};
Надеюсь, этот пример поможет вам начать работу и позволит вам написать собственную / отшлифованную версию. Дайте мне знать, если я пропустил какие-либо требования.