How can "sequelize.import()" import models from another file? - javascript

When I create a new model in the following way:
//user.js file
module.exports = function (sequelize, DateTypes) {
return sequelize.define("user", {
email: {
type: DateTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DateTypes.STRING,
allowNull: false,
validate: {
len: [7, 100]
}
}
});
};
and into db.js file where i built a new database:
var Sequelize = require('sequelize');
var env = process.env.NODE_ENV || "development"; // established if you work in production or in development mode
var sequelize;
if (env == "production") {
sequelize = new Sequelize(process.env.DATABASE_URL, {
"dialect": "postgres",
});
} else {
var sequelize = new Sequelize(undefined, undefined, undefined, {
'dialect': 'sqlite',
'storage': __dirname + '/data/dev-todo-api.sqlite' // location where you create a new sqlite database
});
}
var db = {};
db.todo = sequelize.import(__dirname + "/models/todo.js");
db.user = sequelize.import(__dirname + "/models/user.js");
db.sequelize = sequelize; //contain a settings of database
db.Sequelize = Sequelize;
module.exports = db;
I don't understand how user.js knows that sequelize (that I insert as a parameter into module.exports) is the instance of a sequelize package if it is situated into another file? Maybe because with sequelize.import('/user.js') it imports the entire sequelize package?

See the definition of sequelize.import:
Sequelize.prototype.import = function(path) {
// is it a relative path?
if(Path.normalize(path) !== Path.resolve(path)){
// make path relative to the caller
var callerFilename = Utils.stack()[1].getFileName()
, callerPath = Path.dirname(callerFilename);
path = Path.resolve(callerPath, path);
}
if (!this.importCache[path]) {
var defineCall = (arguments.length > 1 ? arguments[1] : require(path));
if (typeof defineCall === 'object' && defineCall.__esModule) {
// Babel/ES6 module compatability
defineCall = defineCall['default'];
}
this.importCache[path] = defineCall(this, DataTypes);
}
return this.importCache[path];
};
Effectively it calls require on the path and then calls the result with the sequelize instance as its first argument. This is what ties the knot allowing the module to have a reference to the sequelize instance that imported it.

Might be helpful. This is what that code looks like for me when compiled:
/**
* Imports a model defined in another file
*
* Imported models are cached, so multiple calls to import with the same path will not load the file multiple times
*
* See https://github.com/sequelize/express-example for a short example of how to define your models in separate files so that they can be imported by sequelize.import
* #param {String} path The path to the file that holds the model you want to import. If the part is relative, it will be resolved relatively to the calling file
* #return {Model}
*/
import(path) {
// is it a relative path?
if (Path.normalize(path) !== Path.resolve(path)) {
// make path relative to the caller
const callerFilename = Utils.stack()[1].getFileName();
const callerPath = Path.dirname(callerFilename);
path = Path.resolve(callerPath, path);
}
if (!this.importCache[path]) {
let defineCall = arguments.length > 1 ? arguments[1] : require(path);
if (typeof defineCall === 'object') {
// ES6 module compatibility
defineCall = defineCall.default;
}
this.importCache[path] = defineCall(this, DataTypes);
}
return this.importCache[path];
}

Related

Running raw complicated queries with sequelize

I have a peculiar problem. I have a joined query due to the distribution of data in various table. I want to run this as raw sql query using node module for sequelize as alot existing code is associated using sequelize orm. I do not want to use orm here but just a plain raw query.
SELECT testmodel.bacode, testmodel2.paopkey from (SELECT ptr.paopkey as paopkey, psgkey.pskey from (SELECT * FROM pts where paopkey = 4 and ptr_active = 1) ptr,
pt_sg psgkey where ptr.pkey = psgkey.pkey) testmodel2,
pt_act testmodel where testmodel.pskey = testmodel2.pskey;
Any advise if the above query can be run as raw query and the data can be captured as result.
Also when I run the below code with below sequelize code, I get
the error "_sequelize.default.query is not a function"
import Sequelize from 'sequelize';
async function getTestDataList(opcoKey) {
const testdata = await Sequelize.query(`SELECT testmodel.bacode, testmodel2.paopkey from (SELECT ptr.paopkey as paopkey, psgkey.pskey from (SELECT * FROM pts where paopkey = 4 and ptr_active = 1) ptr,
pt_sg psgkey where ptr.pkey = psgkey.pkey) testmodel2,
pt_act testmodel where testmodel.pskey = testmodel2.pskey;`,
{ type: Sequelize.QueryTypes.SELECT }).then(function (results) {
// SELECT query - use then
})
return toPlain(testdata);
}
Dbclient code using sequelize instance is as follows
import config from 'config';
import Sequelize from 'sequelize';
class DataSource { constructor() {this.DBclient = null; }
initializeDB(dbCredentials) { const dataSource = config.datasource;
const options = { host: "127.0.0.1", dialect: dataSource.dialect, port: dataSource.port, logging: false, };
const { password } = dbCredentials || dataSource;
this.DBclient = new Sequelize( dataSource.database, "root", "root", options, );
} getDBClient() { return this.DBclient; }}export default new DataSource();
You should store a created Sequelize connection and use it to call query method:
const sequelize = new Sequilize(connection_options_here)
const testdata = await sequelize.query(`SELECT testmodel.bacode, testmodel2.paopkey from (SELECT ptr.paopkey as paopkey, psgkey.pskey from (SELECT * FROM pts where paopkey = 4 and ptr_active = 1) ptr,
pt_sg psgkey where ptr.pkey = psgkey.pkey) testmodel2,
pt_act testmodel where testmodel.pskey = testmodel2.pskey;`,
{ type: Sequelize.QueryTypes.SELECT })
I removed then in the above code because there is no need in it thus you use await.

loop in a module export javascript/sequelize

I have an index.js script that contains sequelize models
here is the tree structure of my folder
models/
files/
xml_files/
csv_files/
txt_files/
index.js
server.js
this my index.js code :
const generate_files = require('./files')
const File = generate_files(sequelize, DataTypes)
const generate_xml_files = require('./xml_files')
const Xml_File = generate_xml_files(sequelize, DataTypes)
const generate_csv_files = require('./csv_files')
const Csv_File = generate_csv_files(sequelize, DataTypes)
const generate_txt_files = require('./txt_files')
const Txt_File = generate_txt_files(sequelize, DataTypes)
module.exports = {
Files, Xml_File, Csv_File, Txt_File
}
in `` server.js ``` I imported each model like this :
const {Files, Xml_File, Csv_File, Txt_File} = require('./models')
Now in server.js I want to get the name of the module sequelize then I check if the name of the module matches the name of the table like this :
const {Files, Xml_File, Csv_File, Txt_File} = require('./models')
if (Files.name == TableName){
Files.findAll()
}
if (Xml_File.name == TableName){
Xml_File.findAll()
}
if (Csv_File.name == TableName){
Csv_File.findAll()
}
....
how can I avoid doing tests every time ?
You can do:
const Example = require('./index')
Example.Files
As for making a loop, I'm not sure you can do that, but you can do:
export const File = generate_files(sequelize, DataTypes)
Which is the same thing as:
module.exports = {File}
You can create an object and loop through the entries, like so.
var exports = {
'File': './files',
'Xml_File': './xml_files',
'Csv_File': './csv_files',
'Txt_File': './txt_files'
};
var e = {};
Object.entries(exports).forEach(ex=>{
e[ex[0]] = require(ex[1])(sequelize, DataTypes);
});
module.exports = e;
Then you'll want to import them from this index file...
const {Files, Xml_File, Csv_File, Txt_File} = require('./models/index.js')
Per your update..
const Models = require('./models/index.js');
Object.entries(Models).forEach(model=>{
if (model.name == TableName){
model.findAll();
}
}
To simplify your index.js you could also do it like this:
module.exports = {
Files: require('./files')(sequelize, DataTypes),
Xml_File: require('./xml_files')(sequelize, DataTypes),
Csv_File: require('./csv_files')(sequelize, DataTypes),
Txt_File: require('./txt_files')(sequelize, DataTypes)
}
it's easier to read, at least for me.
Another way is to loop over the dir so you never need to change the code in index.js even if you add another folder like /xxx_files
const Fs = require('fs');
var dir = Fs.readdirSync(__dirname, { withFileTypes: true });
var exports = {};
dir.forEach(d => {
if (d.isDirectory())
exports[d.name] = require('./' + d)(sequelize, DataTypes);
});
// !!!!!!! the names of the exports will be names of the directories
module.exports = exports;
And to avoid the checks if (Files.name == TableName)
all you need is to not import like this:
const {Files, Xml_File, Csv_File, Txt_File} = require('./models')
but rather like this:
const models = require('./models')
// models = { files, xml_files, csv_files, txt_files }
then you can do this:
var db = models[TableName] // assuming TableName is files, xml_files etc.
if (db)
doSomething();

can't use egg-mysql in egg.js framework

in my nodejs program,i use egg.js framework to create an API server.I am using egg-mysql to crud my database,here are error code i don't understand.And there is no mysql in the declaration of ctx.app
nodejs version: 12.16.1,
npm version: 6.14.3
{"code":"ERR_INVALID_ARG_TYPE","message":"The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received undefined","stack":"TypeError [ERR_INVALID_ARG_TYPE]: The \"string\" argument must be of type string or an instance of Buffer or ArrayBuffer. Received undefined\n at Function.byteLength (buffer.js:713:11)\n at respond (/root/workspace/node_modules/koa/lib/application.js:261:25)\n at handleResponse (/root/workspace/node_modules/koa/lib/application.js:164:34)\n at processTicksAndRejections (internal/process/task_queues.js:97:5)","name":"TypeError","status":500}
this is my config files
//config/plugin.js
'use strict';
/** #type Egg.EggPlugin */
exports.mysql = {
enable: true,
package: 'egg-mysql',
};
//config/config.default.js
module.exports = appInfo => {
const config = exports = {}
config.mysql = {
client: {
host: 'localhost',
port: '3306',
user: 'aichechaoshi',
password: 'Aiche123',
database: 'aichechaoshi_db',
},
app: true,
agent: false,
};
// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1585128324727_7762';
// add your middleware config here
config.middleware = [];
// add your user config here
const userConfig = {
// myAppName: 'egg',
};
return {
...config,
...userConfig,
};
};
in service file
//app/service/mysql.js
async read() {
const { ctx } = this;
const result = await ctx.app.mysql.select('2019_09_zhouqi_brand');
return result;
}
need help!!!

logger implementation using winston , morgan and winston-daily-rotate-file

I am trying to implement a logger in node js which will create a new log file every day on a custom format for logs
for this i have used three packages
winston
morgan
winston-daily-rotate-file
so the final output should every day a new log file should create in logs folder and it should log all the http(morgan logs) and typed logs (winston logs) into a single file in the below format
Date || Filename || statusCode || logMessage || uuid (for tracing)
eg: Fri Jan 18 2019 13:48:18 GMT+0530 (IST) || [index.js] || 200 || calling the new route || 287dccb0-1afa-11e9-88a0-dfb1c665be9d
so for this i have written three files index.js(root file of nodejs) logger.js(logger implementation and configuration) and logger.test.js(test cases for logger using jest)
additional packages
cors
uuid
http-context
app-root-path
express-http-context
jest
the problems that i have
if i put a logger.error({message: {statusCode:200, logMsg: "the server will be starting in port 3000"}}) in the index.js on app.listen before to console.log() the uuid is null
the test cases that i have written is wrong, i am new to jest i just want to know how can i check all that cases.
why when i test the suits uuid is null, how can i pass the uuid for test cases also
how can i check whether new folder will be created and if already logs folder are there new file is created kind of test cases.
How can i add other levels , info, debuge , warn based on the env. How can i improve this code to implement the logger functionality
// index.js
const app = require('express')();
const cors = require('cors')
const morgan = require('morgan') // HTTP request logger middleware
const logger = require('./config/logger')(module) //Logger
const uuid = require('uuid')
const httpContext = require('express-http-context')
// Use any third party middleware that does not need access to the context here
// app.use(some3rdParty.middleware);
app.use(httpContext.middleware);
// all code from here on has access to the same context for each request
// Run the context for each request.
// Assigning a unique identifier to each request
app.use((req, res, next) => {
httpContext.set('reqId', uuid.v1());
next()
})
// using morgan with winston(logger)
app.use(morgan('combined', {
stream: {
write: (message) => logger.error(message)
}
}))
app.use(cors());
app.use("/new", (req, res) => {
logger.error({
message: {
statusCode: 400,
logMsg: "hitting new route"
}
})
nextLayer(res)
})
const nextLayer = (res) => {
logger.error({
message: {
statusCode: 400,
logMsg: "hitting in nextLayer function"
}
})
res.send("OK")
}
app.listen(4000, () => {
console.log('Server running on port 4000');
})
// Logger.js
const appRoot = require('app-root-path')
const {
createLogger,
format,
transports
} = require('winston')
const {
combine,
timestamp,
label,
printf
} = format
const path = require('path')
require('winston-daily-rotate-file');
const httpContext = require('express-http-context')
/**
* #method checkMessageProp
* #param {message} can be object if developer defined, else it will be string
* if its a network request
* #returns a fixed format how the status code and message should show
*/
const checkMessageProp = (message) => {
switch (typeof message) {
case "object":
const {
statusCode,
logMsg
} = message
return `${statusCode ? statusCode : "Not Defined"} || ${logMsg ? logMsg : "Not Defined"}`;
case "string":
let messageSplit = message.split(`"`)
var message = messageSplit ? `${messageSplit[2].trim().split(" ")[0]} || ${messageSplit[1]}` : null
return message
default:
return message
}
}
/**
* #method customFormat
* #param {log} the log passed by the developer or based on network requests
* #returns a customFormat how it should be logged to the log files
*/
const customFormat = printf(log => {
const now = new Date();
const reqId = httpContext.get('reqId');
return `${log.timestamp ? new Date(log.timestamp) : now} || [${log.label}] || ${checkMessageProp(log.message)} || ${reqId ? reqId : null}`
});
/**
* #method getFileName
* #param {moduleObj} the module realted object passed from the require of logger file
* #returns the file name where the logger was invoked
*/
const getFileName = moduleObj => {
if (Object.keys(moduleObj).length > 0) {
let parts = moduleObj.filename.split(path.sep)
return parts.pop()
} else {
return "Module not passed while requiring the logger"
}
}
// Custom settings for each transport
const options = moduleObj => {
return {
dailyRotateFile: {
filename: `${appRoot}/logs/TPS-UI-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
prepend: true,
level: "error",
timestamp: new Date(),
localTime: true
}
}
}
// Instantiate a Winston Logger with the settings
let logger = moduleObj => {
return createLogger({
format: combine(
label({
label: getFileName(moduleObj)
}),
customFormat
),
transports: [
new transports.DailyRotateFile(options(moduleObj).dailyRotateFile)
],
exitOnError: false // do not exit on handled exceptions
})
}
module.exports = logger
// logger.test.js
const logger = require('./logger')
beforeEach(() => {
mockLoggerMessageObject = {
message: {
statusCode: 400,
logMsg: "Calling in test suite"
}
}
mockLoggerMessageString = `::ffff:127.0.0.1 - - [18/Jan/2019:04:50:57 +0000]
"GET /new HTTP/1.1" 200 2 "http://localhost/" "Mozilla/5.0
(linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0"`
mockLoggerMessageNumberFormat = 123
mockLoggerMessageArrayFormat = ["data", "test", 123]
})
describe(`Logger test cases`, () => {
test('should invoke the logger function with the mock Logger message object', () => {
expect(logger(module).error(mockLoggerMessageObject)).toBeDefined()
})
test(`should invoke the logger function with empty object`, () => {
expect(logger(module).error({})).toBeDefined()
})
test(`should invoke the logger function without any module object`, () => {
expect(logger({}).error(mockLoggerMessageObject)).toBeDefined()
})
test(`should invoke the logger function without any module and message object`, () => {
expect(logger({}).error({})).toBeDefined()
})
test(`should invoke the logger function with the http request`, () => {
expect(logger(module).error(mockLoggerMessageString)).toBeDefined()
})
test(`should invoke the logger function with the number format`, () => {
expect(logger(module).error(mockLoggerMessageNumberFormat)).toBeDefined()
})
test(`should invoke the logger function with the array format`, () => {
expect(logger(module).error(mockLoggerMessageArrayFormat)).toBeDefined()
})
})
for winston i'm using timestamp(), like this it will automatically add timestamp() property to the object
const {transports, createLogger, format} = require('winston');
const logger = createLogger({
format: format.combine(
format.timestamp(),
format.json()
),
Also to check if it creates file you can mock date, to say 2019-01-01 and check is it create file 2019-01-01.log
than move date to 2019-01-02 and log something else.
Winston will create new folder and gzip archive and you can check is file exists and can be unzipped and contains information
Try to read winston's documentation.
Basically i would say that you may need to use
format.timestamp()
format.json()
colorize()
dailyRotate with zippedArchive:true
If morgan doesn't suits your needs you can try to log directly in
app.use((req, res, next) => {
logger.silly({ message:'start', req,res});
return next().then(r=>logger.silly({ message:'end', req,res}; return r;);
}

Ember FastBoot SimpleDOM body is empty

I'm trying to read from the DOM in an instance initializer in the FastBoot environment but document.body seems to be empty. The following code works in the browser but returns nothing in FastBoot:
const ELEMENT_NODE_TYPE = 1;
// TODO(maros): Get this working with FastBoot. For some reason the SimpleDOM
// body is empty.
const findBootstrapData = (document) => {
for (let currentNode = document.body.firstChild; currentNode; currentNode = currentNode.nextSibling) {
if (currentNode.nodeType !== ELEMENT_NODE_TYPE) {
continue;
}
if (currentNode.getAttribute('class') === 'bootstrap-data') {
return JSON.parse(currentNode.firstChild.nodeValue);
}
}
};
export function initialize(appInstance) {
const store = appInstance.lookup('service:store');
const document = appInstance.lookup('service:-document');
const data = findBootstrapData(document);
if (data) {
store.push({ data: data.posts });
}
}
export default {
name: 'blog-posts',
initialize
};
The data that I'm trying to read has been injected into the {{content-for "body"}} section using an Ember CLI addon. This works perfectly without FastBoot.
How can I get this instance initializer working in FastBoot?
Edit:
For extra context, here's how I'm populating the DOM using an Ember CLI Addon:
/* eslint-env node */
'use strict';
const fs = require('fs');
const path = require('path');
const convertMarkdown = require('marked');
const parseFrontMatter = require('front-matter');
// Reads blog posts from `/posts`, compiles from markdown to HTML and stores
// as JSON in a script tag in the document. An instance initializer then picks
// it up and hydrates the store with it.
module.exports = {
name: 'blog-posts',
contentFor: function(type) {
if (type !== 'body') {
return;
}
const dirname = path.join(__dirname, '..', '..', 'posts');
const data = fs.readdirSync(dirname).map((filename, index) => {
if (!filename.endsWith('.md')) {
return;
}
const fileContent = fs.readFileSync(path.join(dirname, filename), 'utf-8');
const frontMatter = parseFrontMatter(fileContent);
return {
id: index + 1,
type: 'post',
attributes: {
title: frontMatter.attributes.title,
body: convertMarkdown(frontMatter.body),
},
};
});
return `
<script class="bootstrap-data" type="text/template">
${JSON.stringify({ posts: data })}
</script>
`;
}
};
document.body will always be empty during the app boot lifecycle. Since the rendering is not done yet (during the instance initializer phase), the fastboot placeholders aren't replaced.
You could do either of the following:
You could use the shoebox API to push and get the fastboot bootstrap data : http://ember-fastboot.com/docs/user-guide#the-shoebox
If you want to do it outside the app lifecycle, then you could do it after fastboot.visit request is done and after result.html() is called.

Categories

Resources