I'm trying to implement a password validation system to accept a user's "update password" request.
I'm using Strapi as my backend and Next.js as my frontend.
Strapi says it uses "bcrypt with autogenerated salt and hash" (mentioned here)
so the stored password is encrypted.
I use MongoDB for my database.
The user is registered, I use it for all my tests so I know the password is correct.
Question: What am I doing wrong ?
I am very new to this.
Thank you.
Comparison code in my controller file :
I did this but it always return false (backend):
const comparePass = await bcrypt.compare(oldPassword, user.password);
if(comparePass){
ctx.throw(200, 'valid password !')
}else{
ctx.throw(400, 'wrong password !')
}
When I debbug I can see in my console that:
oldPassword = 'maddie'
user.password ='$2a$10$tEXnOI.OASIeL0BsG2Go/ecHjXn38xNnM9HVHJlPnBFpqSRe6Yyf6'
which is correct
Hashing code- In my backend, this code below used to hash the password when the user register:
hashPassword(user = {}) {
return new Promise(resolve => {
if (!user.password || this.isHashed(user.password)) {
resolve(null);
} else {
bcrypt.hash(`${user.password}`, 10, (err, hash) => {
resolve(hash);
});
}
});
},
Complete code in my User.js service including the hashing code:
'use strict';
/**
* User.js service
*
* #description: A set of functions similar to controller's actions to avoid code duplication.
*/
const bcrypt = require('bcryptjs');
const crypto = require('crypto');
const { sanitizeEntity, getAbsoluteServerUrl } = require('strapi-utils');
module.exports = {
/**
* Promise to count users
*
* #return {Promise}
*/
count(params) {
return strapi.query('user', 'users-permissions').count(params);
},
/**
* Promise to search count users
*
* #return {Promise}
*/
countSearch(params) {
return strapi.query('user', 'users-permissions').countSearch(params);
},
/**
* Promise to add a/an user.
* #return {Promise}
*/
async add(values) {
if (values.password) {
values.password = await strapi.plugins['users-permissions'].services.user.hashPassword(
values
);
}
return strapi.query('user', 'users-permissions').create(values);
},
/**
* Promise to edit a/an user.
* #return {Promise}
*/
async edit(params, values) {
if (values.password) {
values.password = await strapi.plugins['users-permissions'].services.user.hashPassword(
values
);
}
return strapi.query('user', 'users-permissions').update(params, values);
},
/**
* Promise to fetch a/an user.
* #return {Promise}
*/
fetch(params, populate) {
return strapi.query('user', 'users-permissions').findOne(params, populate);
},
/**
* Promise to fetch authenticated user.
* #return {Promise}
*/
fetchAuthenticatedUser(id) {
return strapi.query('user', 'users-permissions').findOne({ id }, ['role']);
},
/**
* Promise to fetch all users.
* #return {Promise}
*/
fetchAll(params, populate) {
return strapi.query('user', 'users-permissions').find(params, populate);
},
hashPassword(user = {}) {
return new Promise(resolve => {
if (!user.password || this.isHashed(user.password)) {
resolve(null);
} else {
bcrypt.hash(`${user.password}`, 10, (err, hash) => {
resolve(hash);
});
}
});
},
isHashed(password) {
if (typeof password !== 'string' || !password) {
return false;
}
return password.split('$').length === 4;
},
/**
* Promise to remove a/an user.
* #return {Promise}
*/
async remove(params) {
return strapi.query('user', 'users-permissions').delete(params);
},
async removeAll(params) {
return strapi.query('user', 'users-permissions').delete(params);
},
validatePassword(password, hash) {
return bcrypt.compare(password, hash);
},
Related
I am trying to create a discord bot for the first time in my life so I would like to say that my code is far from being perfect and optimized ^^ '
I have a problem, TypeError: Cannot read properties of null (reading 'play').
The problem occurs when I try the "skip" command. Everything works fine but when I try the command I have absolutely nothing in my debugging console and only an error on discord returned by the bot.
Play command:
const { VoiceConnection } = require("discord.js");
const { Command, CommandoMessage } = require("discord.js-commando");
const ytdl = require('ytdl-core-discord');
const {UserNotInVoiceChannel } = require(`../../strings.json`);
module.exports = class PlayCommand extends Command {
constructor(client) {
super(client, {
name: `play`,
aliases: [`p`],
group: `music`,
memberName: `play`,
description: `Lit une video youtube`,
args: [
{
key: `query`,
prompt: `Quelle musique veux tu lire?`,
type: `string`
}
]
});
}
/**
*
* #param {CommandoMessage} message
* #param {String} query
*/
async run(message, { query }) {
const server = message.client.server;
if(!message.member.voice.channel){
return message.say(UserNotInVoiceChannel);
}
await message.member.voice.channel.join().then((connection) => {
if (server.currentVideo.url != "") {
server.queue.push({ title: "", url: query });
return message.say("Ajouté a la file d'attente")
}
server.currentVideo = { title: "", url: query };
this.runVideo(message, connection, query);
});
}
/**
*
* #param {*} message
* #param {VoiceConnection} connection
* #param {*} video
*/
async runVideo(message, connection, videoUrl) {
const server = message.client.server;
const dispatcher = connection.play(await ytdl(videoUrl, {filter: 'audioonly'}), { type: 'opus' });
server.queue.shift();
server.dispatcher = dispatcher;
server.connection = null
dispatcher.on('finish', () => {
if (server.queue[0]) {
server.currentVideo = server.queue[0];
return this.runVideo(message, connection, server.currentVideo.url);
}
});
return message.say("En train de jouer :notes:");
}
}
Skip command:
const { Command, CommandoMessage } = require("discord.js-commando");
const {UserNotInVoiceChannel, BotNotInVoiceChannel } = require(`../../strings.json`);
module.exports = class skipCommand extends Command {
constructor(client) {
super(client, {
name: `skip`,
group: `music`,
memberName: `skip`,
description: `Skip la video.`
});
}
/**
*
* #param {CommandoMessage} message
* #param {String} query
*/
async run(message) {
const voiceChannel = message.member.voice.channel;
const server = message.client.server;
if(!voiceChannel){
return message.say(UserNotInVoiceChannel)
}
if (!message.client.voice.connections.first()) {
return message.say(BotNotInVoiceChannel);
}
server.queue.shift();
if (!server.queue[0]) {
server.currentVideo = {url: "", title: "Rien pour le moment"}
}
server.currentVideo = server.queue[0];
server.connection.play(await ytdl(server.currentVideo.url, {filter: 'audioonly'}), { type: 'opus' } );
return message.say(":fast_foward: Ignoré :thumbsup:");
}
}
The error says that it cannot read properties of null (reading 'play') in your skip command. It means that server.connection is null.
If you check your play command, you can see that inside runVideo you set server.connection = null.
Did you mean to assign the connection passed to runVideo?
async runVideo(message, connection, videoUrl) {
const server = message.client.server;
const dispatcher = connection.play(
await ytdl(videoUrl, { filter: 'audioonly' }),
{ type: 'opus' },
);
server.queue.shift();
server.dispatcher = dispatcher;
server.connection = connection;
dispatcher.on('finish', () => {
if (server.queue[0]) {
server.currentVideo = server.queue[0];
return this.runVideo(message, connection, server.currentVideo.url);
}
});
return message.say('En train de jouer :notes:');
}
In Java land I would do something like
#Transactional
FormData update(FormData updatedFormData) {
var result = dsl
.query(
"select id, formData from formStuff where formId = ?",
updatedFormData.formId
);
var result2 = dsl
.query(
"select reference from referenceStuff where formStuffId = ?",
result.get("id")
);
var mergedFormData = merge(
result.get("formData"),
result2.get("reference"),
updatedFormData
);
var updateResult = dsl
.executeUpdate(
"update formStuff set formData = ? where id = ?",
mergedFormData,
result.get("id")
);
return mergedFormData;
}
I am trying to do something similar on Expo SQLite but it started to appear like callback hell
async function update(db, updatedFormData) {
return
new Promise((resolve, reject) => {
db.transaction(
(tx) => {
tx.executeSql(
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId],
(tx1, resultSet1) => {
tx1.executeSql(
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id],
(tx2, resultSet2) => {
const mergedFormData = merge(
resultSet1.rows.item(0).formData,
resultSet2.rows.item(0).reference,
updatedFormData
);
tx2.executeSql(
"update formStuff set formData = ? where id = ?",
[mergedFormData, resultSet1.rows.item(0).id],
(tx3) => {
resolve(mergedFormData)
},
(tx3, error) => {
console.log(error);
reject(error);
return true;
}
)
}
(tx2, error) => {
console.log(error);
reject(error);
return true;
}
)
},
(tx1, error) => {
console.log(error);
reject(error);
return true;
}
);
},
(tx, error) => {
console.error(error);
reject(error);
},
() => {
resolve();
}
);
Wrap each call to executeSql in its own promise.
Generally it is better to then wrap each promise in its own function (which you can give a sensible name and arguments).
Then await the return value of each function in turn (which lets you assign the resolved value to a variable and pass it to the next function).
Maybe you can chain queries using recursion on success/error, something like:
function executeBatch(queries, ready) {
if (queries.length === 0) {
console.log('all done!');
ready();
return;
}
const queryId = `q${queries.length}`;
const query = queries.shift();
console.log(`starting ${query}`);
console.time(queryId);
const continueExecution = () => {
console.timeEnd(queryId);
executeBatch(queries);
};
db.transaction(tx =>
tx.executeSql(
query,
[],
() => {
console.log('ok');
continueExecution();
},
() => {
console.error('fail');
continueExecution();
}
)
);
}
executeBatch(['query1','query2',...], doneCallback);
UPDATE this does not work due to https://github.com/nolanlawson/node-websql/issues/46
I just did a quick hack of a module to do this for me. Likely there are better ways of doing this with extending classes and what not (plus I am limited to JavaScript though I use VSCode's TS check with JSDoc)
// #ts-check
/**
* This module provides an async/await interface to Expo SQLite. For now this provides a functional interface rather than a class based interface.
* #module
*/
/**
* #typedef {import("expo-sqlite").SQLTransaction} SQLTransaction
* #typedef {import("expo-sqlite").SQLError} SQLError
* #typedef {import("expo-sqlite").SQLResultSet} SQLResultSet
* #typedef {(tx: SQLTransaction)=>Promise<any>} AsyncTransactionCallback
*/
import * as SQLite from "expo-sqlite";
/**
*
* #param {string} name
* #param {string} [version]
* #returns {Promise<SQLite.WebSQLDatabase>}
*/
export async function openDatabaseAsync(name, version) {
return new Promise((resolve) => {
SQLite.openDatabase(name, version, "", 0, (db) => {
resolve(db);
});
});
}
/**
*
* #param {SQLTransaction} tx transaction
* #param {string} sqlStatement
* #param {any[]} [args]
* #return {Promise<SQLResultSet>}
*/
export async function executeSqlAsync(tx, sqlStatement, args = []) {
return new Promise((resolve, reject) => {
tx.executeSql(
sqlStatement,
args,
(txObj, resultSet) => {
resolve(resultSet);
},
(error) => {
console.log(error);
reject(error);
return true;
}
);
});
}
/**
*
* #param {SQLite.WebSQLDatabase} db
* #return {(fn: AsyncTransactionCallback)=>Promise<any>}
*/
export function txn(db) {
return async (f) => {
new Promise((resolve, reject) => {
db.transaction(
(tx) => {
f(tx)
.then((result) => resolve(result))
.catch(reject);
},
/**
*
* #param {SQLError} error error
*/
(error) => {
reject(error);
},
() => {
resolve();
}
);
});
};
}
For my scenario it is used like this
async function update(db, updatedFormData) {
return await txn(db)(async (tx) => {
// there's probably a less retarded way of writing this using bind or something
const resultSet1 = await executeSqlAsync(tx,
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId]);
const resultSet2 = await executeSqlAsync(tx,
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id]);
const mergedFormData = merge(
resultSet1.rows.item(0).formData,
resultSet2.rows.item(0).reference,
updatedFormData
);
await executeSqlAsync(tx,
"update formStuff set formData = ? where id = ?",
[mergedFormData, resultSet1.rows.item(0).id],
);
return mergedFormData;
});
};
Maybe I'll figure out how to tweak it so it looks like this in the future, but for now what I have does what I need.
async function update(db: AsyncSQLiteDatabase, updatedFormData: FormData) {
return await db.asyncTransaction<FormData>(async (tx) => {
// there's probably a less retarded way of writing this using bind or something
const resultSet1 = await tx.executeSqlAsync(
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId]);
const resultSet2 = await tx.executeSqlAsync(
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id]);
const mergedFormData = merge(
resultSet1.rows.item(0).formData,
resultSet2.rows.item(0).reference,
updatedFormData
);
await tx.executeSqlAsync(
"update formStuff set formData = ? where id = ?",
[mergedFormData, resultSet1.rows.item(0).id],
);
return mergedFormData;
});
};
Use async/await to make things feel synchronous. Assuming your sqlite lib is promise ready.
async function update(db, updatedFormData) {
const tx = await db.transaction();
try {
const resultSet1 = await tx.executeSql(
"select id, formData from formStuff where formId = ?",
[updatedFormData.formId]
);
const resultSet2 = await tx.executSql(
"select reference from referenceStuff where formStuffId = ?",
[resultSet1.rows.item(0).id]
);
// ... etc
tx.commit();
}
catch (e) {
tx.rollback();
}
}
I am trying to refactor some ugly code in a Google Apps Script web application so that it uses async / await.
It uses the google.script.url.getLocation client-side, to pull URL parameters and then send them off to other async functions.
There must be a way to do this elegantly.
var doSomeAsyncShit =()=> {
google.script.url.getLocation(function (location) {
var rid = (location.parameter.rid) ? location.parameter.rid : defaultReportID;
var uid = (location.parameter.uid) ? location.parameter.uid : defaultUserID;
console.log (((location.parameter.rid) ? "Report #" : "Default Report ID #")+rid);
console.log (((location.parameter.uid) ? "User #" : "Default User ID #" )+uid);
google.script.run.withSuccessHandler(paintReport).returnJSON(rid);
google.script.run.withSuccessHandler(getMyReportsList).listMyReports(uid);
});
}
$(function () {
doSomeAsyncShit();
}
It is possible to intercept requests to google api and directly return Promise using Proxy.
Script:
/**
* Revives old client facing google api in apps script web applications
* Directly returns promises for `google.scipt.run` and `google.script.url.getLocation`
* #see https://stackoverflow.com/a/63537867/
*/
(function projectAdrenaline_google() {
const lifeline = {
funcList: [],
excludeList: [
'withSuccessHandler',
'withFailureHandler',
'withUserObject',
'withLogger',
],
get: function(target, prop, rec) {
if (this.excludeList.includes(prop))
//return (...rest) => new Proxy(Reflect.apply(target[prop], target, rest), trap);
throw new TypeError(
`${prop}: This method is deprecated in this custom api`
);
if (this.funcList.includes(prop))
return (...rest) =>
new Promise((res, rej) =>
target
.withSuccessHandler(res)
.withFailureHandler(rej)
[prop](...rest)
);
switch (prop) {
case 'run':
this.funcList = Object.keys(target.run);
break;
case 'getLocation':
return () => new Promise(res => target[prop](res));
}
return new Proxy(Reflect.get(target, prop, rec), lifeline);
},
};
//const superGoogle = new Proxy(google, trap);
//OR overwrite currently loaded google object:
google = new Proxy(google, lifeline);
})();
Example:
const doSomeAsyncStuff = async () => {
const location = await google.script.url.getLocation();
const rid = location.parameter.rid ? location.parameter.rid : defaultReportID;
const uid = location.parameter.uid ? location.parameter.uid : defaultUserID;
//promise
google.script.run.returnJSON(rid).then(paintReport);
//async-await
const reportsList = await google.script.run.listMyReports(uid);
getMyReportsList(reportsList);
};
Alternatively, It is possible to use functions as syntactic sugars. But this requires learning new syntax definitions:
/**
* Syntactic sugar around old callback api returning a promise
*
* #returns {promise} Promise of call from server
* #param {string[]|string} propertyAccesors Array of properties to access
* #param {object[][]} methodAccesors Array of [method_to_access,arguments[]]
* #param {number[]} resRejIdxs 2 Indexes of methodAccesors corresponding to resolve/success and rejection/failure. If omitted promise is resolved immediately.
*/
const GS = (propertyAccesors, methodAccesors, resRejIdxs) =>
new Promise((res, rej) => {
//Boilerplate for type correction
const nestArray = e => (Array.isArray(e) ? e : [e]);
propertyAccesors = nestArray(propertyAccesors);
methodAccesors = nestArray(methodAccesors);
methodAccesors[0] = nestArray(methodAccesors[0]);
if (typeof resRejIdxs !== 'undefined') {
resRejIdxs = Array.isArray(resRejIdxs) ? resRejIdxs : [resRejIdxs];
resRejIdxs[0] && (methodAccesors[resRejIdxs[0]][1] = res);
resRejIdxs[1] && (methodAccesors[resRejIdxs[1]][1] = rej);
} else {
res('Done');
}
//Access properties and call methods
methodAccesors.reduce(
(acc, [method, methodArg]) =>
Array.isArray(methodArg)
? acc[method](...methodArg)
: acc[method](methodArg),
propertyAccesors.reduce(
(acc, currentProp) => acc[currentProp],
google.script
)
);
});
//EXAMPLES:
GS(
'run',
[
['withSuccessHandler', null],
['callServer', [5, 4]], //call server function `callServer` with 2 arguments 5 and 4
['withFailureHandler', null],
],
[0, 2] //0 is withSuccessHandler and 2 is withFailureHandler
).then(alert);
GS('history', [['setChangeHandler', e => console.log(e.location.hash)]]);
GS('url', 'getLocation', 0).then(location => console.log(location.hash));
GS(['host', 'editor'], 'focus');
GS('host', ['setHeight', 50]);
Since a Promise can be constructed with a custom executor function, you can wrap the google.script.url into it and resolve or reject whenever you like. If you then make it a utility function, use await to wait for it to resolve.
Below is a small flexible utility for making google.script.url async-friendly:
/**
* #typedef {{
* hash : string,
* parameter : Object.<string, string>,
* parameters : Object.<string, string[]>
* }} UrlLocationObject
*
* #typedef {{
* callback : function (UrlLocationObject, ...any) : any,
* params : any[]
* }} AsyncUrlOptions
*
* #summary Promise-friendly google.script.url
* #param {AsyncUrlOptions}
* #returns {Promise}
*/
const asyncLocation = ({
callback,
params = [],
}) => {
return new Promise((res, rej) => {
google.script.url.getLocation((loc) => {
try {
const result = callback(loc, ...params);
res(result);
}
catch(error) {
rej(error);
}
});
});
};
Same goes for google.script.run:
/**
* #typedef {{
* funcName : string,
* onFailure : function,
* onSuccess : function,
* params : array
* }} AsyncOptions
*
* #summary v2 of async-friendly google.script.run
* #param {AsyncOptions}
* #returns {Promise}
*/
const asyncGAPIv2 = ({
funcName,
onFailure = console.error,
onSuccess,
params = []
}) => {
return new Promise((res, rej) => {
google.script.run
.withSuccessHandler(data => {
typeof onSuccess === "function" && onSuccess(data);
res(data);
})
.withFailureHandler(error => {
typeof onFailure === "function" && onFailure(error);
rej(error);
})
[funcName].apply(null, params);
});
};
Something like this isn't too bad:
var doSomeAsyncShit = async () => {
let location = await new promise(resolve => google.script.url.getLocation(resolve))
// do stuff with location
}
(async () => {
await doSomeAsyncShit();
// do something after
})()
Even still, you've added complexity, reduced readability and for no good reasom IMHO
I'm working on a nest.js microservices project. The controllers and services are defined and the back-end runs without errors. I'm trying to create a resource(subscriptionPlan in this example) using
grpCurl, like this:
grpcurl -d '{
"name": "Test GRPC",
"code": "12312",
"description": "test",
"price": 10,
"invoicePeriod": 10,
"invoiceDuration":"DAY"
}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Create
when I run this command I get an error message: Failed to process proto source files.: could not parse given files: %!v(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference).
I feel there isn't a problem in the back-end/services code , it's something with the project set-up maybe I'm missing a library on command-line tools. I checked x-code dev tools are installed and all the module dependencies are also installed.
here is my : subscription_plan.service.ts
/* eslint-disable complexity */
/* eslint-disable #typescript-eslint/no-unused-vars */
import { injectable } from 'inversify';
import _ from 'lodash';
import { createEverLogger } from '../../helpers/Log';
import { ErrorGenerator } from '../../shared/errors.generator';
import {
BadRequestError,
ConflictError,
NotFoundError,
ParseError,
} from '../../shared/errors.messages';
import { DatabaseService } from '../database/database.service';
import { servicesContainer } from '../inversify.config';
import { IService } from '../IService';
import { SubscriptionPlans } from './model/subscription_plan.model';
import {
DeleteSubscriptionPlanResponse,
ISubscriptionPlanService,
SubscriptionInputPayload,
SubscriptionPlan,
SubscriptionPlanFilter,
SubscriptionPlanResponse,
SubscriptionPlanUpdatePayload,
UpdateSubscriptionPlanResponse,
} from './types/subscription_plan.types';
import { subscriptionPlanCreateSchema } from './validators/subscription_plan.create.yup';
import { subscriptionPlanFilterSchema } from './validators/subscription_plan.filter.yup';
import { subscriptionPlanUpdateSchema } from './validators/subscription_plan.update.yup';
/**
* Subscription Plans Service
* CRUD operation for Subscription Plan
* #export
* #class SubscriptionPlanService
* #implements {ISubscriptionPlanService}
* #implements {IService}
*/
#injectable()
export class SubscriptionPlanService
implements ISubscriptionPlanService, IService {
private logger = createEverLogger({ name: 'SubscriptionPlanService' });
private dbService = servicesContainer.get<DatabaseService>(DatabaseService);
/**
* Create the subscription plan
*
* Returns the newly created subscription plan object with id
*
* #param {SubscriptionInputPayload} payload
* #returns {Promise<SubscriptionPlan>}
* #memberof SubscriptionPlanService
*/
async create(payload: SubscriptionInputPayload): Promise<SubscriptionPlan> {
let result: SubscriptionPlan;
try {
// Validate the payload
await subscriptionPlanCreateSchema.validate(payload, {
abortEarly: false,
});
const slug = payload.name.toLowerCase().replace(' ', '-');
// Check for existing slug
const isExist = await this.dbService.findOne<
SubscriptionPlan,
SubscriptionPlanFilter
>({ slug });
if (!_.isNil(isExist)) {
throw ConflictError(ErrorGenerator.Duplicate('Subscription Plan'));
}
// Make db call
result = await this.dbService.create<SubscriptionPlan, SubscriptionPlans>(
new SubscriptionPlans({ ...payload, slug }),
);
this.logger.debug('Subscription Plan added Successfully', result);
} catch (e) {
this.logger.error(e);
ParseError(e, ErrorGenerator.Duplicate('Subscription Plan'));
}
if (!_.isEmpty(result?.id)) {
return result;
}
throw BadRequestError(ErrorGenerator.UnableSave('Subscription Plan'));
}
/**
* Get the subscription plan by id only
* will return single object
* #param {SubscriptionPlanFilter} where
* #returns {Promise<SubscriptionPlan>}
* #memberof SubscriptionPlanService
*/
async findOne(where: SubscriptionPlanFilter): Promise<SubscriptionPlan> {
let edge: SubscriptionPlan;
try {
// Validate Input
await subscriptionPlanFilterSchema.validate(where, {
abortEarly: false,
});
// Get the subscription plan id
// TODO: Implement other filters
const id = where?.id;
if (!_.isNil(id)) {
// make db call
edge = await this.dbService.findOne<
SubscriptionPlan,
SubscriptionPlanFilter
>(new SubscriptionPlans({ id }));
}
} catch (e) {
this.logger.error(e);
ParseError(e, ErrorGenerator.NotFound('Subscription Plan'));
}
if (!_.isEmpty(edge)) {
this.logger.debug('Subscription Plan loaded Successfully', edge);
return edge;
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
/**
* Get all the subscriptions plans
* with pagination
* #param {SubscriptionPlanFilter} [where]
* #returns {Promise<SubscriptionPlanResponse>}
* #memberof SubscriptionPlanService
*/
async findAll(
where?: SubscriptionPlanFilter,
): Promise<SubscriptionPlanResponse> {
// Validate the Input
let edges: SubscriptionPlan[];
let count: number; // Rows counts
let recordLimit = 10; // Pagination Limit
let recordSkip = 0; // Pagination: SKIP
// TODO
// Transform from Object to Array
// { id: SortDirection.ASC } to [ "id", "ASC"]
// for (const [key, value] of Object.entries(sortBy)) {
// sortOrder.push([key, value]);
// }
try {
await subscriptionPlanFilterSchema.validate(where, {
abortEarly: false,
});
if (where) {
// TODO: Implement other filters
const { id, limit, skip } = where;
// isNil check for for null or undefined
if (!_.isNil(id) && !_.isNil(limit) && !_.isNil(skip)) {
// Set Limit and Skip for `page_info`
recordLimit = limit;
recordSkip = skip;
// Load the SubscriptionPlan with ID and Pagination
[edges, count] = await this.dbService.findAll<
SubscriptionPlan,
Partial<SubscriptionPlanFilter>
>(new SubscriptionPlans({ id }), recordLimit, recordSkip);
} else if (!_.isNil(limit) && !_.isNil(skip)) {
// Set Limit and Skip for `page_info`
recordLimit = limit;
recordSkip = skip;
// Load All SubscriptionPlan with default pagination
[edges, count] = await this.dbService.findAll<
SubscriptionPlan,
Partial<SubscriptionPlanFilter>
>(new SubscriptionPlans(), recordLimit, recordSkip);
} else if (!_.isNil(id)) {
// Load All SubscriptionPlan with id with default pagination
[edges, count] = await this.dbService.findAll<
SubscriptionPlan,
Partial<SubscriptionPlanFilter>
>(new SubscriptionPlans({ id }), recordLimit, recordSkip);
}
} else {
// Load All SubscriptionPlan with default pagination
[edges, count] = await this.dbService.findAll<
SubscriptionPlan,
Partial<SubscriptionPlanFilter>
>(new SubscriptionPlans(), recordLimit, recordSkip);
}
} catch (error) {
this.logger.error(error);
// Empty
ParseError(error, ErrorGenerator.NotFound('Subscription Plan'));
}
// Validate edges are not empty
if (!_.isEmpty(edges)) {
this.logger.debug('Subscription Plan loaded Successfully', edges);
return {
edges,
page_info: {
total: count,
limit: recordLimit,
skip: recordSkip,
has_more: count > recordLimit + recordSkip ? true : false,
},
};
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
count(where?: SubscriptionPlanFilter): Promise<number> {
throw new Error('Method not implemented.');
}
/**
* Update the subscription plan
* by id only
* #param {SubscriptionPlanUpdatePayload} payload
* #param {SubscriptionPlanFilter} where
* #returns {Promise<UpdateSubscriptionPlanResponse>}
* #memberof SubscriptionPlanService
*/
async update(
payload: SubscriptionPlanUpdatePayload,
where: SubscriptionPlanFilter,
): Promise<UpdateSubscriptionPlanResponse> {
let modified: number;
let edges: SubscriptionPlan[];
try {
// Validate the input
await subscriptionPlanUpdateSchema.validate(
{ ...payload, ...where },
{ abortEarly: false },
);
// Check where is defined
if (where) {
const { id } = where;
// Get Subscription plan id
if (!_.isNil(id)) {
// Generate the slug
const slug = payload.name.toLowerCase().replace(' ', '-');
// Check for existing slug
const isExist = await this.dbService.findOne<
SubscriptionPlan,
SubscriptionPlanFilter
>({ slug });
// Validate the ID is not same
// Return document can have the same ID as of update
if (!_.isNil(isExist) && isExist?.id != id) {
throw ConflictError(ErrorGenerator.Duplicate('Subscription Plan'));
}
// Make db call
[edges, modified] = await this.dbService.update<
SubscriptionPlan,
Partial<SubscriptionPlan>,
SubscriptionPlanFilter
>(
new SubscriptionPlans({ ...payload, slug }),
new SubscriptionPlans({ id }),
);
this.logger.debug('Subscription Plan Update Successfully', edges);
}
}
} catch (e) {
this.logger.error(e);
ParseError(e, ErrorGenerator.Duplicate('Subscription Plan'));
}
if (modified > 0) {
// Return the update data with count
return { modified, edges };
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
/**
* Delete the subscription plan
* by id only
* #param {SubscriptionPlanFilter} where
* #returns {Promise<DeleteSubscriptionPlanResponse>}
* #memberof SubscriptionPlanService
*/
async delete(
where: SubscriptionPlanFilter,
): Promise<DeleteSubscriptionPlanResponse> {
let modified: number;
let edges: SubscriptionPlan[];
try {
this.logger.info(where, 'Delete request');
// Validate the payload
await subscriptionPlanFilterSchema.validate(where, { abortEarly: false });
// Check where is defined
if (where) {
// Get the subscription plan id
const { id } = where;
if (!_.isNil(id)) {
// Make db call
[edges, modified] = await this.dbService.delete<
SubscriptionPlan,
SubscriptionPlanFilter
>(new SubscriptionPlans({ id }));
this.logger.debug('Subscription Plan deleted Successfully', edges);
}
}
} catch (e) {
this.logger.error(e);
ParseError(e, ErrorGenerator.UnableToDelete('Subscription Plan'));
}
if (modified > 0) {
return { modified, edges };
}
throw NotFoundError(ErrorGenerator.NotFound('Subscription Plan'));
}
}
Here is my service_plan.controller.ts
/* eslint-disable #typescript-eslint/no-unused-vars */
import { Controller } from '#nestjs/common';
import { GrpcMethod, RpcException } from '#nestjs/microservices';
import { HttpError } from 'http-json-errors';
import { inject, LazyServiceIdentifer } from 'inversify';
import { rpc, subscription_plan } from '../codegen/rpc';
import { SubscriptionPlanService } from '../services/SubscriptionPlan/subscription_plan.service';
import SubscriptionPlanResponse = subscription_plan.SubscriptionPlanResponse;
import SubscriptionPlanFilter = subscription_plan.SubscriptionPlanFilter;
import SubscriptionPlan = subscription_plan.SubscriptionPlan;
import DeleteSubscriptionPlanResponse = subscription_plan.DeleteSubscriptionPlanResponse;
import UpdateSubscriptionPlanResponse = subscription_plan.UpdateSubscriptionPlanResponse;
import UpdateSubscriptionPlanRequest = subscription_plan.UpdateSubscriptionPlanRequest;
import SubscriptionPlanInput = subscription_plan.SubscriptionPlanInput;
import IEmpty = rpc.IEmpty;
#Controller()
export class SubscriptionPlanController {
constructor(
#inject(new LazyServiceIdentifer(() => SubscriptionPlanService))
private readonly subscriptionPlanService: SubscriptionPlanService,
) {}
/**
* Get all subscription plans
* Test command : grpcurl -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/FindAll
* #param {IEmpty} req
* #returns {Promise<SubscriptionPlans>}
* #memberof SubscriptionPlanController
*/
#GrpcMethod('SubscriptionPlanService', 'FindAll')
async findAll(req: IEmpty): Promise<SubscriptionPlanResponse> {
try {
const obj = await this.subscriptionPlanService.findAll();
return SubscriptionPlanResponse.create(
(obj as unknown) as SubscriptionPlanResponse,
);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,
message: JSON.stringify(error),
});
}
}
/**
* Get One subscription plan
* Test command : grpcurl -d '{"id":"513-A"}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/FindOne
* #param {SubscriptionPlanFilter} where
* #returns {Promise<SubscriptionPlan>}
* #memberof SubscriptionPlanController
*/
#GrpcMethod('SubscriptionPlanService', 'FindOne')
async findOne(where: SubscriptionPlanFilter): Promise<SubscriptionPlan> {
try {
const id = where?.id;
const obj = await this.subscriptionPlanService.findOne({ id });
return SubscriptionPlan.create((obj as unknown) as SubscriptionPlan);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,
message: JSON.stringify(error),
});
}
}
/**
* Create subscription plan
* Test command : grpcurl -d '{
"name": "Test GRPC",
"code": "12312",
"description": "test",
"price": 10,
"invoicePeriod": 10,
"invoiceDuration":"DAY"
}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Create
*
* #param {SubscriptionPlanInput} payload
* #returns {Promise<SubscriptionPlan>}
* #memberof SubscriptionPlanController
*/
#GrpcMethod('SubscriptionPlanService', 'Create')
async create(payload: SubscriptionPlanInput): Promise<SubscriptionPlan> {
try {
const obj = await this.subscriptionPlanService.create({
name: payload?.name,
price: payload?.price,
invoice_duration: payload?.invoice_duration as any,
invoice_period: payload?.invoice_period,
trail_period: payload?.trail_period,
trail_duration: payload?.trail_duration as any,
description: payload?.description,
code: payload?.code,
});
return SubscriptionPlan.create((obj as unknown) as SubscriptionPlan);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,
message: JSON.stringify(error) || error,
});
}
}
/**
* Update subscription plan
* Test command :
* grpcurl -d '{"payload":{"name":"Update Text"},"where":{"id":"97-A"}}'
* -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Update
* #param {UpdateSubscriptionPlanRequest} data
* #returns {Promise<UpdateSubscriptionPlanResponse>}
* #memberof SubscriptionPlanController
*/
#GrpcMethod('SubscriptionPlanService', 'Update')
async update(
data: UpdateSubscriptionPlanRequest,
): Promise<UpdateSubscriptionPlanResponse> {
try {
const { payload, where } = data;
const obj = await this.subscriptionPlanService.update(
payload as any,
where,
);
return UpdateSubscriptionPlanResponse.create(
(obj as unknown) as UpdateSubscriptionPlanResponse,
);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,
message: JSON.stringify(error),
});
}
}
/**
* Delete subscription plan
* Test command : grpcurl -d '{"id":"513-A"}' -plaintext -proto rpc/rpc.proto 127.0.0.1:5000 rpc.SubscriptionPlanService/Delete
* #param {SubscriptionPlanFilter} where
* #returns {Promise<DeleteSubscriptionPlanResponse>}
* #memberof SubscriptionPlanController
*/
#GrpcMethod('SubscriptionPlanService', 'Delete')
async delete(
where: SubscriptionPlanFilter,
): Promise<DeleteSubscriptionPlanResponse> {
try {
const id = where?.id;
const obj = await this.subscriptionPlanService.delete({ id });
return DeleteSubscriptionPlanResponse.create(
(obj as unknown) as DeleteSubscriptionPlanResponse,
);
} catch (error) {
const errorInfo = error as HttpError;
throw new RpcException({
code: errorInfo.statusCode,
message: JSON.stringify(error),
});
}
}
}
I have also added the subscription module to app modules.
my app.module.ts
import { Module } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { APP_FILTER, APP_INTERCEPTOR } from '#nestjs/core';
import { GraphQLModule } from '#nestjs/graphql';
import GraphQLJSON, { GraphQLJSONObject } from 'graphql-type-json';
import _ from 'lodash';
import { ConfigModule } from './config/config.module';
// import { servicesContainer } from './services/inversify.config';
import { ServicesModule } from './services/service.module';
// import { SubscriptionPlanService } from './services/SubscriptionPlan/subscription_plan.service';
import { HttpExceptionFilter } from './shared/exception-filter/http-exception.filter';
import { TimeoutInterceptor } from './shared/interceptor/timeout.interceptor';
import schemaDirectives from './shared/schema-directive/index';
import { SubscriptionPlanModule } from './subscription_plans/subscription_plan.module';
import { UserModule } from './users/user.module';
#Module({
imports: [
ConfigModule,
ServicesModule,
SubscriptionPlanModule,
UserModule,
GraphQLModule.forRootAsync({
useFactory: () => ({
schemaDirectives,
include: [],
typePaths: ['./**/**/*.graphql'],
installSubscriptionHandlers: true,
context: ({ req }) => ({ req }),
introspection: true,
// debug: configService.get<string>('app.nodeEnv') === 'development',
// engine: {
// schemaTag: configService.get<string>('app.nodeEnv'),
// apiKey: configService.get<string>('app.apolloEngineApiKey'),
// },
resolverValidationOptions: {
requireResolversForResolveType: false,
},
resolvers: {
JSON: GraphQLJSON,
JSONObject: GraphQLJSONObject,
},
formatError: (error) => {
try {
error.message = JSON.parse(error.message);
} catch (e) {
// Empty
}
return {
...error,
message: error.message,
code: _.get(error, 'extensions.exception.title', 'UNKNOWN'),
locations: error.locations,
path: error.path,
};
},
formatResponse: (response) => response,
}),
inject: [ConfigService],
}),
],
controllers: [],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: TimeoutInterceptor,
},
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {
constructor() {
// Debug the Insert operation
// const s = servicesContainer.get<SubscriptionPlanService>(
// SubscriptionPlanService,
// );
// void s.create({
// name: 'Test',
// invoice_duration: 'DAY',
// invoice_period: 30,
// price: 10,
// code: '12312',
// description: 'test',
// trail_duration: 'DAY',
// trail_period: 12,
// });
// void s.findAll();
// void s.delete({ id: '257-A' });
// void s.findOne({ id: '257-A' });
// void s.update({ name: 'Test Update name1' }, { id: '353-A' });
}
}
I feel like sharing all these code were un-necessary but if anyone needs any kind of information do let me know in the comments.
Even if you a clue to what might be raising the issue mentioned above would be of great help.
I got the same error message while everything in the server seemed to work as expected. It turned out I was calling grpcurl in the wrong directory.
Based on the argument -proto rpc/rpc.proto, you should check that you are in the parent directory of rpc and try calling grpcurl again.
The gRPCurl project already has a suggestion to change the error message to a better one.
UPDATE: The above bad error message issue seems to be fixed as mentioned in it:
Failed to process proto source files.: could not parse given files: open <your given proto path and file argument here>: no such file or directory
I've got a quite strange error from eslint with this piece code:
import axios from 'axios';
import config from '../config.json';
import AgentResult from './agentResult';
/**
* Class Agent used for making calls to api
*/
class Agent {
/**
*
* #param {string} apiRoot
* #param {string} wsRoot
* #constructor
*/
constructor(apiRoot = config.apiRoot, wsRoot = config.wsRoot) {
this._apiRoot = apiRoot;
this._wsRoot = wsRoot;
this._transport = axios.create({
baseURL: this._apiRoot,
timeout: 10000,
withCredentials: true
});
}
/**
* Make request to apiRoot/users/me
* #returns {Promise<AgentResult>} AgentResult
*/
async getMe() {
try {
let res = await this._transport.get('/users/me');
return AgentResult.ok(res.data);
} catch (ex) {
if (ex.request) {
return AgentResult.fail(ex.request);
}
return AgentResult.fail(ex.response.error.message);
}
}
/**
* Make request to apiRoot/users/:id
* #param {string} id
* #returns {Promise<AgentResult>} AgentResult
*/
async getUserById(id) {
try {
let res = await this._transport.get(`/users/${id}`);
return AgentResult.ok(res.data);
} catch (ex) {
if (ex.request) {
return AgentResult.fail(ex.request);
}
return AgentResult.fail(ex.response.error.message);
}
}
/**
* Make request to apiRoot/users/sign-in
* #param {object} body
* #returns {AgentResult} AgentResult
*/
async postSignIn(body) {
try {
let res = await this._transport.post('/users/sign-in', body);
return AgentResult.ok(res.data);
} catch (ex) {
if (ex.request) {
return AgentResult.fail(ex.request);
}
return AgentResult.fail(ex.response.error.message);
}
}
/**
* Make request to apiRoot/users/sign-up
* #param {object} body
* #returns {Promise<AgentResult>} AgentResult
*/
async postSignUp(body) {
try {
let res = await this._transport.post('/users/sign-up', body);
return AgentResult.ok(res.data);
} catch (ex) {
if (ex.request) {
return AgentResult.fail(ex.request);
}
return AgentResult.fail(ex.response.error.message);
}
}
/**
* Make request to apiRoot/users/update
* #param {object} body
* #returns {Promise<AgentResult>} AgentResult
*/
async putUpdate(body) {
try {
let res = await this._transport.put('/users/update', body);
return AgentResult.ok(res.data);
} catch (ex) {
if (ex.request) {
return AgentResult.fail(ex.request);
}
return AgentResult.fail(ex.response.error.message);
}
}
/**
* Opens ws connection at wsRoot/find
* #returns {WebSocket} websocket connection
*/
wsFind() {
let ws = new WebSocket('${this._wsRoot}/find')
return ws;
}
/**
* Opens ws connection at wsRoot/open?context=context
* #param {string} context
* #returns {WebSocket} websocket connection
*/
wsOpen(context) {
let ws = new WebSocket(`${this._wsRoot}/open?context=${context}`);
return ws;
}
}
export default Agent;
The error reads:
135:28 error '_x' is defined but never used no-unused-vars
186:27 error '_x2' is defined but never used no-unused-vars
237:27 error '_x3' is defined but never used no-unused-vars
288:26 error '_x4' is defined but never used no-unused-vars
The problem is: these lines don't even exist in the code, furthermore such variables. I can suspect that the problem is here because of async code. Am i right?