I'm currently trying to implement a base class which has multiple properties. All but one property are being injected via the constructor using InversifyJS's #Inject tag. I'm also getting an instance via the container.Get() function in the constructor. When I run my application everything is fine, but when the application recieves a request, the app crashes without an error.
Base class
/**
* Base class for all intent logic
*/
#injectable()
export default abstract class IntentBase implements IIntent {
protected logger: ILogger;
protected responseService: IResponseService;
protected contextService: IContextService;
protected fallbackIntent: IIntent;
protected responseBuilder: IResponseBuilder;
/**
* Constructor
* #param logger logger
* #param responseService response service
* #param contextService context service
*/
constructor(
#inject(TYPES.WinstonLogger) logger: ILogger,
#inject(TYPES.ResponseService) responseService: IResponseService,
#inject(TYPES.ContextService) contextService: IContextService,
#inject(TYPES.ResponseBuilder) responseBuilder: IResponseBuilder,
) {
this.logger = logger;
this.responseService = responseService;
this.contextService = contextService;
this.responseBuilder = responseBuilder;
this.fallbackIntent = container.get(TYPES.DefaultFallbackIntent); // <-- container.Get() line
}
/**
* Override the standard fallback logic for an intent.
* #param fallback fallback to set
*/
setFallback(fallback: IIntent): void {
this.fallbackIntent = fallback;
}
When I uncomment the container.get(TYPES.DefaultFallbackIntent) line, my application can recieve requests like normally and it doesn't crash. The reason why I am trying this injection setup is because I'd like to set a default behavior for each child class in the constructor.
Does anyone have an explaination to why I cannot seem to inject this class?
Thanks in advance
Update
inversify.config.ts
import DefaultFallbackIntent from "./src/bot/intents/fallbacks/defaultFallbackIntent";
import TextResponseRepository from "./src/repositories/textResponseRepository";
import SsmlResponseRepsitory from "./src/repositories/ssmlResponseRepository";
import ContextService from "./src/services/contextService";
import GoogleResponseBuilder from "./src/builders/googleResponseBuilder";
const container = new Container();
container.bind<IGoogleAssistantController>(TYPES.GoogleController).to(GoogleAssistantController);
container.bind<IResponseService>(TYPES.ResponseService).to(ResponseSerivce);
container.bind<IContextService>(TYPES.ContextService).to(ContextService);
container.bind<IResponseRepository>(TYPES.TextResponseRepository).to(TextResponseRepository);
container.bind<IResponseRepository>(TYPES.SsmlResponseRepository).to(SsmlResponseRepsitory);
container.bind<ILogger>(TYPES.WinstonLogger).to(WinstonLogger);
container.bind<IIntent>(TYPES.WelcomeIntent).to(WelcomeIntent);
container.bind<IIntent>(TYPES.DefaultFallbackIntent).to(DefaultFallbackIntent);
container.bind<IResponseBuilder>(TYPES.ResponseBuilder).to(GoogleResponseBuilder);
export { container };
Default fallback intent
/**
* Default fallback intent class
*/
#injectable()
export default class DefaultFallbackIntent extends IntentBase {
invoke(conv: DialogflowConversation): DialogflowConversation {
const response = this.responseService.getResponse("defaultFallback");
return conv.ask(response);
}
}
In order for container.get(<Type>) to work, <Type> must be bound to the container at some point. In your composition root (where you set up your container) you can create this binding:
const container = new Container();
container.bind<IIntent>(TYPES.DefaultFallbackIntent)).to(<TheDefaultFallbackIntentClass>);
EDIT:
From the discussion in the comments it seems that DefaultFallbackIntent inherting from IntentBase is the problem, because at the time DefaultFallbackIntent gets instantiated (through binding it), there doesn't exist such an instance in the container when the base constructor is executed.
A workaround would be to not inherit from IntentBase and just implement the interface and set the required fields in this class as well:
/**
* Default fallback intent class
*/
#injectable()
export default class DefaultFallbackIntent implements IIntent {
protected logger: ILogger;
private responseService: IResponseService;
private contextService: IContextService;
private fallbackIntent: IIntent;
private responseBuilder: IResponseBuilder;
invoke(conv: DialogflowConversation): DialogflowConversation {
const response = this.responseService.getResponse("defaultFallback");
return conv.ask(response);
}
}
However, a better solution would be two create another super class which contains all the required fields both the default fallback intent and others have in order to reduce duplicated code.
Related
I'm trying to make a simple change in the domain classes generated by JHipster 7.9.3, I just want to change the sequenceGenerator name:
get [POST_WRITING_ENTITIES_PRIORITY]() {
this.log(`[DEBUG] POST_WRITING_ENTITIES_PRIORITY: get`);
return {
async postWritingTemplateTask() {
const asEntityClass = this.asEntity(this.entity.name);
this.editFile( `${SERVER_MAIN_SRC_DIR}${this.jhipsterConfig.packageFolder}/domain/${asEntityClass}.java`, content => {
return content.replaceAll(' = "sequenceGenerator"', ` = "seq${asEntityClass}"`);
});
},
};
}
The problem is that the "get" function is never called.
When I tried to use the priority POST_WRITING_PRIORITY, the content of entity classes don't have the content still processed, the content is something like the following:
#Schema(description = "not an ignored comment")
<&- fragments.annotationSection() -&>
#SuppressWarnings("common-java:DuplicatedBlocks")
public class MyEntity <&- fragments.extendsSection() -&>implements Serializable<&- fragments.additionalInterfacesSection() -&> {
private static final long serialVersionUID = 1L;
Finally, I tried to use the END_PRIORITY. It kind of works, but it always asks for confirmation to overwrite the files.
Is there a better way to customize the domain classes generated by JHipster?
Regards,
I've found out my mistake! I was inheriting my generator from the wrong class:
import EntityServerGenerator from 'generator-jhipster/esm/generators/entity-server';
...
export default class extends EntityServerGenerator {
...
}
After the following change, the problem was partially solved. Now, the "get" function is called and the functions returned by it are called also.
import { GeneratorBaseEntities } from 'generator-jhipster';
...
export default class extends GeneratorBaseEntities {
...
}
However, the content of entities classes still have those "fragments" notation:
#Schema(description = "not an ignored comment")
<&- fragments.annotationSection() -&>#SuppressWarnings("common-java:DuplicatedBlocks")
public class MyEntity <&- fragments.extendsSection() -&>implements Serializable<&- fragments.additionalInterfacesSection() -&> {
private static final long serialVersionUID = 1L;
I have 2 Node modules. In module A I have the following definition:
/**
* HTTP Client
* #module src/http/client
*/
/**
* A HTTP Client
* #alias src/http/client
*/
class HTTPClient {
[... class def with documented methods etc]
}
module.exports = HTTPClient
And now in module B I want to say that the first constructor parameter should be of type HTTPClient. So I tried the following
class PackageFactory {
/**
* #param {module:src/http/client} httpClient - the HTTPClient instance
*/
constructor(httpClient) {
this._httpClient = httpClient
}
}
I also tried a few variations but it never worked. Within module B the httpClient is always of type "any". What do I have to change so I can see the class member of HTTPClient in module B?
The solution was easier then I thought. There is no need to include the module paths (aka longname) or anything.
const HTTPClient = require('../http/client')
class PackageFactory {
/**
* #param {HTTPClient} httpClient - the HTTPClient instance that shall be used to make requests
*/
constructor(httpClient) {
this._httpClient = httpClient
}
}
To avoid the HTTPClient is unused error that any linter will give you, you can actually import the class in the #param field itself. This makes the doc a bit less easy to read, but you get autocompletion at design time, a nice tradeoff.
Note that you can combine this with the compilerOptions.paths option of tsconfig.json to create aliases that will make those documentation-imports look better.
class PackageFactory {
/**
* #param {import('../http/client')} httpClient - the HTTPClient instance that shall be used to make requests
*/
constructor(httpClient) {
this._httpClient = httpClient
}
}
i have created one custom module, in the controller file i used File::load static method. but when I'm run phpcs for check coding standards it will give an error as
File::load calls should be avoided in classes, use dependency injection instead
anyone can please tell how to create dependency injection for this.
The be achieved by using Drupal\Core\Entity\EntityTypeManagerInterface.
use Drupal\Core\Entity\EntityTypeManagerInterface;
class MyForm extends FormBase {
/**
* The storage handler class for files.
*
* #var \Drupal\file\FileStorage
*/
protected $fileStorage;
/**
* This is an example.
*
* #param \Drupal\Core\Entity\EntityTypeManagerInterface $entity
* The Entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity) {
$this->fileStorage = $entity->getStorage('file');
}
....
}
From there you can update the File::load($fid) to $this->fileStorage->load($fid)
i have a weird problem...
i try to build a button that onClick doing something(i dont use in regular event because i need to transfer a data with the event).
i built a UIButon class and create an instance of him in the parent class.
i create a custom Event class:
package ;
import openfl.events.Event;
/**
* ...
* #author Michael
*/
class ChangeWinEvent extends Event
{
public static inline var CHANGE_WINDOW:String = "changeWindow";
public var _winToClose:String;
public function new(name:String, winToClose:String, bubbles:Bool=false, cancelable:Bool=false)
{
super(type, bubbles, cancelable);
_winToClose = winToClose;
}
}
and i dispatch CHANGE_WINDOW event like this:
dispatchEvent(new ChangeWinEvent(ChangeWinEvent.CHANGE_WINDOW,"LoginWin"));
and listen to this event in parent class:
_loginBtn.addEventListener(ChangeWinEvent.CHANGE_WINDOW, handleChangeWindows);
thank you helpers!
michael
You also have to override the clone method. Take a look at this custom event class i'm currently using:
/**
* Custom button event used for communication
* between button classes and their respective
* views.
* #author Tiago Ling Alexandre
*/
class ButtonEvent extends Event
{
public static var ACTIVATE:String = "Activate";
public var data:Dynamic;
public function new(type:String, data:Dynamic, bubbles:Bool = false, cancelable:Bool = false)
{
super(type, bubbles, cancelable);
this.data = data;
}
override public function clone():Event
{
return new ButtonEvent(type, bubbles, cancelable);
}
}
The only difference from your class is the clone() method. That's the missing piece.
Regards,
super(type, bubbles, cancelable);
...apparently takes type variable from type instance field (as you have constructor parameter named name, not type), so it is null and you haven't registered any listener for null event type.
By default the generated XText artifacts generate code from my DSL to the default outlet (which defaults to src-gen folder). I know that you can explicitly pass the outlet configuration name in fsa.generateFile("myfile.txt", "MY_OUTLET_NAME", "Some file content").
I it because I want to generate code with my XText DSL and want to use the generation gap pattern and generate code in a folder called "src-once".
I'am using XText 2.2.1.
My questions:
1) Where and how do I define my outlets like "MY_OUTLET_NAME"?
2) Is there a way to prevent overwriting existing files in a specific outlet?
The hint form Christian Dietrich pointed me in the right direction. Below is the code that I ended up with.
I have created a new class MyOutputConfigurationProvider that implements IOutputConfigurationProvider. The getOutputConfigurations method returns two output configurations, the default src-gen and a custom src-gen-once with the correct settings for generating sources only once.
package com.my.dsl;
import static com.google.common.collect.Sets.newHashSet;
import java.util.Set;
import org.eclipse.xtext.generator.IFileSystemAccess;
import org.eclipse.xtext.generator.IOutputConfigurationProvider;
import org.eclipse.xtext.generator.OutputConfiguration;
public class MyOutputConfigurationProvider implements
IOutputConfigurationProvider {
public final static String DEFAULT_OUTPUT_ONCE = "DEFAULT_OUTPUT_ONCE";
/**
* #return a set of {#link OutputConfiguration} available for the generator
*/
public Set<OutputConfiguration> getOutputConfigurations() {
OutputConfiguration defaultOutput = new OutputConfiguration(IFileSystemAccess.DEFAULT_OUTPUT);
defaultOutput.setDescription("Output Folder");
defaultOutput.setOutputDirectory("./src-gen");
defaultOutput.setOverrideExistingResources(true);
defaultOutput.setCreateOutputDirectory(true);
defaultOutput.setCleanUpDerivedResources(true);
defaultOutput.setSetDerivedProperty(true);
OutputConfiguration onceOutput = new OutputConfiguration(DEFAULT_OUTPUT_ONCE);
onceOutput.setDescription("Output Folder (once)");
onceOutput.setOutputDirectory("./src-gen-once");
onceOutput.setOverrideExistingResources(false);
onceOutput.setCreateOutputDirectory(true);
onceOutput.setCleanUpDerivedResources(false);
onceOutput.setSetDerivedProperty(true);
return newHashSet(defaultOutput, onceOutput);
}
}
To use the MyOutputConfigurationProvider implementation add a configure method to your module class:
/**
* Use this class to register components to be used within the IDE.
*/
public class MyDslUiModule extends com.my.dsl.ui.AbstractMyDslUiModule {
public MyDslUiModule(AbstractUIPlugin plugin) {
super(plugin);
}
#Override
public void configure(Binder binder) {
super.configure(binder);
binder.bind(IOutputConfigurationProvider.class).to(MyOutputConfigurationProvider.class).in(Singleton.class);
}
}
implement a custom IOutputConfigurationProvider should do the trick