Jhipster: Allowing users to effortlessly generate linked objects under their account

My goal is to develop a service that assists individuals in making informed decisions. To achieve this, I must guide users through an onboarding process where they create entities that reflect their circumstances.

During data input, I aim to establish links between these entities and also connect them to the user.

In my quest to facilitate this, I explored the following resources:

Jhipster extented user entity and account creation

JHipster : Registering a user with additional information

Although these resources didn't address my specific inquiry, my approach aligns with their methodology.

I have devised a user metadata object and linked it to the Jhipster user entity through JDL. Now, my focus is on retrieving the logged in user to establish a reference between the user and the entities they generate.

I've made considerable progress and have utilized insights from the JHipster source code.

Within a component method, several questions arise:

company: Company;
project: Project;
team: Team;

account: Account;
user: User;
userMetadata: UserMetadata;

linkEntities() {

    // Question 1: Am I approaching this correctly?
    // Identify the logged-in account, to which the user is associated
    this.accountService.identity().then(account => {
        this.account = account;
    });

    // Retrieve the user linked to that account
    this.userService.find(this.account.login)
            .subscribe(res => this.user = res.body);

    // Fetch the metadata
    this.userMetadataService.find(this.user.id)
            .subscribe(res => this.userMetadata = res.body);

    // Question 2: Why are these values undefined?
    // Is there a glaring oversight that could explain this?
    console.log(this.account);
    console.log(this.user);
    console.log(this.userMetadata);

    // The company, project, and team entities have already been created and submitted in a previous function. Here, I update them with mutual references.
    this.company.employees.push(currentlyLoggedInUserMetadata)
    this.project.participants.push(currentlyLoggedInUserMetadata)
    this.team.members.push(currentlyLoggedInUserMetadata)

    this.company.teamOwners.push(this.team);
    this.company.companyProjects.push(this.project);

    this.project.implementingTeams.push(this.team);
    this.project.parentCompany = this.company;

    this.team.parentCompany = this.company;
    this.team.teamProjects.push(this.project);

    // Proceed to send the updated entities to the API
    this.subscribeToCompanySaveResponse(this.companyService.update(this.company));
    this.subscribeToProjectSaveResponse(this.projectService.update(this.project));
    this.subscribeToTeamSaveResponse(this.teamService.update(this.team));
}

I'm perplexed about the errors in the three console logs above. I just assigned these values before the logs. As a newcomer to RxJS, could there be a quirk in how observables function causing this?

It would be ideal to have a globally accessible value for the logged-in user stored within the user service (the account service holds a private account instance, but lacks user - should I expose the account publicly as a default private value?)

I'm uncertain about the optimal or most 'JHipster' method to obtain the one-to-one linked user <--> userMetadata object for the presently logged in user.

Moreover, I'm aware that this method is quite extensive and attempts to accomplish multiple tasks. Once I confirm its functionality, I plan to refactor it for better manageability.

If anyone can offer advice on this approach, has undertaken a similar task, or can shed light on why the account and user variables, in particular, are undefined (I performed the method while logged in as admin), your insights would be greatly appreciated!

Thank you in advance for any guidance and time you can provide!

Answer №1

JHipster has a handy Utility class known as SecurityUtils that allows you to easily access the current user's login or their JWT token.

SecurityUtils.getCurrentUserLogin();
SecurityUtils.getCurrentUserJWT();

If you require additional user information, you can utilize the UserRepository to retrieve the complete user object using the user's login obtained from the SecurityUtils class.

UserRepository.findOneByLogin('usersLogin');

This functionality exists on the API side, eliminating the need to access the account object on the front-end for your specific needs.

On a side note, if you are unable to console log the account information in your code, it is likely due to the fact that the promise fetching that data has not been resolved yet, resulting in a null object. To display the information, you must place your console.log within the promise resolution, like so:

this.accountService.identity().then(account => {
    this.account = account;
    console.log(this.account);
});

The same procedure should be followed for any other requests.

Answer №2

Thank you, Phobos, for your helpful information. I have devised a simple solution that has proven to be effective.

For those facing a similar issue in the future, I will outline the steps I took below:

At my current Jhipster version (5.7.2), I utilized the SecurityUtils helper to create a basic API endpoint for retrieving the details of the currently logged-in user (specifically, their metadata which corresponds to a Jhipster user entity).

Initially, I added a method to the metadata entity repository:

/**
 * Spring Data  repository for the UserMetadata entity.
 */
@SuppressWarnings("unused")
@Repository
public interface UserMetadataRepository extends JpaRepository<UserMetadata, Long> {
    Optional<UserMetadata> findOneByUser(User user);
}

As I prefer separate service classes, I referenced the repository in the user metadata service class:

/**
 * Get the user metadata object associated with the current user
 * @return an optional containing the metadata object, if available
 */
public Optional<UserMetadata> getUserMetadataForCurrentUser() {

    Optional<UserMetadata> userMetadata = Optional.empty();

    Optional<User> currentUser = SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneByLogin);

    if (currentUser.isPresent()) {
        userMetadata = userMetadataRepository.findOneByUser(currentUser.get());
    }

    return userMetadata;
}

To simplify things for the client, most logic was implemented in the API. I believe in smart APIs and dumb clients.

In the code above, I used securityUtils to retrieve the user's login and then fetched the user metadata based on that login. The client should not need to request the actual user object.

I passed the Optional of type UserMetadata to the web layer, which utilized Jhipster's ResponseUtil to either return the object within the optional or an error:

/**
 * GET  /user-metadata/me : retrieve the user metadata of the current user.
 *
 * @return the ResponseEntity with status 200 (OK) and with body the userMetadata, or with status 404 (Not Found)
 */
@GetMapping("/user-metadata/me")
@Timed
public ResponseEntity<UserMetadata> getUserMetadataForCurrentUser() {
    log.debug("REST request to get UserMetadata for current user");
    Optional<UserMetadata> userMetadata = userMetadataService.getUserMetadataForCurrentUser();
    return ResponseUtil.wrapOrNotFound(userMetadata);
}

Moving on to the front end, in the userMetadata Angular service:

@Injectable({ providedIn: 'root' })
export class UserMetadataService {
    public resourceUrl = SERVER_API_URL + 'api/user-metadata';

    currentUserMetadata: UserMetadata;

    constructor(protected http: HttpClient) {
        this.getCurrentUserMetadata().subscribe(res => (this.currentUserMetadata = res.body));
    }

    getCurrentUserMetadata() {
        return this.http.get<IUserMetadata>(`${this.resourceUrl}/me`, { observe: 'response' });
}

// Rest of File

I opted to set a globally available value so that other components and services could easily access the metadata variable without having to fetch it every time. I created a method for calling the new API endpoint and invoked it in the constructor for continual availability to the client.

My goal was to minimize complexity for downstream consumers. The component I originally inquired about can now achieve its task with just three lines of code:

this.company.employees.push(this.userMetadataService.currentUserMetadata);
this.project.participants.push(this.userMetadataService.currentUserMetadata);
this.team.members.push(this.userMetadataService.currentUserMetadata);

The intricacies are abstracted within the services and the API itself.

I hope this information proves helpful to others. I have endeavored to provide a thorough explanation as I found this topic to be quite perplexing when delving into the codebase initially. While Jhipster offers many conveniences, it can sometimes feel like magic rather than understanding the mechanics behind it.

Now, I have gained a better understanding of this particular functionality!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error encountered in TypeScript's Map class

When working with TypeScript, I keep encountering an error message that reads "cannot find name Map." var myMap = new Map(); var keyString = "a string", keyObj = {}, keyFunc = function () {}; // assigning values to keys myMap.set(keyString, "val ...

Experimenting with Cesium using Jasmine (Angular TypeScript)

I have a TypeScript app built using Angular that incorporates Cesium: cesium-container.component.ts import { Component, ElementRef } from '@angular/core'; import { Viewer } from 'cesium'; import { SomeOtherCesiumService } from 'sr ...

How to Make an HTTP POST Request in Angular without Including Headers

How can I configure Angular (version 4.0.2) to send a minimal HTTP POST request? When I use the default code like this: import { Http, Response } from '@angular/http'; export class MyService { constructor(public http: Http) { this.http.pos ...

Creating a custom HTTP request in Angular 2

Utilizing a custom HTTP request class to add an Authorization Header to all requests has been successful on the majority of android devices. However, some customers have reported receiving a 'No internet connection' error despite having a functio ...

Trouble extracting and utilizing GraphQL queries in Typescript with relay-compiler

I attempted to utilize relay with the Typescript react starter, but I am encountering several problems. It appears that babel-plugin-relay is unable to detect the graphql statements extracted by the relay-compiler. Below is my compiler script: "relay": " ...

When using an Angular client to send a request with an access token to Azure AD WebAPI, the response may still show

I have been facing an issue with my Angular client while trying to authenticate and receive a token from Azure AD. Despite adding the token to the header and calling the WebAPI, I keep encountering the message: "Authorization has been denied for this requ ...

The functionality of Angular material input fields is compromised when the css backface property is applied

I utilized this specific example to create a captivating 3D flip animation. <div class="scene scene--card"> <div class="card"> <div class="card__face card__face--front">front</div> <div class="card__face card__face--ba ...

The error message indicates that the property `v.context.$implicit` is not callable

I am a beginner with Typescript and it has only been 3 days. I am trying to access data from Firebase and display it in a list. However, I keep encountering an error when trying to navigate to another page using (Click) ="item ()". Can someone point out wh ...

How to efficiently filter an array containing nested objects using TypeScript

I'm currently working with a list of menus and submenus: [ { "NUA_ID_Menu": 1, "TXT_Nom_Menu": "Menu 1", "Liste_Sous_Menus": [ { "TXT_Nom_Menu": ...

Running a Vue.js 3 application with TypeScript and Vite using docker: A step-by-step guide

I am currently facing challenges dockerizing a Vue.js 3 application using Vite and TypeScript. Below is my Dockerfile: FROM node:18.12.1-alpine3.16 AS build-stage WORKDIR /app COPY package.json ./ RUN yarn install COPY . . RUN yarn build-only FROM ngin ...

Utilizing useClass in Angular's APP_INITIALIZER

Before my application starts up, I require some API data and am currently handling it with APP_INITIALIZER and useFactory. However, I aim to enhance the structure by separating all the code from app.module.ts: app.module.ts import { NgModule} from '@ ...

Prevent authenticated users in Angular2 from accessing certain routes

In the main.ts file, I have defined a set of routes: const routes: RouterConfig = [ { path: '', component: HomeComponent }, { path: '', redirectTo: 'home', terminal: true }, { path: 'dashboard', component: Das ...

How to integrate a toggle switch into an Angular datepicker component

Can the toggle switch be placed inside the calendar? I would like to have it positioned at the top of the open calendar so that users can choose to view date details using the toggle switch <form #uploadForm="ngForm" (keydown.enter)="$event.preventDe ...

What causes the NbAdjustableConnectedPositionStrategy error and how can it be remedied efficiently?

Using the nebular theme for UI, I have encountered an error in my template that was not present in my previous project. In this current Nx (monorepo) project, when I click on a name, the popover pane is displaying at the wrong position. This issue can be s ...

Include data types when destructuring arrays within a loop

Is it possible to use type annotations in for...of loops in TypeScript? For example, like this for array destructuring: for(let [id, value]: [string, number] of Object.entries(some_object)) { } Or perhaps like this for object destructuring: for(let {a, b} ...

Try skipping ahead to the following spec file even if the initial spec has not been completed yet

I have encountered an issue when trying to execute two spec files for Angular.js. The problem arises when I attempt to run both files consecutively - the browser initially opens for angular.js, then switches to angularCal.js without executing angular.js ...

When trying to apply ::ng-deep to a mat-toggle in Angular, the attr binding does not seem to function

I'm looking to modify the 'content' property of a CSS class that is applied when using Angular's Slide-Toggle feature. Below is my SCSS code: :host .red { .mat-toggle { ::ng-deep .mat-slide-toggle-bar{ &::after{ ...

Updating content in Angular 2 using PUT method with the "uri/list" content type for multiple elements

What is the process for sending multiple elements in a PUT request using Angular 2 and Typescript? In order to send multiple URIs, they must be separated by a line break: curl -i -X PUT -H "Content-Type:text/uri-list" --data-binary @uris.txt http://loc ...

Is it possible to have unique styles for individual tabs in a mat-tab-group using Angular Material?

Is it possible to have different text colors for each tab in a mat-tab-group and change the color of the blue outline at the bottom? If so, can you provide guidance on how to achieve this? Here is the default appearance of the tabs in my app: https://i.st ...

This component is not compatible with JSX syntax and cannot be used as a JSX component. The type '() => Element' is not suitable for JSX element rendering

My Nextjs seems to be malfunctioning as I encountered the following error in a Parent component. Interestingly, the Spinner Component remains error-free Spinner.tsx export default function Spinner() { return ( <div className='flex ...