How to override feathersjs defaults service methods - node.js

I have a feathersjs service created using the feathers generate service command. I want to override its definition of create method.
This is my service class
/* eslint-disable no-unused-vars */
// Initializes the `userGroup` service on path `/usergroup`
const createService = require('feathers-sequelize');
const createModel = require('../../models/user-group.model');
const hooks = require('./user-group.hooks');
const filters = require('./user-group.filters');
const async = require('async');
module.exports = function () {
const app = this;
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'usergroup',
Model,
paginate,
create: fnCreateGroup // documentation says this allows you to create your own create method
};
function fnCreateGroup(data, params) {
let json = {
done: false
};
let permissionIds = Object.keys(data.permissionList), inIds = [];
for (let i in permissionIds) {
if (data.permissionList[permissionIds[i]]) {
inIds.push(parseInt(permissionIds[i]));
}
}
if (inIds.length === 0) {
json.cause = 'You must provide the permission List';
return Promise.resolve(json);
}
async.parallel([
function (cb) {
Model.create({
groupName: data.groupName
}).then(function (ug) {
cb(null, ug);
}, function (err) {
cb(err, null);
});
},
function (cb) {
app.models.permissions.findAll({
where: {
id: {
$in: inIds
}
}
}).then(function (plist) {
cb(null, plist);
});
}
]
, function (err, results) {
if (typeof err !== 'undefined' && err !== null) {
json.err = err;
return Promise.resolve(json);
} else {
let permissions = results[1], group = results[0];
for (let i = 0; i < permissions.length; i++) {
group.addPermission(permissions[i]);
}
json.done = true;
json.id = group.id;
return Promise.resolve(json);
}
});
}
// Initialize our service with any options it requires
app.use('/usergroup', createService(options));
// Get our initialized service so that we can register hooks and filters
let service = app.service('usergroup');
service.hooks(hooks);
if (service.filter) {
service.filter(filters);
}
};
The test on create method shows the result of original method and not mine.
What can I do?

How to override and extend existing services is documented in the database adapter common API. You can use hooks by setting hook.result or extend the existing ES6 class:
const Service = require( 'feathers-sequelize').Service;
class MyService extends Service {
create(data, params) {
data.created_at = new Date();
return super.create(data, params);
}
}
app.use('/todos', new MyService({
paginate: {
default: 2,
max: 4
}
}));

Related

MongoError: Document must be a valid JavaScript object

I have a problem where MongoDB says that my object is not a valid JavaScript Object, Even though it is! This has been staying for days!
Basically, this is an account system that uses MongoDB's client, and the ObjectId for the ID.
I want to be able to fix the MongoError that says object (sent to updateOne, not filter) is not a valid JavaScript object.
Here is the code:
const { MongoClient, ObjectId } = require("mongodb");
const fs = require("node:fs");
const uri = "mongodb://127.0.0.1:27017";
if (!fs.existsSync("./db")) {fs.mkdirSync("./db")};
const client = new MongoClient(uri,{ useUnifiedTopology: true });
async function conn() {
await client.connect();
}
conn();
const database = client.db("login");
const accs = database.collection("accounts");
const myfil = {
_id: new ObjectId('63b6441832087ccc7e3edea2')
};
const users = accs.findOne(myfil);
const path = require("node:path");
const bcrypt = require('bcrypt');
const env = process.env;
var saltRounds = 10;
const AddSet = class AddSet {
constructor(user,pass) {
console.log(pass);
this.set = {[user]:pass};
this.set = Object.keys(this.set).reduce((acc, key) => {
acc[key.toString()] = this.set[key];
return acc;
}, {});
console.log(this.set);
return this.set;
}
}
const Account = class Account {
constructor(user,password) {
conn();
if (!users[user]) {
conn();
accs.updateOne(myfil,bcrypt.hash(password, saltRounds, function(err, hash)
{
try {
var a = ""+user;
return new AddSet(a.toString(),hash);
} catch(err) {
console.error("bcrypt",err);
}
}));
this.assetDir = path.join(path.join(env.SAVED_FOLDER,"/"+this.user),"/assets");
this.metaDir = this.assetDir + '/meta';
this.starterDir = path.join(path.join(env.SAVED_FOLDER,"/"+this.user),"/starters");
this.videoDir = path.join(path.join(env.SAVED_FOLDER,"/"+this.user),"/videos");
var fs = require('fs');
if (!fs.existsSync(this.assetDir)) fs.mkdirSync(this.assetDir, { recursive: true });
if (!fs.existsSync(this.starterDir)) fs.mkdirSync(this.assetDir, { recursive: true });
if (!fs.existsSync(this.videoDir)) fs.mkdirSync(this.assetDir, { recursive: true });
}
}
getAssetDir() {
return this.assetDir;
}
getStarterDir() {
return this.starterDir;
}
getVideoDir() {
return this.videoDir;
}
getMetaDir() {
return this.metaDir;
}
checkSession(pswd) {
conn();
bcrypt.compare(pswd, users[this.user], function(err, result) {
if (result) return true;
else return false;
});
}
}
module.exports = { Account, users };
I tried fixing it, making the keys strings, removing the $set, and it did not work.

I need help to get a simple query for MongoDB (SQL equivalent: "select speed from values")

I need help to get a simple query for MongoDB (SQL equivalent: "select speed from values"). But I can't find a solution for that.
Node.js backend for Vue.js
// a Part of Init-function:
await collection.replaceOne({}, {
speed: {
value: 65,
type: 2,
},
eightSpeed: {
value: 0.0043,
type: 2,
},
oSpeed: {
value: 0.31,
type: 2,
},
minusSpeed: {
value: 2.42,
type: 2,
}
}
//...
Now I need the query like this: http://192.168.220.220:3000/settings/getValue/speed to get an object speed.
const express = require("express");
const mongodb = require("mongodb");
const SettingsRouter = new express.Router();
SettingsRouter.get("/getValue/:value", async function (req, res) {
try {
const val = req.params.value; // req.body.text;
const select = {};
select[`${val}.value`] = 1;
console.log("settings.js:", "/getValue/:name", select);
const collection = await loadMongoCollection();
const value = await collection.findOne({},select)//.toArray();
res.status(201).send(value);
} catch (error) {
res.status(500).send("Failed to connect Database");
console.error("settings.js:", "/getValue/:name:", error);
}
});
async function loadMongoCollection() {
const dbUri = process.env.MONGODB_CONNSTRING || config.dbUri;
const MongoDB = process.env.MONGODB_DB || config.db;
try {
const client = await mongodb.MongoClient.connect(dbUri, {
useNewUrlParser: true,
});
const conn = client.db(MongoDB).collection("values");
return conn;
} catch (error) {
console.error("settings.js:", "loadMongoCollection:", error);
}
}
When I try it, I get all (not only the Speed Object) what I expected, or nothing.
What I do wrong?
EDIT 2022.01.06:
I try it to change my database:
data = {
speed: {
value: 65,
type: 2,
},//...
}
//...
await collection.replaceOne({}, {values:data}, { upsert: true });
And then the query:
const select = {[`values.${val}.value`]:"1"};
const where = {}; //{"values":val};
const value = await collection.find(where,select,).toArray();
But it will not work in rest... is there an issue in mongo package?
When I do it in https://cloud.mongodb.com - it works:
But my request returns all values...
Log shows:"'settings.js:', '/getValue/:name', { 'values.speed.value': '1' }"
In the screenshot you show the projection is values.speed.value, but in your endpoint you have:
const select = {};
select[`${val}.value`] = 1;
Which would evaluate to speed.value. To mimic what you have on the MongoDB console this should be:
const select = {};
select[`values.${val}.value`] = 1;
So, I changed my Frondend (vue) to spred all Values. I maked some Helper Functions:
CheckForChangedObj
check two object for changes
updateObj
copy all data from object into another
export function CheckForChangedObj(targetObject, obj) {
if (targetObject === typeof undefined || targetObject == {}) {
return true
}
if (JSON.stringify(targetObject) !== JSON.stringify(obj)) {
return true
}
return false
}
export function updateObj(targetObject, obj) {
let keys = Object.keys(obj)
for (let k in keys) {
try {
let key = keys[k]
if (!targetObject.hasOwnProperty(key)) {
//define target, if not exist
targetObject[key] = {}
}
//Workaround for References
if (obj[key].hasOwnProperty("__v_isRef")) {
if (targetObject[key].hasOwnProperty("__v_isRef")) {
targetObject[key].value = obj[key].value
} else {
targetObject[key] = obj[key].value
}
} else {
//Source i a Norm Value
//check for an Object inside, then go in...
if ("object" === typeof obj[key] && !Array.isArray(obj[key])) {
updateObj(targetObject[key], obj[key]) // recurse
} else {
//not else-if!!! __v_isRef: true
if (targetObject[key].hasOwnProperty("__v_isRef")) {
targetObject[key].value = obj[key]
} else {
targetObject[key] = obj[key]
}
}
}
} catch (error) {
console.log(
"key:",
keys[k],
"Error:",
error
)
}
}
}
Then spreading...
import {updateObj,CheckForChangedObj } from "../tools/tools"
beforeMount() {
this.getAllValuesAPI()
setInterval(() => {
this.checkForChanges()
// ml("TABLE", this.values)
}, 10000)
},
setup() {
let prevValues = {}
let values = {
speed: {
id:1,
type: SettingType.Slider,
value: ref(0)
}
//...
}
function checkForChanges() {
//intervaled function to get changes from API
let changed = CheckForChangedObj(this.values, this.prevValues)
if (changed) {
updateObj(this.prevValues, this.values)
setValuesToAPI()
}
else{
getAllValuesFromAPI()
}
}
async function getAllValuesFromAPI() {
//get ALL Settings-Data from API, and put them in values
const res = await axios.get(`settings/getAll`)
return updateObj(this.values, res.data[0].values) u
}
}
When you have a better solution, please help...

TypeError: dbConn.GetInstance is not a function

I trying to create a common database connection class using typescript for my nodejs express application that returns the MongoDB database object as follows but I always get TypeError: dbConn.GetInstance is not a function
const config = require("config");
const MongoClient = require('mongodb').MongoClient;
export class dbConn {
private db = null;
static url = config.database.uri;
static options = {
bufferMaxEntries: 0,
reconnectTries: 5000,
useNewUrlParser: true,
useUnifiedTopology: true,
};
private constructor() { }
private static instance: dbConn;
public static GetInstance() {//I also tried removing static keyword but still the error remains
if (dbConn.instance == null)
{
dbConn.instance = new dbConn();
}
return dbConn.instance;
}
public getDb() {
if (dbConn.instance.db) {
return dbConn.instance.db;
}
MongoClient.connect(dbConn.url, dbConn.options, function(err: any, db: any){
if(err) {
console.log(err);
return null;
}
dbConn.instance.db = db.db(config.database.name);
return dbConn.instance.db;
});
}
}
Updated 01-Aug-2020
I invoke the above instance from app.ts and my controllers as follows:
app.ts file
const dbConn = require('./utils/db/dbConn');
...//code removed for clarity
const app = express();
const server = http.createServer(app);
...//code removed for clarity
server.listen(port, ()=> {
dbConn.GetInstance().getDb();//I get the error here
console.log('Server running')
});
module.exports = app;
my controller file
getAll = async (pageNumber:any, pageSize:any) : Promise<PageResult<Team>> => {
return new Promise<PageResult<Team>>(async function (resolve, reject){
let result = new PageResult<Team>(pageSize, pageNumber);
var dbo = dbConn.GetInstance().getDb();//same error here too.
var query = {};
var recCount = await dbo.collection("teams").find().count();
if (recCount == 0) {
result.IsSuccessful = true;
result.ReasonForFailure = process.env.NO_RECORDS || "No record(s) to show.";
return resolve(result);
}
if (pageSize == -1) { //-1 means to return all records
dbo.collection("teams")
.find(query)
.sort({ name: 1 })
.toArray(function(err: any, resultSet: any) {
if (err) {
result.IsSuccessful = false;
result.ReasonForFailure = err.message;
return reject(result);
} else {
result.IsSuccessful = true;
result.TotalRecords = recCount;
result.PageNumber = parseInt(pageNumber);
result.PageSize = parseInt(pageSize);
result.Data = resultSet;
return resolve(result);
}
});
} else {
dbo.collection("teams")
.find(query)
.sort({ name: 1 })
.skip((parseInt(pageNumber)-1)*parseInt(pageSize))
.limit(parseInt(pageSize)).toArray(function(err: any, resultSet: any) {
if (err) {
result.IsSuccessful = false;
result.ReasonForFailure = err.message;
return reject(result);
} else {
result.IsSuccessful = true;
result.TotalRecords = recCount;
result.PageNumber = parseInt(pageNumber);
result.PageSize = parseInt(pageSize);
result.Data = resultSet;
return resolve(result);
}
});
}
});
}
Can you please assist what is wrong or what is the missing piece to get this to work?
Thanks,
Hemant.
It seems you're using commonjs as module-resolution strategy. Your import will be the problem in that case. Try changing it to:
const dbConn = require('./utils/db/dbConn').dbConn;
or
const { dbConn } = require('./utils/db/dbConn');
or
import {dbConn } from './utils/db/dbConn';
Here's a simple example to show what's going on. Consider this simple ts-class:
export class TestClass {
static test():void {
console.log("it works")
}
}
It will be transpiled into:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestClass = void 0;
class TestClass {
static test() {
console.log("in workds");
}
}
exports.TestClass = TestClass;
//# sourceMappingURL=index.js.map
If you then require this with const TestClassModule = require('./test-class');, TestClassModule will yield:
{ TestClass: [Function: TestClass] }
Hence, you need to use const { TestClass } = require('./test-class');.

AWS Lambda (NodeJS) does not log to cloudwatch

I'm trying to log my lambda app after following serverless-next.js because of the issue where I can't go to the root of my file. So basically I'm deploying nextJS app in AWS through lambda#edge, s3, and cloudfront.
I'm new to AWS so I'm not really sure how to debug this thing at all. I assume traditional console.log in my lambda where every request comes in would log it in the cloudwatch. I also made sure that I deployed my lambda to my cloud front
Here's the code:
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handler = void 0;
const prerender_manifest_json_1 = __importDefault(require("./prerender-manifest.json"));
const manifest_json_1 = __importDefault(require("./manifest.json"));
const next_aws_cloudfront_1 = __importDefault(require("#sls-next/next-aws-cloudfront"));
const addS3HostHeader = (req, s3DomainName) => {
req.headers["host"] = [{ key: "host", value: s3DomainName }];
};
const isDataRequest = (uri) => uri.startsWith("/_next/data");
const normaliseUri = (uri) => (uri === "/" ? "/index" : uri);
const normaliseS3OriginDomain = (s3Origin) => {
if (s3Origin.region === "us-east-1") {
return s3Origin.domainName;
}
if (!s3Origin.domainName.includes(s3Origin.region)) {
const regionalEndpoint = s3Origin.domainName.replace("s3.amazonaws.com", `s3.${s3Origin.region}.amazonaws.com`);
return regionalEndpoint;
}
return s3Origin.domainName;
};
const router = (manifest) => {
const { pages: { ssr, html } } = manifest;
const allDynamicRoutes = Object.assign(Object.assign({}, ssr.dynamic), html.dynamic);
return (uri) => {
let normalisedUri = uri;
if (isDataRequest(uri)) {
normalisedUri = uri
.replace(`/_next/data/${manifest.buildId}`, "")
.replace(".json", "");
}
if (ssr.nonDynamic[normalisedUri]) {
return ssr.nonDynamic[normalisedUri];
}
console.log(uri);
for (const route in allDynamicRoutes) {
const { file, regex } = allDynamicRoutes[route];
const re = new RegExp(regex, "i");
const pathMatchesRoute = re.test(normalisedUri);
if (pathMatchesRoute) {
return file;
}
}
if (html.nonDynamic["/404"] !== undefined) {
return "pages/404.html";
}
return "pages/_error.js";
};
};
exports.handler = (event) => __awaiter(void 0, void 0, void 0, function* () {
const request = event.Records[0].cf.request;
const uri = normaliseUri(request.uri);
const manifest = manifest_json_1.default;
const prerenderManifest = prerender_manifest_json_1.default;
const { pages, publicFiles } = manifest;
const isStaticPage = pages.html.nonDynamic[uri];
const isPublicFile = publicFiles[uri];
const isPrerenderedPage = prerenderManifest.routes[request.uri];
const origin = request.origin;
const s3Origin = origin.s3;
const isHTMLPage = isStaticPage || isPrerenderedPage;
const normalisedS3DomainName = normaliseS3OriginDomain(s3Origin);
s3Origin.domainName = normalisedS3DomainName;
if (isHTMLPage || isPublicFile) {
s3Origin.path = isHTMLPage ? "/static-pages" : "/public";
addS3HostHeader(request, normalisedS3DomainName);
if (isHTMLPage) {
request.uri = `${uri}.html`;
}
return request;
}
const pagePath = router(manifest)(uri);
if (pagePath.endsWith(".html")) {
s3Origin.path = "/static-pages";
request.uri = pagePath.replace("pages", "");
addS3HostHeader(request, normalisedS3DomainName);
return request;
}
const page = require(`./${pagePath}`);
const { req, res, responsePromise } = next_aws_cloudfront_1.default(event.Records[0].cf);
if (isDataRequest(uri)) {
const { renderOpts } = yield page.renderReqToHTML(req, res, "passthrough");
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(renderOpts.pageData));
}
else {
page.render(req, res);
}
return responsePromise;
});
Permission:
Allow: logs:CreateLogGroup
Allow: logs:CreateLogStream
Allow: logs:PutLogEvents
What else should I do? Should I create a new stream or is it automatically created? I can see a log group in my cloudwatch named aws/lambda but i'm not sure how to connect them
Really appreciate any help
Cheers

Seeding mongoDB data in node.js by referencing ObjectId

i'm using mongoose-data-seed to seed data into mongodb, however it has no mechanism to allow passing of ObjectId() as references to other seed files
I found a way to store the output of each of the seeders in a json file and retrieve the ObjectIds from the previous seeds to use in the current seeder. This way i can reference ObjectIds from previous seeders.
seeding-helper.js
const fs = require('fs');
// const path = require('path');
const seedersTmpDataFolder = 'seeders/bin';
class SeedingHelper {
static saveData(filename, data) {
return new Promise((resolve) => {
fs.writeFile(`${seedersTmpDataFolder}/${filename}.json`, JSON.stringify(data, null, '\t'), (err) => {
if (err) throw err;
resolve();
});
});
}
static readData(filename) {
return new Promise((resolve) => {
fs.readFile(`${seedersTmpDataFolder}/${filename}.json`, 'utf8', (err, data) => {
if (err) throw err;
resolve(JSON.parse(data));
});
});
}
}
module.exports = SeedingHelper;
resourceActions.seeder.js
const { Seeder } = require('mongoose-data-seed');
const mongoose = require('mongoose');
const ResourceAction = require('../models/resourceAction');
const SeedingHelper = require('../helpers/seeding-helper');
const { Types: { ObjectId } } = mongoose;
const data = [
{
_id: ObjectId(),
name: 'test1'
},
{
_id: ObjectId(),
name: 'test2'
},
];
class ResourceActionSeeder extends Seeder {
async shouldRun() { // eslint-disable-line class-methods-use-this
return ResourceAction.count().exec().then(count => count === 0);
}
async run() { // eslint-disable-line class-methods-use-this
let result;
await SeedingHelper.saveData('resourceActions', data)
.then(() => {
result = ResourceAction.create(data);
});
return result;
}
}
module.exports = ResourceActionSeeder;
resources.seeder.js
const { Seeder } = require('mongoose-data-seed');
const mongoose = require('mongoose');
const Resource = require('../models/resource');
const SeedingHelper = require('../helpers/seeding-helper');
const { Types: { ObjectId } } = mongoose;
class ResourcesSeeder extends Seeder {
async shouldRun() { // eslint-disable-line class-methods-use-this
return Resource.count().exec().then(count => count === 0);
}
async run() { // eslint-disable-line class-methods-use-this
let result;
await SeedingHelper.readData('resourceActions')
.then((resourceActionsData) => {
const machinesId = ObjectId();
const actionTest1 = ObjectId(resourceActionsData.find(x => x.name === 'test1')._id);
const actionTest2 = ObjectId(resourceActionsData.find(x => x.name === 'test2')._id);
const data = [
{
_id: machinesId,
name: 'machines',
actions: [
actionTest1,
actionTest2,
],
},
];
result = Resource.create(data);
if (result) SeedingHelper.saveData('resources', data);
});
return result;
}
}
module.exports = ResourcesSeeder;

Resources