I’m making a call to the method called loadFilteredPolicy() by passing the filter object which is my hashkey from client side but its method get() or hgetall() never get executed and control goes out without fetch the data from Redis server without throwing any error. Let me confirm you that I can debug and can assure that there is an object of redis on this.redisInstance but not sure why then method do not execute. I can fetch the same data directly using the redis-cli by the command hgetall .
One more thing I want to tell you is that when I uncomment Promise line just above (await this.redisInstance.hgetall) and debug again then this.redisInstance returns undefined. I’m not sure what is happening. I have worked with JS before but not much TypeScript and I’m stuck in this error since Morning and now it is evening.
This is my TypeScript code (adapter.ts) which gets compiled to JS file :-
import { Helper, Model, FilteredAdapter } from 'casbin';
import * as redis from 'redis';
interface IConnectionOptions {
host: string;
port: number;
}
class Line {
ptype: string;
v0: string;
v1: string;
v2: string;
v3: string;
v4: string;
v5: string;
}
export class RedisAdapter implements FilteredAdapter {
private redisInstance = null;
private policies = null;
private filtered = false;
public isFiltered(): boolean {
return this.filtered;
}
private deliveredOptions = {
retry_strategy(options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
return new Error('The server refused the connection');
}
if (options.total_retry_time > 1000 * 60 * 60) {
return new Error('Retry time exhausted');
}
if (options.attempt > 10) {
return undefined;
}
// reconnect after
return Math.min(options.attempt * 100, 300);
},
};
/**
* Helper Methods
*/
savePolicyLine(ptype, rule) {
const line = new Line();
line.ptype = ptype;
if (rule.length > 0) {
line.v0 = rule[0];
}
if (rule.length > 1) {
line.v1 = rule[1];
}
if (rule.length > 2) {
line.v2 = rule[2];
}
if (rule.length > 3) {
line.v3 = rule[3];
}
if (rule.length > 4) {
line.v4 = rule[4];
}
if (rule.length > 5) {
line.v5 = rule[5];
}
return line;
}
loadPolicyLine(line, model) {
console.log("load Policies line called");
let lineText = line.ptype;
if (line.v0) {
lineText += ", " + line.v0;
}
if (line.v1) {
lineText += ", " + line.v1;
}
if (line.v2) {
lineText += ", " + line.v2;
}
if (line.v3) {
lineText += ", " + line.v3;
}
if (line.v4) {
lineText += ", " + line.v4;
}
if (line.v5) {
lineText += ", " + line.v5;
}
Helper.loadPolicyLine(lineText, model);
}
storePolicies(policies) {
return new Promise((resolve, reject) => {
console.log({ r: this.redisInstance });
this.redisInstance.del('policies');
this.redisInstance.set('policies', JSON.stringify(policies), (err, reply) => {
if (err) {
reject(err);
} else {
resolve(reply);
}
});
});
}
reducePolicies(policies, ptype, rule) {
let i = rule.length;
let policyIndex = policies.fieldIndex((policy) => {
let flag = false;
flag = policy.ptype === ptype ? true : false;
flag = i > 5 && policy.v5 === rule[5] ? true : false;
flag = i > 4 && policy.v4 === rule[4] ? true : false;
flag = i > 3 && policy.v3 === rule[3] ? true : false;
flag = i > 2 && policy.v2 === rule[2] ? true : false;
flag = i > 1 && policy.v0 === rule[1] ? true : false;
return flag;
});
if (policyIndex !== -1) {
return policies.splice(policyIndex, 1);
}
return [];
}
constructor(options: IConnectionOptions) {
this.redisInstance = redis.createClient(
{
...options,
...this.deliveredOptions,
},
);
}
static async newAdapter(options: IConnectionOptions) {
const adapter = new RedisAdapter(options);
await new Promise(resolve => adapter.redisInstance.on('connect', resolve));
return adapter;
}
/**
* Adapter Methods
*/
public async loadPolicy(model) {
this.redisInstance.get("policies", (err, policies) => {
var AdapterRef = this;
console.log("Loading Policies...n", policies);
if (!err) {
policies = JSON.parse(policies);
this.policies = policies;//For add and remove policies methods
console.log(policies);
policies.forEach(function (policy, index) {
AdapterRef.loadPolicyLine(policy, model);
});
console.log("Policies are loaded");
} else {
return err;
}
});
}
public async loadFilteredPolicy(model: Model, filter: object): Promise<void> {
let key = filter['hashKey'];
//return await new Promise(function (resolve, reject) {
this.redisInstance.hgetall(key, (err, policies) => {
if (err) {
reject(err);
} else {
resolve(err);
var AdapterRef = this;
console.log("Loading filtered Policies...n", policies);
policies = JSON.parse(policies);
this.policies = policies;//For add and remove policies methods
console.log(policies);
policies.forEach(function (policy, index) {
AdapterRef.loadPolicyLine(policy, model);
});
console.log("Filtered Policies are loaded...");
this.filtered = true;
}
});
//})
}
public async savePolicy(model: Model): Promise<boolean> {
const policyRuleAST = model.model.get("p");
const groupingPolicyAST = model.model.get("g");
let policies = [];
//var rows2 = <Array<any>>policyRuleAST;
//var rows2 = <Array<any>>groupingPolicyAST;
for (const [ptype, ast] of Object.entries(policyRuleAST)) {
for (const rule of ast.policy) {
const line = this.savePolicyLine(ptype, rule);
policies.push(line);
}
}
for (const [ptype, ast] of Object.entries(groupingPolicyAST)) {
for (const rule of ast.policy) {
const line = this.savePolicyLine(ptype, rule);
policies.push(line);
}
}
//this.storePolicies(policies);
return new Promise((resolve, reject) => {
console.log({ r: this.redisInstance });
this.redisInstance.del('policies');
this.redisInstance.set('policies', JSON.stringify(policies), (err, reply) => {
if (err) {
reject(err);
} else {
resolve(true);
}
});
});
}
async addPolicy(sec, ptype, rule) {
const line = this.savePolicyLine(ptype, rule);
this.policies.push(line);
this.storePolicies(this.policies);
//reSave the policies
}
async removePolicy(sec, ptype, rule) {
let result = this.reducePolicies(this.policies, ptype, rule);
//the modified policies
if (result.length) { //if length>0
this.policies = result;
//Store in Redis
this.storePolicies(this.policies);
} else {
// console.IN("No Policy found");
throw new Error("No Policy Found");
}
}
public async removeFilteredPolicy(sec: string, ptype: string, fieldIndex: number, ...fieldValues: string[]) {
throw new Error("Method not implemented");
}
}
Corresponding adapter.js :-
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisAdapter = void 0;
const casbin_1 = require("casbin");
const redis = require("redis");
class Line {
}
class RedisAdapter {
constructor(options) {
this.redisInstance = null;
this.policies = null;
this.filtered = false;
this.deliveredOptions = {
retry_strategy(options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
return new Error('The server refused the connection');
}
if (options.total_retry_time > 1000 * 60 * 60) {
return new Error('Retry time exhausted');
}
if (options.attempt > 10) {
return undefined;
}
return Math.min(options.attempt * 100, 300);
},
};
this.redisInstance = redis.createClient(Object.assign(Object.assign({}, options), this.deliveredOptions));
}
isFiltered() {
return this.filtered;
}
savePolicyLine(ptype, rule) {
const line = new Line();
line.ptype = ptype;
if (rule.length > 0) {
line.v0 = rule[0];
}
if (rule.length > 1) {
line.v1 = rule[1];
}
if (rule.length > 2) {
line.v2 = rule[2];
}
if (rule.length > 3) {
line.v3 = rule[3];
}
if (rule.length > 4) {
line.v4 = rule[4];
}
if (rule.length > 5) {
line.v5 = rule[5];
}
return line;
}
loadPolicyLine(line, model) {
console.log("load Policies line called");
let lineText = line.ptype;
if (line.v0) {
lineText += ", " + line.v0;
}
if (line.v1) {
lineText += ", " + line.v1;
}
if (line.v2) {
lineText += ", " + line.v2;
}
if (line.v3) {
lineText += ", " + line.v3;
}
if (line.v4) {
lineText += ", " + line.v4;
}
if (line.v5) {
lineText += ", " + line.v5;
}
casbin_1.Helper.loadPolicyLine(lineText, model);
}
storePolicies(policies) {
return new Promise((resolve, reject) => {
console.log({ r: this.redisInstance });
this.redisInstance.del('policies');
this.redisInstance.set('policies', JSON.stringify(policies), (err, reply) => {
if (err) {
reject(err);
}
else {
resolve(reply);
}
});
});
}
reducePolicies(policies, ptype, rule) {
let i = rule.length;
let policyIndex = policies.fieldIndex((policy) => {
let flag = false;
flag = policy.ptype === ptype ? true : false;
flag = i > 5 && policy.v5 === rule[5] ? true : false;
flag = i > 4 && policy.v4 === rule[4] ? true : false;
flag = i > 3 && policy.v3 === rule[3] ? true : false;
flag = i > 2 && policy.v2 === rule[2] ? true : false;
flag = i > 1 && policy.v0 === rule[1] ? true : false;
return flag;
});
if (policyIndex !== -1) {
return policies.splice(policyIndex, 1);
}
return [];
}
static async newAdapter(options) {
const adapter = new RedisAdapter(options);
await new Promise(resolve => adapter.redisInstance.on('connect', resolve));
return adapter;
}
async loadPolicy(model) {
this.redisInstance.get("policies", (err, policies) => {
var AdapterRef = this;
console.log("Loading Policies...n", policies);
if (!err) {
policies = JSON.parse(policies);
this.policies = policies;
console.log(policies);
policies.forEach(function (policy, index) {
AdapterRef.loadPolicyLine(policy, model);
});
console.log("Policies are loaded");
}
else {
return err;
}
});
}
async loadFilteredPolicy(model, filter) {
let key = filter['hashKey'];
//await new Promise(function (resolve, reject) {
await this.redisInstance.hgetall(key, (err, policies) => {
if (err) {
return err;
}
else {
var AdapterRef = this;
console.log("Loading filtered Policies...n", policies);
policies = JSON.parse(policies);
this.policies = policies;
console.log(policies);
policies.forEach(function (policy, index) {
AdapterRef.loadPolicyLine(policy, model);
});
console.log("Filtered Policies are loaded...");
this.filtered = true;
}
});
// });
}
async savePolicy(model) {
const policyRuleAST = model.model.get("p");
const groupingPolicyAST = model.model.get("g");
let policies = [];
for (const [ptype, ast] of Object.entries(policyRuleAST)) {
for (const rule of ast.policy) {
const line = this.savePolicyLine(ptype, rule);
policies.push(line);
}
}
for (const [ptype, ast] of Object.entries(groupingPolicyAST)) {
for (const rule of ast.policy) {
const line = this.savePolicyLine(ptype, rule);
policies.push(line);
}
}
return new Promise((resolve, reject) => {
console.log({ r: this.redisInstance });
this.redisInstance.del('policies');
this.redisInstance.set('policies', JSON.stringify(policies), (err, reply) => {
if (err) {
reject(err);
}
else {
resolve(true);
}
});
});
}
async addPolicy(sec, ptype, rule) {
const line = this.savePolicyLine(ptype, rule);
this.policies.push(line);
this.storePolicies(this.policies);
}
async removePolicy(sec, ptype, rule) {
let result = this.reducePolicies(this.policies, ptype, rule);
if (result.length) {
this.policies = result;
this.storePolicies(this.policies);
}
else {
throw new Error("No Policy Found");
}
}
async removeFilteredPolicy(sec, ptype, fieldIndex, ...fieldValues) {
throw new Error("Method not implemented");
}
}
exports.RedisAdapter = RedisAdapter;
I would really appreciate if you can suggest me to make this code work. My GitHub link for this adapter source code is available here https://github.com/vinod827/node-casbin-redis-adapter.git
Thank you,
[Updated code, not working still]
async loadFilteredPolicy(model, filter) {
let key = filter['hashKey'];
let filteredPolicies = await this.getFilteredPolicies(key).then(data => { return data });
console.log(filteredPolicies);
}
getFilteredPolicies = async (hashKey) => {
return await new Promise(function (resolve, reject) {
this.redisInstance.hgetall((hashKey), function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
})
})
};
2
Answers
I think you need to re-learn the use of
Promise
, at least you need to know when to useresolve()
.See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
By the way, don’t use
typescript
as an excuse, onlyjavascript
here.I am here to provide you with a minimal implementation that works:
Could you try the below code on https://github.com/vinod827/node-casbin-redis-adapter/blob/master/src/adapter.ts#L150
?