Missing #Injectable annotation when importing json value - node.js

I'm currently trying to inject a json file into a service that I am building. This class is injecting the json via a #inject() tag in the constructor.
Below you can find the code that I'm using.
DiContainer.ts
import { Container, decorate, injectable } from "inversify";
import { IResponseBuilder, InjectionTypes, IUserIdGenerator, ILogger, IResponseRepository, IResponseService, IContextService, IFileWriter, IBoardSessionService, IIntent } from "./src/common/types";
import GoogleResponseBuilder from "./src/common/builders/googleResponseBuilder";
import UserIdGenerator from "./src/common/helpers/userIdGenerator";
import { DialogflowConversation } from "actions-on-google";
import WinstonLogger from "./src/common/winstonLogger";
import FileWriter from "./src/common/helpers/fileWriter";
import TextResponseRepository from "./src/repositories/textResponseRepository";
import SsmlResponseRepository from "./src/repositories/ssmlResponseRepository";
import ResponseSerivce from "./src/services/responseService";
import ContextService from "./src/services/contextService";
import { BoardService } from "./src/services/boardService";
import { BoardSessionService } from "./src/services/boardSessionService";
import WelcomeIntent from "./src/intents/new/welcomeIntent";
import uuid from "uuid/v4";
const sessionJson = require("./src/data/boardSessions.json");
const DIContainer = new Container();
DIContainer.bind<IResponseBuilder<DialogflowConversation>>(InjectionTypes.GoogleResponseBuilder).to(GoogleResponseBuilder);
DIContainer.bind<ILogger>(InjectionTypes.WinstonLogger).to(WinstonLogger);
DIContainer.bind<IFileWriter>(InjectionTypes.FileWriter).to(FileWriter);
DIContainer.bind<IUserIdGenerator>(InjectionTypes.UserIdGenerator).to(UserIdGenerator);
DIContainer.bind<IIntent>(InjectionTypes.WelcomeIntent).to(WelcomeIntent);
DIContainer.bind<IResponseRepository>(InjectionTypes.TextResponseRepository).to(TextResponseRepository);
DIContainer.bind<IResponseRepository>(InjectionTypes.SsmlResponseRepository).to(SsmlResponseRepository);
DIContainer.bind<IResponseService>(InjectionTypes.ResponseService).to(ResponseSerivce);
DIContainer.bind<IBoardSessionService>(InjectionTypes.BoardSessionService).to(BoardSessionService);
DIContainer.bind<IContextService>(InjectionTypes.ContextService).to(ContextService);
DIContainer.bind(InjectionTypes.SessionJSON).to(sessionJson);
DIContainer.bind(InjectionTypes.UUIDv4).toFunction(uuid);
export default DIContainer;

Every dependency you try to inject via #inject has to be marked with #injectable and registered through bind.
I don't really know how the decorate function works (so I don't know if you can use it to mark a function, and not a class, as injectable).
By the way, I think you can achieve what you want just registering your dependency as a dynamic value and returning the desired function, as stated here. In your case, something like this:
DIContainer.bind(InjectionTypes.UUIDv4).toDynamicValue((context: interfaces.Context) => { return uuid });
Alternatively, you could just directly import the function in your service without injecting it, or wrap the function in another service you can mark as injectable (let say, an uuid provider service).

Related

Unable to override CartPageMetaResolver

I am trying to override the CartPageMetaResolver which looks like this
import { Injectable } from '#angular/core';
import { BreadcrumbMeta,
CmsService,
PageBreadcrumbResolver,
Priority,
TranslationService } from '#spartacus/core';
import { CartPageMetaResolver } from '#spartacus/core/src/cart/services/cart-page-meta.resolver';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root',
})
export class NxsCartPageMetaResolver extends CartPageMetaResolver {}
While starting the server I am getting the following error
ERROR in ./src/app/features/nxs-cart/services/nxs-cart-page-meta.resolver.ts
Module not found: Error: Can't resolve '#spartacus/core/src/cart/services/cart-page-meta.resolver'
in 'D:\HYBRIS_SPARTACUS\SPARTACUS\nexus\js-storefront\mystore\src\app\features\nxs-cart\services'
what might be the issue ?
The CartPageMetaResolver is unfortunately not exported as part of the public API, it's a bug. We're fixing it, but you might want to unblock yourself by the following trick:
import { ɵct } from '#spartacus/core';
Note that the exact symbol for this class is different per release. This is why it's a temporary workaround, not something you want to keep. You can find the exact symbol in the node_modules/#spartacus/core/spartacus-core.d.ts.
We're fixing this for 3.0, but if you need a backport for 2.1, please let us know (we can continue the conversation in the ticket).

Splitting inversifyJS config file into multiple files

I recently bumped into a backend project using Typescript and would like to implement IoC principle with the help of Inversify.js. Following the official documentation, I have one huge file named inversify.config.ts containing all my interfaces and classes that implement them:
import "reflect-metadata"
import { Container } from "inversify"
import TYPES from './types';
import { ModuleRepo } from '../repo/interfaces'
import { ModuleARepoImpl } from '../repo/moduleA'
import { ModuleBRepoImpl } from '../repo/moduleB'
import { ModuleService } from '../services/interfaces'
import { ModuleAServiceImpl } from '../services/moduleA'
import { ModuleBServiceImpl } from '../services/moduleB'
const container = Container();
container.bind<ModuleRepo>(TYPES.ModuleARepo).to(ModuleARepoImpl);
container.bind<ModuleRepo>(TYPES.ModuleBRepo).to(ModuleBRepoImpl);
container.bind<ModuleService>(TYPES.ModuleAService).to(ModuleAServiceImpl);
container.bind<ModuleService>(TYPES.ModuleBService).to(ModuleBServiceImpl);
export default container;
One big problem in the above setting is when the project gets complex, more modules are added resulting in a very long config file (imagine you have dozens of modules). My plan is to divide it into smaller config files, with inversify.config.ts remains the main file.
consider the following settings:
./dependencies/interface/index.ts
import { Container } from 'inversify';
export type InversifyContainer = Container
export interface BasicInterface {
register(container: InversifyContainer): void
readonly types: Object
}
./dependencies/moduleA/index.ts
import {InversifyContainer, BasicDependencies} from '../interface';
import { ModuleRepo } from '../../repo/interfaces'
import { ModuleARepoImpl } from '../../repo/moduleA'
import { ModuleService } from '../../services/interfaces'
import { ModuleAServiceImpl } from '../../services/moduleA'
export class ModuleADependencies {
register(container: InversifyContainer) {
container.bind<ModuleRepo>(TYPES.ModuleARepo).to(ModuleARepoImpl);
container.bind<ModuleService>(TYPES.ModuleAService).to(ModuleAServiceImpl);
}
readonly types = {
ModuleARepo: Symbol('ModuleARepo'),
ModuleAService: Symbol('ModuleAService'),
}
}
./dependencies/inversify.config.ts
import "reflect-metadata"
import { Container } from "inversify"
import { ModuleADependencies } from './moduleA';
import { ModuleBDependencies } from './moduleB'; // consider moduleB also has the same file
const container = Container();
const registrationList = [ModuleADependencies, ModuleBDependencies];
for (const reg of registrationList) {
new reg().register(container);
}
export default container;
./dependencies/types.ts
import { ModuleADependencies } from './moduleA';
import { ModuleBDependencies } from './moduleB';
const TYPES = {
...(new ModuleADependencies().types),
...(new ModuleBDependencies().types),
}
export default TYPES
However, this way I always have an error showing something like Cannot read property of ModuleARepo of undefined from the types. I browsed the internet however nobody seems to care about how lengthy and messy inversify.config.ts would be if it is in a complex project.
Hoping someone can help with this :)
First of all your problem is described in the doc and has a solution.
Your solution is generally correct but there is a circular dependency
./dependencies/types.ts -> ./dependencies/moduleA/index.ts -> ./dependencies/types.ts
In types a new instance of class is created but the module that contain the class definition imports types. You don't list this import but use TYPES.ModuleARepo in bind.
To avoid it you can make types field static or move it out of the class into a separate exportable object. As a positive side effect of it, there will be no need to instantiate a class in ./dependencies/types.ts.
Just in case please keep in mind that if you instantiate a class that has a Symbol as a field this symbol is unique for every instance since Symbol('ModuleARepo') !== Symbol('ModuleARepo').
Playground

Is there a way to abstract the import from a node module using typescript config files in the same style as the 'paths' property?

I am creating private node modules which for now might change considerably in structure which could mean splitting existing code into multiple packages.
If I have 100 files importing from a package that no longer holds the import I can do a find and replace but it becomes more difficult when classes are imported from that package...
so something like:
import { thing1, thing2} from 'my-package';
in the future may need to be:
import { thing1} from 'my-package';
import { thing2} from 'my-package2';
You can abstract imports using tsconfig like so:
"paths": {
"#shared/*": ["app/shared/*"]
}
But I cant figure out a way to do the same thing with node modules so that if there is a bigger change I only need to change 1 line. Is this possible?
Create index.ts file , import and export your modules:
import { assign, assignWith } from 'lodash';
import { addDays } from 'date-fns';
export { assign, assignWith, addDays };
and import modules from that index:
import { assign, addDays } from './index';
Check https://github.com/nrwl/nx
They can help you in using monorepo approach.
You can split your system into Applications and Libraries.

Get translation text without using FormattedMessage and React ContextTypes

I'm looking for a way to get translation text without using FormattedMessage. Until now, I've found only this solution that provides to use ContextTypes an React's EXPERIMENTAL feature. Are there others ways to accomplish this (or other library/npm module)?
I prefer using context, but react-intl does also provide a higher order component injectIntl you can use instead. This will pass a prop intl that has all the imperative formatting functions.
import React from "react";
import {injectIntl, intlShape} from "react-intl";
class MyComponent extends React.Component {
static propTypes = {
intl: intlShape.isRequired
}
render() {
return <p>{this.props.intl.formatDate(new Date())}</p>;
}
}
export default injectIntl(Component);

How to use TypeScript ambient declaration interfaces in interface declared in d.ts file

I want to do a helper in .ts file like:
class ResponseHelper implements IResponseHelper {...}
and IResponseHelper is simple .d.ts file with
import * as mongoose from 'mongoose';
import * as express from 'express'
interface IResponseHelper {
toStandartResponse(response: mongoose.Promise<any>, res: express.Response): mongoose.Promise<any>;
}
as you can see params in toStandartResponse are coming from mongoose which is ambiently declared. So this if fine but if i do so I cannot us it with 'implements' like class ResponseHelper implements IResponseHelper because I got errror 'Could not find symbol IResponseHelper in external module ResponseHelper' in other words compiler cannot see d.ts file.
If i remove import statements from d.ts. file everuthing is ok but i cannot specify response types then.
Is there any way to use it all together?
I believe when you use import in makes the file a module, so you must export any members you want visible:
export interface IResponseHelper { }
Now you can import it from other files:
import {IResponseHelper} from "./IResponseHelper";
class ResponseHelper implements IResponseHelper { }

Resources