Unable to override CartPageMetaResolver - sap-commerce-cloud

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).

Related

Nest can't resolve dependencies of the PublicKeysService (?, ConfigService). Please make sure that the argument HttpService at index [0] is available

I have seen a couple of articles related to this error here, but I still need some guidance on exactly what is going on because I have tried to make HttpService a part of the current PublicKeysModule and i have tried to import it within the PublicKeysModule.
This is my current public keys module:
import { Module } from "#nestjs/common";
import { PublicKeysService } from "./public-keys.service";
import { PublicKeysController } from "./public-keys.controller";
#Module({
providers: [PublicKeysService],
controllers: [PublicKeysController],
})
export class PublicKeysModule {}
The HttpService is coming from Axios and it's imported in the public keys service:
import { Injectable } from "#nestjs/common";
import { HttpService } from "#nestjs/axios";
import { ConfigService } from "#nestjs/config";
import { MessageResponseDto } from "../../../shared-dtos";
import { ConfigService } from "./dtos/public-key-response.dto";
#Injectable()
export class PublicKeysService {
constructor(
private _httpService: HttpService,
private _config: ConfigService
) {}
}
I tried adding HttpModule as imports: [] in the public keys module as I learned in another post that it should be HttpModule and not HttpService, but when I did that, the error did not go away.
Now when I used HttpService in the imports: [HttpService] for public key module. I get a totally new error saying:
Please make sure that the argument AXIOS_INSTANCE_TOKEN at index [0]
is available in the HttpService context
So I am stumped here.
You're missing the imports: [HttpModule] in your PublicKeysModule. That's what gives access to the HttpService in the context of the module.
You need to import the HTTPModule in your PublicKeyModule imports array.
import { HttpModule } from "#nestjs/axios";
import { Module } from "#nestjs/common";
import { PublicKeysService } from "./public-keys.service";
import { PublicKeysController } from "./public-keys.controller";
#Module({
imports: [HttpModule],
providers: [PublicKeysService],
controllers: [PublicKeysController],
})
export class PublicKeysModule {}
Here is a detailed article about how the HTTP Module works How to use Axios in NestJs

NestJS inject custom TypeOrm repository based on an Interface

I'm currently working through the database integration docs for NestJS using TypeOrm. In these docs there are examples that show how to inject a custom database repository using the app.module from NestJS. All of these examples inject classes using the actual type of the custom repository.
#Injectable()
export class AuthorService {
constructor(private authorRepository: AuthorRepository) {}
}
This code is injected via the app.modules by providing a import like such:
#Module({
imports: [TypeOrmModule.forFeature([AuthorRepository])],
controller: [AuthorController],
providers: [AuthorService],
})
export class AuthorModule {}
This works well if you are fine with programming against an implementation, but I prefer to use an interface in my classes. I've already found the solution to injecting classes via an interface with NestJS in a previous question, but when I try to inject my custom repository like that, it doesn't seem to instanciate correctly and becomes undefined.
(node:16658) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'save' of undefined
Because of this, I assume you can only inject customRepositories via the forFeature() call in the app.module, but that won't allow me to use interfaces for injection, as far as I know. Is there any other way I can inject a custom TypeOrm repository without having the replace all my interfaces for the implementation of my custom repository? Thanks in advance!
Edit
Here is my current code, I managed to get it to inject, but this still forces me to use the implementation instead of the interface each time I call the constructor. This is mainly an issue when testing due to mocking.
#CommandHandler(FooCommand)
export class FooHandler
implements ICommandHandler<FooCommand> {
private fooRepository: IFooRepository; // Using Interface as a private property.
private barEventBus: IEventBus;
constructor(fooRepository: FooRepository,
barEventBus: EventBus) { // Forced to use implementation in constructor for injection.
this.fooRepository = fooRepository;
this.barEventBus = barEventBus;
}
#EntityRepository(FooEntity)
export class FooRepository extends Repository<FooEntity> implements IFooRepository {
getFoo() {
// Do stuff
}
}
#Module({
imports: [TypeOrmModule.forRoot(), TypeOrmModule.forFeature([FooRepository]],
// Other module setup
})
export class AppModule {}
It should work with using the InjectRepository decorator where you specify the Repository but then you type is as your interface instead and when testing you just provide the IFooRepository!
Example code:
constructor(#InjectRepository(FooRepository) fooRepository: IFooRepository,
barEventBus: EventBus) {
Edit: This answer is crap, that abstract-class-as-interface hack used does not work out as the defined methods seem to be optional to implement despite being marked as abstract.
Well, kind of got it working. Based on this answer https://stackoverflow.com/a/74561702/913136 I used an abstract class as interface (you can actually implement it) for not being required to pass strings around as tokens. Only drawback is the misuse of the abstract class. Not sure yet if I like it.
Using an actual interface in the same way seems not to be possible unfortunately. Urgh.
#Module({
imports: [
TypeOrmModule.forRoot({
...dataSource.options,
autoLoadEntities: true,
}),
TypeOrmModule.forFeature([Listing]),
],
controllers: [ViewListingController],
providers: [
{
provide: ListingRepository,
useClass: TypeOrmListingRepository,
},
],
})
makeshift interface:
import { Listing } from "./Listing";
export abstract class ListingRepository {
abstract findMostRecent: () => Promise<Listing[]>;
}
implementation:
import { Listing, ListingRepository } from "../../Domain";
import { Injectable } from "#nestjs/common";
import { Repository, DataSource } from "typeorm";
#Injectable()
export class TypeOrmListingRepository
extends Repository<Listing>
implements ListingRepository
{
constructor(private dataSource: DataSource) {
super(Listing, dataSource.createEntityManager());
}
findMostRecent() {
return this.find({});
}
}
import { Controller, Get } from "#nestjs/common";
import { ListingRepository } from "../Domain";
#Controller("listings")
export class ViewListingController {
constructor(private readonly listingRepo: ListingRepository) {}
#Get("/most-recent")
listMostRecent() {
return this.listingRepo.findMostRecent();
}
}

Missing #Injectable annotation when importing json value

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).

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

Angular 6 Error... calendar.component has no exported member 'CalendarComponent'

I report here a issue, hoping some pious souls could help..
Here's the problem, Node gives me this ERROR:
ERROR in src/app/app.module.ts(11,10): error TS2305: Module
'"V:/Projects/Kompany/source/kompany-war/src/main/websrc/test2/test-kompany/src/app/components/calendar/calendar.component"'
has no exported member 'CalendarComponent'.
src/app/components/calendar/calendar.component.ts(27,3): error TS2390:
Constructor implementation is missing.
src/app/components/oculus/oculus.component.ts(2,34): error TS2307:
Cannot find module 'components/calendar/calendar.component'.
src/app/components/top-menu/top-menu.component.ts(2,10): error TS2305:
Module
'"V:/Projects/Kompany/source/kompany-war/src/main/websrc/test2/test-kompany/src/app/components/calendar/calendar.component"'
has no exported member 'CalendarComponent'.
Here's the sitauation..
I have this on the app module...
`import { BrowserModule } from '#angular/platform-browser';`
`import { NgModule } from '#angular/core';`
`import { CalendarModule, DateAdapter } from 'angular-calendar';`
`import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';`
import { CalendarComponent } from'./components/calendar/calendar.component';
What I'm trying to do is putting a calendar library into my site.
The error is on a component I created ( Calendar Component ) and on which I put all the information I found on the web in order to try it..
https://mattlewis92.github.io/angular-calendar/docs/index.html
this is where I downloaded the library.
I'm new to Angular so I'm giving all the info I have, hope not to be boring.
Into Calender Component here is what's there...
import { Component, ChangeDetectionStrategy, OnInit } from '#angular/core';
import { CalendarEvent } from 'angular-calendar';
#Component({
selector: 'app-calendar',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './calendar.component.html'
})
export class DemoComponent {
view: string = 'month';
viewDate: Date = new Date();
events: CalendarEvent[] = [
{
title: 'Click me',
start: new Date()
},
{
title: 'Or click me',
start: new Date()
}
];
constructor ()
ngOnInit() {
}
eventClicked({ event }: { event: CalendarEvent }): void {
console.log('Event clicked', event);
}
}
I read on the site that this error is due to Angular's infrastructure as I got..
And some answers to similar topics where about ordering the imports, but they are already in order.
I really don't know what to do.. hope someone will be helpful..
Lots of smiles
I see a number of errors, all of which angular is complaining about.
There is no exported class called 'CalendarComponent' in the file
you listed - in that file you export a class called 'DemoComponent'
instead.
In line 27 of your component.ts file you have no definition for the
constructor. You have constructor () but you should have
constructor() {} if you want it to be empty.
It looks like you are also attempting to import the wrongly named
class in two other components, named 'oculus.component.ts', and
'top-menu.component.ts'

Resources