I'm developing an application in which I need to have some abstraction.
I mean there, that I would like to "simulate" an interface behaviour like creating a contract inside of my concrete classes.
Actually, dealing with Users, I'm having a UserMongoRepository class with the contract implemented :
getAll() returns the full list of users by promise
getById(id) returns the user concerned by promise
save(user) saves the user by promise
... etc
I have the same methods implemented inside of the UserMysqlRepository (allowing me to switch behaviour when a change is needed.
Problem
My problem is that I'm dealing with Mongoose that doesn't act like a datamapper, but more like an active record.
It means that my implementation of save(user) would be a bit weird like following :
save(user){
let mongooseUser = this.convert(user);
return user.save();
}
The convert method allows me to switch from a standard Model to a specific Mongoose model. It allows me, again, to have some abstraction and to don't have to rewrite my full application data access.
My real problem is when I try to unit test my full class :
import MongooseUser from '../../auth/mongooseModel/MongooseUser';
/**
* UserMongoRepositoryclass
*/
export default class UserMongoRepository{
/**
* Create an UserMongoRepository
*/
constructor(){
}
/**
* Convert a User to a MongooseUser
*/
convert(user){
return new MongooseUser({email:user.mail,password:user.password,firstname:user.firstName, lastname:user.lastName});
}
findById(id){
return MongooseUser.find({id:id});
}
save(user){
return user.save();
}
}
In a standard way, I would inject my DAO inside of my constructor, and being able to mock it.
In the case of mongoose, it's a bit disturbing, because the element that makes the job isn't an instantiated object (so that I can mock it) but a class definition imported at the top of the document.
Solutions
Should I pass the MongooseUser class definition as a parameter inside of the constructor ?
Implying that I will have this code inside of the convert method :
let user = new this.MongooseUser({})
Have you got a better idea, to abstract mongoose behaviour in data mapper way ?
I don't want to use another module, it's, in my sense, the most advanced one with NodeJS...
I'm not familiar with the import syntax (nor EMCASCRIPT-6), though you say you're using node.js, so I'd recommend using the proxquire package. The idea is that the package allows you to require an external package, while stubbing the requirements that that package would use. So in your case, you could do something like:
proxyquire('../my/class/that/uses/mongoose', {
mongoose: MyTestMongooseImplementation
})
Which would allow you to use your own mongoose implementation while still using your MongooseUser as you have defined it in your package. Alternatively, you could just override the the MongooseUser class (path relative to the the file whose requirements you are stubbing:
proxyquire('/path/to/UserMongooseRepository', {
'../../auth/mongooseModel/MongooseUser': MyTestMongooseUser
})
Documentation: https://www.npmjs.com/package/proxyquire
Related
So my problem is I have models that is separated by one of our domain's types, and it has a lot of types which each one of em has a dedicated collection. As I know, we can inject the model in the service constructor like this way:
#InjectModel(ModelName.Job) private readonly jobModel: JobModel,
It is a bit messy to me to inject all of those collections in the constructor, and also they are not useful at the same time. So I wonder if I could load mongoose model dynamically inside the service's method using the our domain type as the key, more or less same as the module reference like this:
private getModelReference(reference: any) {
return this.moduleReference.get(ModelName[reference]);
}
But, any other workarounds to load the model dynamically on the fly are appreciated.
It is technically possible to do. Using your code above you can do
private getModelReference(reference: any) {
return this.moduleReference.get(getModelToken(ModelName[reference]));
}
Assuming that ModelName[reference] refers back to a mongoose model name (i.e. Cat.name or just 'Cat')
I used loopback 4 to bootstrap my API application and developed some parts of it.
Now I'm trying to access repositories outside the controllers and tried everything I could but didn't help. None of my searches did too. For example I have a controller in which I can access repo such way.
constructor(
#repository(UserRepository) public userRepository: UserRepository){}
But if it isn't a controller it won't work and I found out I had a wrong understanding about #repository and #inject decorators.
My Case:
I want to run a cron-job to perform an update operation on database every day at a specific time.
The thing is I think I should create a service or something like that to expose database operations so it can be accessible anywhere.
The issue you're trying to tackle comes down to dependency injection. It's described at https://loopback.io/doc/en/lb4/Dependency-injection.html but that article does not explain how to create a separate class that works with dependency injection.
In short, in order to perform the dependency injection through decorators, LoopBack needs to be the one to construct the class you're writing, so that it can pick what you need from its Context. So, instead of writing new ClassSomething, it has to be #inject(ClassSomething) or #service(ClassSomething), and those decorators only work in a class also instantiated by LoopBack. In other words, your class has to be in the "call tree" from the original application class (application.ts) for dependency injection to work.
The easiest way to do what you want to do is to use LoopBack's Cron component: see https://loopback.io/doc/en/lb4/Running-cron-jobs.html. You could convert your class to a CronJob if it has no other purpose, or let the CronJob create an instance of your class. In the latter case, you need to bind your class to the application Context so that the CronJob can access it through decorators. You can do so in two ways:
Convert your class to a Service (see https://loopback.io/doc/en/lb4/Service.html and https://loopback.io/doc/en/lb4/Service-generator.html). If what your class does can be thought of as a service, this is the way to go.
Inject your class with: #service(MyServiceClass) myService: MyServiceClass
Directly bind your class to the application context, in application.ts:
this.bind('MyClass').toClass(MyClass);
Inject your class with: #inject(MyClass) myClass: MyClass
The latter option is not best practice AFAIU as it does not adhere to IoC-principles (https://en.wikipedia.org/wiki/Inversion_of_control) - basically, by hard-coding the class binding in the application class, it is not exactly modular. When your class is converted to a service (and placed in src/services), it will automatically be added to the Context, meaning you can inject it everywhere using #service().
I also experience the same(cron word). I couldn't find any solution still in the documentation. But still, I have achieved this in this way.
just call repository as like class with dependency in it. do this in your index.ts file where you start the application.
async function startCronJobs(app: SawLoopbackApplication) {
const userRepo = app.repository(UserRepository);
const userRepoInstance = await userRepo.getValue(app);
const cron = new CronComponent(userInstance);
//---- Exicute your cron here
new CronJob('0 6 * * *', function () {
cron.sendMorningMail()
}).start();
//---- Exicute your Second Cron
new CronJob('0 8 * * 1', function () {
cron.weeklyAppraisalsToAgent()
}).start();
}
call your cron in the functional component and execute a raw query to get the details from DB etc...
import {MysqlDataSource} from '../../datasources'
const query = `select * from accounts where IP="${ipAddress}" ORDER By
createdAt DESC Limit 1`
const dataSource = new MysqlDataSource()
const accountDetails = await dataSource.execute(query)
console.log(accountDetails)
instead of a raw query. call your repository from the function component below
import {MysqlDataSource} from '../datasources'
import {ContactUsUserDetailsRepository} from '../repositories'
const contactUsUserDetailsRepository = new ContactUsUserDetailsRepository(new MysqlDataSource)
function saveContactDetails(){
const payload={UID:"Unique ID",name:"Durai"}
await contactUsUserDetailsRepository.create(payload)
}
I would prefer 2 because, if you have a lot of imports in your cron class constractor you need to pass all of them while calling a function like in the 3rd point.
Have fun:)
I have the follwoing code from tutorial|:
constructor(#InjectModel('User') private readonly userModel: Model<User>) {}
Where User is:
export interface User extends Document {
readonly name: string;
readonly age: number;
readonly phone: string;
}
Could you explain how #InjectModel works, what is 'User' and why we passed Model<User>, what does it mean?
What I can inject also using #InjectModel?
All right, to get into this, first we have to take to truth that interfaces do not exist at runtime. So the User interface you have is only useful during development. I'll try to break this down step by step, starting from the end of the line and working backwards.
Model<User>: Model is an interface type exposed by mongoose that allows us to know that the model we're using has methods like find and create. By saying Model<User> we are saying "This is a mongoose model object that refers to the User interface. This is especially useful for Typescript because as the functions are typed with generics, it knows what the return of methods like find are: an array of User objects. The model interface is really Model<T> where T is the interface that extends Document (another mongoose type).
What is 'User': 'User' is the string equivalent of the name of the interface. If your interface that extends Document is called Dog you use 'Dog', if it's Animal you use 'Animal'. The reason for not passing the interface is because interfaces do not exist at runtime (unlike classes).
How does #InjectModel() work: Okay, the really fun part of the question to answer. Nest works normally by using Injection Tokens. Normally, these tokens are determined by the type of the injected value. In your case Model<User>. Now, the problem here is that A) interfaces don't exist at runtime and B) Typescript does not reflect generics well, so even if Model was a class, all that could be gotten would be Model which isn't enough information on what to inject. So the next logical step Nest takes is to allow a user to provide injection tokens and use the #Inject() decorator. You can do things like injecting an object this way (like package configuration information). Useful, but a bit hard to work with without building your own providers. Now steps in #InjectModel(). #InjectModel() builds an injection token based on the string that's passed into the function. This token is something along the lines of typeModel where type is actually what you pass into the function. This tells Nest specifically what model we are injecting. This also needs to align with the provider created with MongooseModule.forFeature(), hence why name and the value passed to #InjectModel() need to be aligned. Usually it's easiest to align when they use the same string name as the interface.
I use the following pattern when I need to make sure that I have a single instance of a class:
class DB {
constructor() {
// create db connection
}
}
const myDB = new DB();
export default myDB;
So that even if I import it multiple other modules, I get the same instance. Is this a reliable pattern? I know that module caching comes with the file case sensitivity caveat. But are there any edge cases that could cause my multiple imports to create multiple db objects?
I know that I can alternatively create a concrete singleton, with a getInstance() method but this pattern looks simpler.
Modules are cached by file name in node. But caching is case sensitive. require("./Bar") and require("./bar") returns two different object
https://nodejs.org/api/modules.html#modules_module_caching_caveats
I'm doing this Ensime package for Atom.io https://github.com/ensime/ensime-atom and I've been thinking about the possibility to use scala.js instead of writing Coffeescript.
Atom is a web based editor which is scripted with js and is node.js based. A plugin/package defines it's main entry point by pointing out a javascript object with a few specific.
I figured I should start out simple and try using scala.js replacing the simplest coffeescript file I have:
{View} = require 'atom-space-pen-views'
# View for the little status messages down there where messages from Ensime server can be shown
module.exports =
class StatusbarView extends View
#content: ->
#div class: 'ensime-status inline-block'
initialize: ->
serialize: ->
init: ->
#attach()
attach: =>
statusbar = document.querySelector('status-bar')
statusbar?.addLeftTile {item: this}
setText: (text) =>
#text("Ensime: #{text}").show()
destroy: ->
#detach()
As you can see this exports a require.js module and is a class extending a class fetched with require as well.
Sooo.
I'm thinking I'd just use Dynamic for the require dep as I've seen on SO How to invoke nodejs modules from scala.js?:
import js.Dynamic.{global => g}
import js.DynamicImplicits._
private[views] object SpacePen {
private val spacePenViews = require("atom-space-pen-views")
val view = spacePenViews.view
}
But if I wanted to type the super-class, could I just make a facade-trait and do asInstanceOf?
Secondly, I wonder how I can export my class as a node module. I found this:
https://github.com/rockymadden/scala-node/blob/master/main/src/main/coffeescript/example.coffee
Is this the right way? Do I need to do the sandboxing? Couldn't I just get moduleimported from global and write module.exports = _some_scala_object_?
I'm also wondering how I could extend existing js classes. The same problem as asked here, but I don't really understand the answer:
https://groups.google.com/forum/#!topic/scala-js/l0gSOSiqubs
My code so far:
private[views] object SpacePen {
private val spacePenViews = js.Dynamic.global.require("atom-space-pen-views")
type View = spacePenViews.view
}
class StatusBarView extends SpacePen.View {
override def content =
super.div()
}
gives me compile errors that I can't extend sealed trait Dynamic. Of course.
Any pointers highly appreciated!
I'm not particularly expert in Node per se, but to answer your first question, yes -- if you have a pointer to a JS object, and you know the details of its type, you can pretty much always define a facade trait and asInstanceOf to use it. That ought to work.
As for the last bit, you basically can't extend JS classes in Scala.js -- it just doesn't work. The way most of us get around that is by defining implicit classes, or using implicit def's, to get the appearance of extending without actually doing so.
For example, given JS class Foo, I can write
implicit class RichFoo(foo:Foo) {
def method1() = { ... }
}
This is actually a wrapper around Foo, but calling code can simply call foo.method1() without worrying about that detail.
You can see this approach in action very heavily in jquery-facade, particularly in the relationship between JQuery (the pure facade), JQueryTyped (some tweaked methods over JQuery to make them work better in Scala), and JQueryExtensions (some higher-level functions built around JQuery). These are held together using implicit def's in package.scala. As far as calling code is concerned, all of these simply look like methods on JQuery.