jhipster / generator-jhipster

JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
https://www.jhipster.tech
Apache License 2.0
21.45k stars 4.02k forks source link

Missing 'user.module.ts' file in micro frontend #19342

Closed paris0120 closed 2 years ago

paris0120 commented 2 years ago
Overview of the issue

I got the following errors with micro frontend. The 'user.module.ts' file is missing.

[INFO] container entry:./user[0] - Error: Module not found: Error: Can't resolve 'app/entities/user/user.module.ts' in '/home/paris/projects/tradingemulator/ms/trading' [INFO] [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 17.126 s [INFO] Finished at: 2022-08-04T14:11:27-04:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal com.github.eirslett:frontend-maven-plugin:1.12.1:npm (webapp build dev) on project trading: Failed to run task: 'npm run webapp:build' failed. org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1) -> [Help 1] [ERROR]

Motivation for or Use Case

Generate micro frontend

Reproduce the error

generate app with the following jdl profile then mvnspring-boot:run for the store app.

application {
  config {
    baseName gateway
    reactive true
    packageName com.okta.developer.gateway
    applicationType gateway
    authenticationType oauth2
    buildTool maven
    clientFramework react
    prodDatabaseType postgresql
    serviceDiscoveryType eureka
    testFrameworks [cypress]
    microfrontends [blog, store]
  }
}

application {
  config {
    baseName blog
    reactive true
    packageName com.okta.developer.blog
    applicationType microservice
    authenticationType oauth2
    buildTool maven
    databaseType neo4j
    devDatabaseType neo4j
    prodDatabaseType neo4j
    enableHibernateCache false
    serverPort 8081
    serviceDiscoveryType eureka
    testFrameworks [cypress]
  }
  entities Blog, Post, Tag
}

application {
  config {
    baseName store
    reactive true
    packageName com.okta.developer.store
    applicationType microservice
    clientFramework angularX,
    authenticationType oauth2,
    buildTool maven,  
    prodDatabaseType mysql,
    devDatabaseType mysql,
    reactive true,
    serviceDiscoveryType eureka,
    serverPort 8082 
  }
  entities Product
}

entity Blog {
  name String required minlength(3)
  handle String required minlength(2)
}

entity Post {
  title String required
  content TextBlob required
  date Instant required
}

entity Tag {
  name String required minlength(2)
}

entity Product {
  title String required
  price BigDecimal required min(0)
  image ImageBlob
  login String
}
Related issues
Suggest a Fix
JHipster Version(s)

7.9.2

JHipster configuration
Entity configuration(s) entityName.json files generated in the .jhipster directory
Browsers and Operating System
deepu105 commented 2 years ago

@paris0120 I see that you are opening many issues and thanks for your interest in helping the project. But could you please follow the issue template and provide more details like the configuration used and so on so that we dont wate time and there is better chance of issue resolution please

paris0120 commented 2 years ago

@paris0120 I see that you are opening many issues and thanks for your interest in helping the project. But could you please follow the issue template and provide more details like the configuration used and so on so that we dont wate time and there is better chance of issue resolution please

OK, updated.

mshima commented 2 years ago

blog application is missing clientFramework, so it's not a microfrontend. gateway is using react, store is using angular, mixing client frameworks is not supported.

paris0120 commented 2 years ago

blog application is missing clientFramework, so it's not a microfrontend. gateway is using react, store is using angular, mixing client frameworks is not supported.

Isn't this duplicated and confusing? If the microservice has to have the same client framework why they can have their own setting?

Also even gateway have the same framework as angular the problem still exists.

mshima commented 2 years ago

blog application is missing clientFramework, so it's not a microfrontend. gateway is using react, store is using angular, mixing client frameworks is not supported.

Isn't this duplicated and confusing? If the microservice has to have the same client framework why they can have their own setting?

Also even gateway have the same framework as angular the problem still exists.

See https://github.com/jhipster/generator-jhipster/issues/19340#issuecomment-1207203662

Please fix the jdl, so we can try to reproduce.

mraible commented 2 years ago

I was able to reproduce this today with the following command:

jhipster jdl reactive-mf --skip-install --client-framework angularX

After generating the apps, I started the gateway and logged in. I looked in the Entities menu and found there are no errors rendered. When you use the above command with react, the fact that the downstream microfrontends can't be loaded is communicated to the user.

I figured if I started the blog app, I'd at least be able to see its entities. After starting the blog and still seeing no entities, I saw the following error in my browser console.

Screen Shot 2022-08-06 at 13 20 40

Then, I tried to start the store app and saw this error.

container entry:./user[0] - Error: Module not found: Error: Can't resolve '
app/entities/user/user.module.ts' in '/Users/mraible/Downloads/mfa-ng/store'
mshima commented 2 years ago

The errors happens on microfrontend side (not gateway) with oauth2. User was wrongly been exposed by the microfrontend. Exposing entities are not required anymore.

paris0120 commented 2 years ago

been

For the case above user entity is not required but if user has relationship with some of the entitles user entity is still needed.

mraible commented 2 years ago

@mshima Are you saying that the JDL for reactive-mf.jdl is incorrect?

https://github.com/jhipster/jdl-samples/blob/main/reactive-mf.jdl

mshima commented 2 years ago

@mraible no, the PR https://github.com/jhipster/generator-jhipster/pull/19357 should fix the issue.

For the case above user entity is not required but if user has relationship with some of the entitles user entity is still needed.

I haven't tried, but it should work just fine, the menu and routes are exposed (and everything they depends on).

mraible commented 2 years ago

@mshima I tried your PR and it does fix this particular problem. However, I did notice that I'm unable to save a new blog entity. It seems like it works if I don't select a user, but no data is inserted into the database. If I do select a user, the entity is created in the database, but the user relationship is not saved.

Something might be missing from our Cypress tests since all e2e pass for the blog app.

mraible commented 2 years ago

I investigated and did some debugging as to why the user relationship is not getting saved properly. It appears that all the data is being sent properly from the UI, and the backend is receiving it. However, saving the blog entity with blog.user populated doesn't seem to be working. I do see the following in the logs.

2022-08-07T11:06:44.820-06:00  WARN 320 --- [o4jDriverIO-2-7] o.s.d.n.c.m.DefaultNeo4jIsNewStrategy    : 
Instances of class com.okta.developer.blog.domain.User with an assigned id will always be treated as new 
without version property! 

@atomfrede Do you think this could be caused by an upgrade to Neo4j?

mraible commented 2 years ago

I just tried my original example from this blog post and everything works as expected. The example is created with v7.0.1 and you can save a blog entity with or without a user and it all works.

mshima commented 2 years ago

@mraible weblux+neo4j is broken for almost 6 months. And were flaky before that. https://github.com/hipster-labs/jhipster-daily-builds/actions/workflows/neo4j.yaml?page=7 Do you think I should test user relationships using another database?

mraible commented 2 years ago

@mshima I tested with React and Vue. Both of them seem to let you add the user successfully.

Screen Shot 2022-08-08 at 17 24 56

However, neither one renders the user on the view or edit screens.

mraible commented 2 years ago

I tried a new JDL for my microfrontends architecture today, changing to no database in the gateway and PostgreSQL in the blog app. In doing so, I discovered that a database is required when using oauth2 on the gateway if you have microservices with relationships to user. If you use the JDL below, it'll generate just fine, but when you try to add a new blog, it'll 404 on the request for users from the gateway.

application {
  config {
    baseName gateway
    reactive true
    packageName com.okta.developer.gateway
    applicationType gateway
    authenticationType oauth2
    buildTool gradle
    clientFramework react
    databaseType no 
    serviceDiscoveryType eureka
    testFrameworks [cypress]
    microfrontends [blog, store]
  }
}

application {
  config {
    baseName blog
    reactive true
    packageName com.okta.developer.blog
    applicationType microservice
    authenticationType oauth2
    buildTool gradle
    clientFramework react
    prodDatabaseType postgresql    
    serverPort 8081
    serviceDiscoveryType eureka
    testFrameworks [cypress]
  }
  entities Blog, Post, Tag
}

application {
  config {
    baseName store
    reactive true
    packageName com.okta.developer.store
    applicationType microservice
    authenticationType oauth2
    buildTool gradle
    clientFramework react
    databaseType mongodb
    devDatabaseType mongodb
    prodDatabaseType mongodb
    enableHibernateCache false
    serverPort 8082
    serviceDiscoveryType eureka
    testFrameworks [cypress]
  }
  entities Product
}

entity Blog {
  name String required minlength(3)
  handle String required minlength(2)
}

entity Post {
  title String required
  content TextBlob required
  date Instant required
}

entity Tag {
  name String required minlength(2)
}

entity Product {
  title String required
  price BigDecimal required min(0)
  image ImageBlob
}

relationship ManyToOne {
  Blog{user(login)} to User
  Post{blog(name)} to Blog
}

relationship ManyToMany {
  Post{tag(name)} to Tag{post}
}

paginate Post, Tag with infinite-scroll
paginate Product with pagination

deployment {
  deploymentType docker-compose
  appsFolders [gateway, blog, store]
  dockerRepositoryName "mraible"
}

deployment {
  deploymentType kubernetes
  appsFolders [gateway, blog, store]
  clusteredDbApps [store]
  kubernetesNamespace demo
  serviceDiscoveryType eureka
  dockerRepositoryName "mraible"
}
mraible commented 2 years ago

I checked jhipster jdl reactive-ms (microservices, not microfrontends). It uses Vue for the frontend. Adding a new blog with a user works and renders the entity as expected in the list. However, when you try to view the entity, there's an error in the console, and nothing loads.

vue-router.esm.js:2316 TypeError: Cannot read properties of undefined (reading 'call')
    at __webpack_require__ (bootstrap:19:1)
    at ./src/main/webapp/app/entities/blog/blog/blog-details.vue (blog-details.vue:15:1)
    at Function.__webpack_require__ (bootstrap:19:1)
Screen Shot 2022-08-08 at 21 32 36

If you try to edit the blog, the form renders just fine, but the assigned user is not selected.

If I create the same stack with React:

jhipster jdl reactive-ms.jdl --client-framework react

The behavior is the same. It adds and renders fine in the list, but not in the view or update screens. There's also an error in the console when the list loads.

react-dom.development.js:86 Warning: Cannot update a component (`PrivateRoute`) while rendering a 
different component (`__WEBPACK_DEFAULT_EXPORT__`). To locate the bad setState() call inside
`__WEBPACK_DEFAULT_EXPORT__`, follow the stack trace as described in 
https://reactjs.org/link/setstate-in-render at __WEBPACK_DEFAULT_EXPORT__
mshima commented 2 years ago

@mraible user api is always using gateways api, it may not be synchronized. Not sure if https://github.com/jhipster/generator-jhipster/pull/19382 fixes user issues.

mraible commented 2 years ago

@mshima I tried https://github.com/jhipster/generator-jhipster/pull/19382 with the following command (after checking out the branch and running npm link):

jhipster jdl reactive-ms.jdl --client-framework react

I get an error when generating the apps:

   create src/main/docker/central-server-config/localhost-config/application.yml
ERROR! /Users/mraible/dev/generator-jhipster/generators/client/templates/react/src/main/webapp/app/shared/reducers/user-management.ts.ejs:30
    28| };
    29|
 >> 30| const apiUrl = '<%= entityApi %>api/users';
    31|
    32| // Async Actions
    33|

entityApi is not defined
ReferenceError: /Users/mraible/dev/generator-jhipster/generators/client/templates/react/src/main/webapp/app/shared/reducers/user-management.ts.ejs:30
    28| };
    29|
 >> 30| const apiUrl = '<%= entityApi %>api/users';
    31|
    32| // Async Actions
    33|

entityApi is not defined
    at JHipsterClientGenerator.eval ("/Users/mraible/dev/generator-jhipster/generators/client/templates/react/src/main/webapp/app/shared/reducers/user-management.ts.ejs":13:26)
    at user-management.ts (/Users/mraible/dev/generator-jhipster/node_modules/ejs/lib/ejs.js:703:17)
    at /Users/mraible/dev/generator-jhipster/node_modules/ejs/lib/ejs.js:260:40
    at new Promise (<anonymous>)
    at tryHandleCache (/Users/mraible/dev/generator-jhipster/node_modules/ejs/lib/ejs.js:258:14)
    at Object.exports.renderFile (/Users/mraible/dev/generator-jhipster/node_modules/ejs/lib/ejs.js:491:10)
    at EditionInterface.processFile (/Users/mraible/dev/generator-jhipster/node_modules/mem-fs-editor/lib/actions/copy-tpl-async.js:22:20)
    at EditionInterface.applyProcessingFileFunc (/Users/mraible/dev/generator-jhipster/node_modules/mem-fs-editor/lib/actions/copy-async.js:14:52)
    at EditionInterface.exports._copySingleAsync (/Users/mraible/dev/generator-jhipster/node_modules/mem-fs-editor/lib/actions/copy-async.js:94:50)
    at EditionInterface.exports.copyAsync (/Users/mraible/dev/generator-jhipster/node_modules/mem-fs-editor/lib/actions/copy-async.js:50:17)
    at async EditionInterface.module.exports [as copyTplAsync] (/Users/mraible/dev/generator-jhipster/node_modules/mem-fs-editor/lib/actions/copy-tpl-async.js:11:3)
    at async renderTemplate (/Users/mraible/dev/generator-jhipster/generators/generator-base.js:2507:11)
    at async Promise.all (index 40)
    at async JHipsterClientGenerator.writeFiles (/Users/mraible/dev/generator-jhipster/generators/generator-base.js:2627:19) {
  path: '/Users/mraible/dev/generator-jhipster/generators/client/templates/react/src/main/webapp/app/shared/reducers/user-management.ts.ejs'
mshima commented 2 years ago

@mraible I did some test with syncUserWithIdP. It's not compatible with microfrontends and microservices (gateways should work). The synchronization happens here https://github.com/jhipster/generator-jhipster/blob/9f478679d9f59ef891243399809479fd93772386/generators/server/templates/src/main/java/package/web/rest/AccountResource.java.ejs#L78

It's executed when the current account is fetched, so only a user that was logged at some time will show. I suggest to open a new bug report to discuss.

mshima commented 2 years ago

The AccountResource doesn't exists at microservices.

mraible commented 2 years ago

@mshima I think it's fine that it's not supported. However, we might want to catch it earlier. Currently, if you use JDL to create a gateway with oauth2 and databaseType: no, it will compile and generate everything just fine. However, if you have a downstream microservice with a relationship to User, the user lookup will fail. I'm not sure if we can do anything to validate that.

FWIW, I do plan to investigate using SCIM to synchronize users from identity providers in the future.

What about the compile error above? It happens when I use your skip_ci-api_url branch and jhipster jdl reactive-ms.

mshima commented 2 years ago

@mraible should be fixed at the PR, but it's useless without the backend support. Should we fail when a user relationship is found at Microservices?

mraible commented 2 years ago

@mshima I pulled from your branch and tried again. jhipster jdl reactive-ms --client-framework react still has strange behavior that didn't exist when I first wrote my tutorial with JHipster 7.0.1.

The original example uses Vue. When you assign a user to a blog it'll show in the list, view, and edit screens. Now, it only shows on the list screen.

I also tried microfrontends using jhipster jdl reactive-mf --client-framework react. It has the same behavior as reactive-ms.

I'll try with Angular and Vue.

mraible commented 2 years ago

Should we fail when a user relationship is found at Microservices?

No. It worked at one point so it seems there's a regression.

mshima commented 2 years ago

Ought I think the examples worked because you are using neo4j. At the form, the user is loaded using the gateway api. Neo4j cascades, so the user entity (which was loaded from the gateway) and the relationship are saved. At databases like postgresql, it would fail to save due to missing user from relationship.

mraible commented 2 years ago

Results for saving a blog user with microservices (reactive-ms) JDL:

One interesting thing to note is I left the data intact after running the Angular example. When I loaded it up with the Vue UI, it does render the user in the list. So it seems that Angular might have some errors in its list template. I'll investigate to see if I can find a fix.

mraible commented 2 years ago

I discovered the difference between Angular and Vue.

For Angular, the list screen issues a request to the backend with parameters.

http://localhost:9000/services/blog/api/blogs?eagerload=true&sort=id,asc

With Vue, there are no parameters. I tried changing blog.component.ts to not use the query object in the queryBackend() method.

return this.blogService.query().pipe(tap(() => (this.isLoading = false)));

This makes it so the user is rendered in Angular's list screen.

Screen Shot 2022-08-09 at 16 14 12

I also inspected the API calls for the view and edit screens. The data returned contains user: null so it seems it might be a problem on the backend.

mraible commented 2 years ago

I tried a different JDL, this time using Angular for the client and PostgreSQL for the blog.

When I try to save a blog with a user, there's a 500 error on the server.

2022-08-09T17:30:13.859-06:00 ERROR 2911 --- [ctor-http-nio-2] o.z.problem.spring.common.AdviceTraits   : Internal Server Error

org.springframework.dao.DataIntegrityViolationException: executeMany; SQL [INSERT INTO blog (name, handle, user_id) VALUES ($1, $2, $3)]; Referential integrity constraint violation: "FK_BLOG__USER_ID: PUBLIC.BLOG FOREIGN KEY(USER_ID) REFERENCES PUBLIC.JHI_USER(ID) ('4c973896-5761-41fc-8217-07c5d13a004b')"; SQL statement:
INSERT INTO blog (name, handle, user_id) VALUES ($1, $2, $3) [23506-200]; nested exception is io.r2dbc.spi.R2dbcDataIntegrityViolationException: [23506] [23506] Referential integrity constraint violation: "FK_BLOG__USER_ID: PUBLIC.BLOG FOREIGN KEY(USER_ID) REFERENCES PUBLIC.JHI_USER(ID) ('4c973896-5761-41fc-8217-07c5d13a004b')"; SQL statement:
INSERT INTO blog (name, handle, user_id) VALUES ($1, $2, $3) [23506-200]
        at org.springframework.r2dbc.connection.ConnectionFactoryUtils.convertR2dbcException(ConnectionFactoryUtils.java:229)
        Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Assembly trace from producer [reactor.core.publisher.MonoError]

My JDL:

application {
  config {
    baseName gateway
    reactive true
    packageName com.okta.developer.gateway
    applicationType gateway
    authenticationType oauth2
    buildTool gradle
    clientFramework angularX
    prodDatabaseType postgresql
    serviceDiscoveryType eureka
    testFrameworks [cypress]
  }
  entities Blog, Post, Tag, Product
}

application {
  config {
    baseName blog
    reactive true
    packageName com.okta.developer.blog
    applicationType microservice
    authenticationType oauth2
    buildTool gradle
    prodDatabaseType postgresql
    serverPort 8081
    serviceDiscoveryType eureka
  }
  entities Blog, Post, Tag
}

application {
  config {
    baseName store
    reactive true
    packageName com.okta.developer.store
    applicationType microservice
    authenticationType oauth2
    buildTool gradle
    databaseType mongodb
    devDatabaseType mongodb
    prodDatabaseType mongodb
    enableHibernateCache false
    serverPort 8082
    serviceDiscoveryType eureka
  }
  entities Product
}

entity Blog {
  name String required minlength(3)
  handle String required minlength(2)
}

entity Post {
  title String required
  content TextBlob required
  date Instant required
}

entity Tag {
  name String required minlength(2)
}

entity Product {
  title String required
  price BigDecimal required min(0)
  image ImageBlob
}

relationship ManyToOne {
  Blog{user(login)} to User
  Post{blog(name)} to Blog
}

relationship ManyToMany {
  Post{tag(name)} to Tag{post}
}

paginate Post, Tag with infinite-scroll
paginate Product with pagination

microservice Product with store
microservice Blog, Post, Tag with blog

deployment {
  deploymentType docker-compose
  appsFolders [gateway, blog, store]
  dockerRepositoryName "mraible"
}

I think this logic in BlogResource#createBlog() used to solve this, but it's not working anymore. Maybe because it's not subscribed to?

if (blog.getUser() != null) {
    // Save user in case it's new and only exists in gateway
    userRepository.save(blog.getUser());
}
mshima commented 2 years ago

@mraible implemented the syncUser logic at the /api/users api for Microservices at https://github.com/jhipster/generator-jhipster/pull/19384. User relationships are ok.

Generated using Postgres:

jhipster jdl reactive-mf.jdl --client-framework angularX --workspaces --monorepository --db postgresql --skip-jhipster-dependencies --build maven
npm run java:docker:arm64
npm run ci:e2e:prepare
mraible commented 2 years ago

@mshima The issue I identified in my last three comments have nothing to do with microfrontends. I know it's confusing since this issues is related to microfrontends. I was simply trying to get reactive-ms.jdl working, which is a microservices architecture with a monolith UI on the gateway. I tried the fix in https://github.com/jhipster/generator-jhipster/pull/19384 and the following command:

jhipster jdl reactive-ms.jdl --client-framework angularX

It has the same issue where it makes a call to /services/blog/api/blogs?eagerload=true&sort=id,asc on the list screen and no user is returned. If I change the URL to /services/blog/api/blogs, the user is returned.

I also tried with PostgreSQL (again, with microservices JDL, not microfrontends):

jhipster jdl reactive-ms.jdl --client-framework angularX --db postgresql 

This results in the blog app not starting with the following error:

2022-08-10T09:52:20.290-06:00 ERROR 17595 --- [  restartedMain] t.j.c.liquibase.AsyncSpringLiquibase     : 
Liquibase could not start correctly, your database is NOT ready: FATAL: role "blog" does not exist

org.postgresql.util.PSQLException: FATAL: role "blog" does not exist

Since the gateway is already using PostgreSQL, I'm guessing this is because I'm unable to start the blog's PostgreSQL Docker container.

➜  blog git:(main) jhpostgresqlup
WARN[0000] Found orphan containers ([docker-jhipster-registry-1 docker-keycloak-1 docker-gateway-postgresql-1 docker-blog-neo4j-1]) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
[+] Running 0/1
 ⠙ Container docker-blog-postgresql-1  Starting                                                                    0.1s
Error response from daemon: driver failed programming external connectivity on endpoint docker-blog-postgresql-1 (5f3f49f571e1a1c070da6849c6a0718db900643e2d701d14e2fe35b94e123a44): Bind for 127.0.0.1:5432 failed: port is already allocated
atomfrede commented 2 years ago

I investigated and did some debugging as to why the user relationship is not getting saved properly. It appears that all the data is being sent properly from the UI, and the backend is receiving it. However, saving the blog entity with blog.user populated doesn't seem to be working. I do see the following in the logs.

2022-08-07T11:06:44.820-06:00  WARN 320 --- [o4jDriverIO-2-7] o.s.d.n.c.m.DefaultNeo4jIsNewStrategy    : 
Instances of class com.okta.developer.blog.domain.User with an assigned id will always be treated as new 
without version property! 

@atomfrede Do you think this could be caused by an upgrade to Neo4j?

Not sure, but yes it might be related. It sounds like by default optimistic locking is active now

mshima commented 2 years ago

@mraible since you opened https://github.com/jhipster/generator-jhipster/issues/19399 to track relationships. https://github.com/jhipster/generator-jhipster/pull/19357 can be reviewed since it fixes this bug.

mraible commented 2 years ago

I can confirm that https://github.com/jhipster/generator-jhipster/pull/19357 fixes this bug and that Angular and React + microfrontends works without issues!

With Vue, I get errors when I create apps with jhipster jdl reactive-mf --client-framework vue and run all apps with Gradle.

Screen Shot 2022-08-14 at 23 25 52
mshima commented 2 years ago

@mraible the PR changes angular only.

mshima commented 2 years ago

With Vue, I get errors when I create apps with jhipster jdl reactive-mf --client-framework vue and run all apps with Gradle.

Screen Shot 2022-08-14 at 23 25 52

looks related to webpack. try removing build/webpack and recreating package-lock.json and node_modules.

mraible commented 2 years ago

@mshima I tried again this morning with the main branch and everything works as expected.

mshima commented 2 years ago

@DanielFran bounty claimed https://opencollective.com/generator-jhipster/expenses/90547.

DanielFran commented 2 years ago

@mshima approved