Accessing Jhipster Principal in ngOnInit - jhipster

First let me say thank you to all the JHispter developers and also that this project is amazing.
I have a Jhipster 4.14.0 microservices app that uses JWT.
I am trying to replace the menu with one from PrimeNG.
This works fine, but I need to access
principal.isAuthenticated()
and
principal.hasAnyAuthority(['ROLE_ADMIN'])
from ngOnInit or another way to load the menu data. This always returns false. I tried some other lifecycle methods and principal.isAuthenticated() eventually returns true after quite a few calls.
ngAfterContentChecked() {
console.log('ngAfterContentChecked');
console.log(this.principal.isAuthenticated());
}
This works fine when I use isAuthenticated from html
<div>{{isAuthenticated()}}</div>
,but I want to control the menu by creating a menu model in the component, but I cannot figure out how to load it correctly.
if(!this.isAuthenticated()) {
this.model.push({ label: 'Sign in', icon: 'fa fa-fw fa-sign-in', command: (onclick)=> {this.login()} });
} else {
this.model.push({ label: 'Sign out', icon: 'fa fa-fw fa-sign-in', command: (onclick)=> {this.logout()} });
}
I also tried adding the following to ngOnInit in NavbarComponent.ts
this.principal.identity().then((account) => {
this.currentAccount = account;
});
,but I get this error in the chrome console.
core.js?593e:1440 ERROR TypeError: _this.connectedPromise is not a function
at eval (tracker.service.ts?aa77:50)
at eval (webstomp.js?afe9:292)
at Array.forEach (<anonymous>)
at SockJS.ws.onmessage (webstomp.js?afe9:284)
at SockJS.EventTarget.dispatchEvent (eventtarget.js?d62b:51)
at eval (main.js?8e93:274)
at Array.forEach (<anonymous>)
at SockJS._transportMessage (main.js?8e93:272)
at WebSocketTransport.EventEmitter.emit (emitter.js?a8c1:50)
at WebSocketTransport.ws.onmessage [as __zone_symbol__ON_PROPERTYmessage] (websocket.js?a354:35)
defaultErrorLogger # core.js?593e:1440
ErrorHandler.handleError # core.js?593e:1501
next # core.js?593e:5481
schedulerFn # core.js?593e:4319
SafeSubscriber.__tryOrUnsub # Subscriber.js?3959:240
SafeSubscriber.next # Subscriber.js?3959:187
Subscriber._next # Subscriber.js?3959:128
Subscriber.next # Subscriber.js?3959:92
Subject.next # Subject.js?8398:56
EventEmitter.emit # core.js?593e:4299
(anonymous) # core.js?593e:4755
ZoneDelegate.invoke # zone.js?6524:388
Zone.run # zone.js?6524:138
NgZone.runOutsideAngular # core.js?593e:4681
onHandleError # core.js?593e:4755
ZoneDelegate.handleError # zone.js?6524:392
Zone.runTask # zone.js?6524:191
ZoneTask.invokeTask # zone.js?6524:495
invokeTask # zone.js?6524:1536
globalZoneAwareCallback # zone.js?6524:1562
Any advice would be greatly appreciated on how to get the menu to load correctly based on authentication or roles when the app first loads. The menu works fine, otherwise.

I was also wondering if this would work in a production build. The setTimeout method did not wait in the production build.
ngOnInit() {
setTimeout(() => {
this.loadMenu();
}, 0);
}
The only thing I have been able to get to work so far is using setInterval in ngOninit. I keep polling to see if a user is authenticated and then load the menu and clear the interval.
ngOnInit() {
/**
* This will load the menu, but only show items that do not require login
* because the principal is not valued yet.
*/
this.loadMenu();
/**
* This interval will run until the user is authenticated and then will reload the menu and clear the interval.
*/
this.intervalId = setInterval(() => {
if(this.isAuthenticated()) {
this.loadMenu();
clearInterval(this.intervalId);
}
}, 100);
}
This seems like a hack to me and I feel there must be a better way. Maybe create a menu service that observes the principal? If anyone has any I ideas please let me know.

I did this in navbar.component.ts:
ngOnInit() {
this.registerAuthenticationSuccess();
}
registerAuthenticationSuccess() {
this.eventManager.subscribe('authenticationSuccess', (message) => {
this.getAccount();
});
}
getAccount() {
this.principal.identity().then((account) => {
this.account = account;
});
}

Related

How to create a custom blueprint?

I'm trying to create a customized JHipster blueprint for my organization.
I've started my journey:
Installed Yeoman v4.3.0
Installed Jhipster v7.9.3
Created a directory for my future blueprint mkdir mygenerator && cd mygenerator
Executed the command to create a new blueprint: jhipster generate-blueprint
selected only the sub-generator server
add a cli: Y
Is server generator a side-by-side blueprint: Y
Is server generator a cli command: N
selected the tasks: initializing, prompting and configuring
From this point, I've opened the generated blueprint project with VS Code and noticed a first problem, some jhipster packages can't be resolved:
Unable to resolve path to module 'generator-jhipster/esm/generators/server'
Unable to resolve path to module 'generator-jhipster/esm/priorities'
I also noticed that the generator created for me has a small difference from the existing generators in the JHipster Github, such as jhipster-dotnetcore, generator-jhipster-quarkus, generator-jhipster-nodejs: the returned functions are async while in the cited repos they are regular functions (sync):
get [INITIALIZING_PRIORITY]() {
return {
async initializingTemplateTask() {},
};
}
Does it make any difference in this Jhipster version or there is no problem if I return the same way as jhipster-dotnetcore:
get initializing() {
return {
...super._initializing(),
setupServerConsts() {
this.packagejs = packagejs;
...
I've assumed that this detail is not important and followed with async function and write my prompting function to get some input from the user/developer in order to replace values in the template files :
get [PROMPTING_PRIORITY]() {
return {
...super._prompting(),
async promptingTemplateTask() {
const choices = [
{
name: 'OAuth 2.0 Protocol',
value: 'oauth2',
},
{
name: 'CAS Protocol',
value: 'cas',
},
];
const PROMPTS = {
type: 'list',
name: 'authenticationProtocol',
message: 'Which authentication protocol do you want to use?',
choices,
default: 'oauth2',
};
const done = this.async();
if (choices.length > 0) {
this.prompt(PROMPTS).then(prompt => {
this.authenticationProtocol = this.jhipsterConfig.authenticationProtocol = prompt.authenticationProtocol;
done();
});
} else {
done();
}
},
};
}
<%_ if (authenticationProtocol == 'oauth2') { _%>
security:
enable-csrf: true
oauth2:
client:
clientId: ${this.baseName}
clientSecret: Z3ByZXBmdGVy
accessTokenUri: http://localhost:8443/oauth2.0/accessToken
userAuthorizationUri: http://localhost:8443/oauth2.0/authorize
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
logoutUri: http://localhost:8443/logout
clientSuccessUri: http://localhost:4200/#/login-success
resource:
userInfoUri: http://localhost:8443/oauth2.0/profile
<%_ } _%>
thymeleaf:
mode: HTML
templates/src/test/java/resources/config/application.yml.ejs
All this done, I've followed the next steps:
Run npm link inside the blueprint directory.
Created a new directory for a app example: mkdir appmygenerator && cd appmygenerator
Started a new example app with my blueprint: jhipster --blueprint mygenerator --skip-git --skip-install --skip-user-management --skip-client answering all question.
Here I've got some surprises:
After answering What is the base name of your application? I've got this warning: [DEP0148] DeprecationWarning: Use of deprecated folder mapping "./lib/util/" in the "exports" field module resolution of the package at /...<my-generator-path>/node_modules/yeoman-environment/package.json. Update this package.json to use a subpath pattern like "./lib/util/*"
My prompting function somehow made some questions be repeated, from question Do you want to make it reactive with Spring WebFlux? until Which other technologies would you like to use?.
When my prompt was finally shown, there was a message in front of the last option: CAS Protocol Run-async wrapped function (sync) returned a promise but async() callback must be executed to resolve
I've made some changes to my prompt function: removed the calling of super._prompting() with the hope to solve the item 2, and removed the async in the hope to solve the item 3.
Well ... apparently it was solved. But I get a new error when JHipster (or Yeoman) try process the template:
An error occured while running jhipster:server#writeFiles
ERROR! /home/fabianorodrigo/Downloads/my-blueprint/generators/server/templates/src/test/resources/config/application.yml.ejs:47
45| favicon:
46| enabled: false
>> 47| <%_ if (authenticationProtocol == 'oauth2') { _%>
48| security:
49| enable-csrf: true
50| oauth2:
authenticationProtocol is not defined
How come authenticationProtocol is not defined? I'm stuck here. What I could noticed is that, in all the Jhipster's generators I've cited above, the prompt function sets the properties like "this.[property] = [value]" and the "this.jhipsterConfig.[property] = [value]" and in the templates they are referenced (just the property's name) and it works.
What am I missing? Why even if I set the property "this.authenticationProtocol" in the function prompting it is not seem at the template?
Yeoman (yo/yeoman-generator/yeoman-environment) are not required and should no be a dependency to avoid duplication in the dependency tree, unless you know what you are doing. JHipster customizes them, yeoman-test is required by tests.
Unable to resolve path to module is a bug at eslint-plugin-import
I also noticed that the generator created for me has a small difference from the existing generators in the JHipster Github, such as jhipster-dotnetcore, generator-jhipster-quarkus, generator-jhipster-nodejs. Those blueprints are quite old (blueprint support is changing very fast for v8/esm) and are full server/backend replacements, seems you are trying to add cas support. The use case is quite different.
Does it make any difference in this Jhipster version or there is no problem if I return the same way as jhipster-dotnetcore? Yes, get [INITIALIZING_PRIORITY]() is the new notation, and INITIALIZING_PRIORITY may be >initializing instead of initializing. The explanation is here. JHipster v8 will not support the old notation.
...super._prompting(), is used to ask original prompts, since this is a side-by-side blueprint, prompts will be duplicated.
[DEP0148] DeprecationWarning: Use of deprecated folder mapping "./lib/util/" is a bug in yeoman-environment, and should be fixed in next version.
CAS Protocol Run-async wrapped function (sync) returned a promise but async() callback must be executed to resolve is shown because you are using async function with const done = this.async(); done(); together.
this.async() is a to support async through callbacks before Promises were a js default.
There are a few blueprints that uses new notation and can be used as inspiration: native, ionic, jooq and entity-audit.
I didn't see anything about the writing priority, so it looks like you are overriding an existing template and the original generator will write it. For this reason you should inject you configuration into the original generator.
The end result should be something like:
get [INITIALIZING_PRIORITY]() {
return {
async initializingTemplateTask() {
this.info('this blueprint adds support to cas authentication protocol');
},
};
}
get [PROMPTING_PRIORITY]() {
return {
async promptingTemplateTask() {
await this.prompt({
type: 'list',
name: 'authenticationProtocol',
message: 'Which authentication protocol do you want to use?',
choices: [
{
name: 'OAuth 2.0 Protocol',
value: 'oauth2',
},
{
name: 'CAS Protocol',
value: 'cas',
},
],
default: 'oauth2',
}, this.blueprintStorage); // <- `this.blueprintStorage` tells the prompt function to store the configuration inside `.yo-rc.json` at the blueprint namespace.
},
};
}
get [CONFIGURING_PRIORITY]() {
return {
configuringTemplateTask() {
// Store the default configuration
this.blueprintConfig.authenticationProtocol = this.blueprintConfig.authenticationProtocol || 'oauth2';
},
};
}
get [LOADING_PRIORITY]() {
return {
loadingTemplateTask() {
// Load the stored configuration, the prompt can be skipped so this needs to be in another priority.
this.authenticationProtocol = this.blueprintConfig.authenticationProtocol;
// Inject the configuration into the original generator. If you are writing the template by yourself, this may be not necessary.
this.options.jhipsterContext.authenticationProtocol = this.blueprintConfig.authenticationProtocol;
},
};
}

Uncaught (in promise) TypeError: Cannot set properties of null (setting 'innerText') at app.js:2186:27

when i use axios in my node app to change the cart icon value it works but after the page refresh/reload. but it should work without page reload please help me to fix this problem. github link for my project check here to understand resources->js->app.js click here
app.js
import axios from 'axios'import Noty from 'noty'let addToCart = document.querySelectorAll('.add-to-cart')let cartCounter = document.querySelector('#cartCounter')function updateCart(food) {axios.post('/update-cart', food).then(res => {cartCounter.innerText = res.data.totalQtynew Noty({type: 'success',timeout: 1000,text: 'Item added to cart',progressBar: false,}).show();}).catch(err => {new Noty({type: 'error',timeout: 1000,text: 'Something went wrong',progressBar: false,}).show();})}
addToCart.forEach((btn) => {btn.addEventListener('click', (e) =>{
let food=JSON.parse(btn.dataset.food)
updateCart(food)
})
})
problem that shows in the inspect console
app.js:2186 Uncaught (in promise) TypeError: Cannot set properties of null (setting 'innerText') at app.js:2186:27 (anonymous) # app.js:2186 Promise.then (async) updateCart # app.js:2185 (anonymous) # app.js:2205
It works but after the page reload/refresh then my cart value incremented otherwise not.
If you are using functional components, you need to use the useEffect handler. If you are using class components, you need to use the componentDidMount() property.
// In Functional Component
useEffect(() => {
// Code you want to run when the component mounts
}, []);
// In Class Component
componentDidMount() {
// Code you want to run when the component mounts
}

Jest puppeteer typescript reference error Page not found

Trying to setup typescript jest with puppeteer
i following step by step instructions as mentioned below
Jest-puppeteer with typescript configuration
there is a simple test
describe('Google', () => {
beforeAll(async () => {
await page.goto('https://google.com')
})
it('should display "google" text on page', async () => {
await expect(page).toMatch('google')
})
})
when i run my test i get weird error
ReferenceError: page is not defined
and it is pointing to the 'await page' object inside beforeAll
i also notice chrome try to kick in and but does not launch may be this error is b/c chrome could not launch.
jest-puppeteer library is responsible for launching a browser and providing browser and page objects
and here is the code taken from the page mentioned above in link
//jest-puppeteer.config.js
let jest_puppeteer_conf = {
launch: {
timeout: 30000,
dumpio: true // Whether to pipe the browser process stdout and stderr
}
}
const isDebugMode = typeof v8debug === 'object' || /--debug|--inspect/.test(process.execArgv.join(' '));
if (isDebugMode) {
jest_puppeteer_conf.launch.headless = false; // for debug: to see what the browser is displaying
jest_puppeteer_conf.launch.slowMo = 250; // slow down by 250ms for each step
jest_puppeteer_conf.launch.devtools = true; // This lets you debug code in the application code browser
jest_puppeteer_conf.launch.args = [ '--start-maximized' ]; // maximise the screen
}
module.exports = jest_puppeteer_conf;
there is a small debug section at the bottom which reminds to add following types i already have them and still no luck, any help is appreciated.
"compilerOptions": {
.....
"types": [
.......
"puppeteer",
"jest-environment-puppeteer",
"expect-puppeteer"
]
}
commenting out the following line fix the issue.
// testEnvironment: "node"

Phaser 3 ScaleManager exception

I am trying to enable fullscreen in my game written in Phaser 3.
I am doing it from Scene class via
this.game.scale.startFullScreen();
but getting error in f12 browser console
Uncaught TypeError: this.game.scale.startFullScreen is not a function
at TitleScene.<anonymous> (TitleScene.js:23)
at InputPlugin.emit (phaser.js:2025)
at InputPlugin.processDownEvents (phaser.js:167273)
...
In docs ScaleManager class has startFullScreen method.
Why console tells me it doesn't?
This is the full code of TitleScene.js:
export class TitleScene extends Phaser.Scene {
constructor ()
{
const config =
{
key: 'TitleScene'
}
super(config);
}
preload ()
{
this.load.image('Title', 'assets/Title.png');
}
create ()
{
this.background = this.add.image(960, 540, 'Title');
this.input.manager.enabled = true;
this.input.once('pointerdown', function () {
this.scene.start('MainScene');
this.game.scale.startFullScreen(); // here is the error
}, this);
}
}
There are two problems prevented me from resolving this problem:
I followed examples from here
https://www.phaser.io/examples/v2
But I am using the third version Phaser. And everyone who uses the same must follow examples from here
https://www.phaser.io/examples/v3
You must pay attention to url while using their site with examples. Both pages are the same from the first look. But urls are different. Also there are warning after each example using the second (old) version of engine.
And finally this function name is not startFullScreen but startFullscreen :)

How to remove or replace all console.log in Angular SSR

I've built an angular project with CLI. when I run the project It shows all console.log that I used in development mode. Then I googled and found a solution. Replaced with empty function in the production mode by adding two lines of code in main.ts file:
if (environment.production) {
enableProdMode();
if (window) {
window.console.log = window.console.warn = window.console.info = window.console.debug = function () {/* no log */ };
}
}
It helps me to get rid out of that problem in production mode and saves a lot of time of removing all console.log lines manually. So, I added those lines in main.server.ts file also to solve the problem in SSR.
But, the problem is when I build with angular SSR and run the project it returns errors:
ReferenceError: window is not defined
at Object.K011 (C:\xampp\htdocs\10MS\10-minute-school-3.0\dist\server\main.js:1:176367)
at __webpack_require__ (C:\xampp\htdocs\10MS\10-minute-school-3.0\dist\server\main.js:1:295)
at Object.0 (C:\xampp\htdocs\10MS\10-minute-school-3.0\dist\server\main.js:1:5525)
at __webpack_require__ (C:\xampp\htdocs\10MS\10-minute-school-3.0\dist\server\main.js:1:295)
at +JNS (C:\xampp\htdocs\10MS\10-minute-school-3.0\dist\server\main.js:1:1624)
at Object.<anonymous> (C:\xampp\htdocs\10MS\10-minute-school-3.0\dist\server\main.js:1:1669)
at Module._compile (internal/modules/cjs/loader.js:956:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
at Module.load (internal/modules/cjs/loader.js:812:32)
at Function.Module._load (internal/modules/cjs/loader.js:724:14)
I understand the problem that there is no global window variable in nodejs. But, I need to remove all console.log lines. How can I able to remove all console.log lines in SSR build.
Instead of doing it in the main.ts file, you can override the console methods (log, warn, error, etc) in the app.module.ts file just before the app is initialized. This can be done using the APP_INITIALIZER injector token that allows you run some custom initialization logic needed for your app.
The PLATFORM_ID is another injector token that is provided by the Angular once the platform is initialized (platform can be server, browser or web worker) and it is available before application initialization. Using this token we can determine the platform using the isPlatformBrowser(platformId) method which returns true if the platform is a browser.
We can combine the above 2 strategies to override the console methods as below.
app.module.ts
import {
NgModule,
Injector,
APP_INITIALIZER,
PLATFORM_ID
} from "#angular/core";
import { isPlatformBrowser, isPlatformServer } from "#angular/common";
import { environment } from '../environments/environment';
...
/**
* Factory method to override the console methods
* during app initialization
*/
function silenceConsoleMethods(injector: Injector) {
return () => {
const platformId = injector.get(PLATFORM_ID);
if (isPlatformBrowser(platformId) && environment.production) {
window.console.log = window.console.debug = window.console.warn = window.console.error = () => {};
} else if (isPlatformServer(platformId)) {
console.log = console.debug = console.warn = console.error = () => {};
}
};
}
#NgModule({
...
providers: [
{
provide: APP_INITIALIZER,
useFactory: silenceConsoleMethods,
deps: [Injector],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
You can find a working version of the above example in Stackbliz here. To demonstrate the solution works, I have overriden all console logging methods except error to show the error messages on console.
P.S:
You can use isPlatformServer(platformId) to determine if the platform is server and perform any initialization logic related to server side rendering.
Edit:
Updated silenceConsoleMethods() in the app.module.ts file to override the console object when the app is rendered using Nodejs runtime. Now you should not face any problem when the app is rendered on server side.

Resources