diff --git a/generators/angular/entity-files-angular.ts b/generators/angular/entity-files-angular.ts index 335d7984d2e1..9c629fdfb527 100644 --- a/generators/angular/entity-files-angular.ts +++ b/generators/angular/entity-files-angular.ts @@ -111,7 +111,7 @@ export const writeEntitiesFiles = asWritingEntitiesTask(async function ( }, }); - if (application.generateUserManagement) { + if (application.generateUserManagement && application.userManagement.skipClient) { await this.writeFiles({ sections: userManagementFiles, context: { diff --git a/generators/angular/templates/src/main/webapp/app/admin/admin.routes.ts.ejs b/generators/angular/templates/src/main/webapp/app/admin/admin.routes.ts.ejs index 6aa602efef46..7d7cb75e83fe 100644 --- a/generators/angular/templates/src/main/webapp/app/admin/admin.routes.ts.ejs +++ b/generators/angular/templates/src/main/webapp/app/admin/admin.routes.ts.ejs @@ -20,7 +20,7 @@ import { Routes } from '@angular/router'; /* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */ const routes: Routes = [ -<%_ if (generateUserManagement) { _%> +<%_ if (generateUserManagement && userManagement.skipClient) { _%> { path: 'user-management', loadChildren: () => import('./user-management/user-management.route'), diff --git a/generators/angular/templates/src/main/webapp/app/entities/_entityFolder_/service/_entityFile_.service.ts.ejs b/generators/angular/templates/src/main/webapp/app/entities/_entityFolder_/service/_entityFile_.service.ts.ejs index 8d18de50b906..da5d45ff66f2 100644 --- a/generators/angular/templates/src/main/webapp/app/entities/_entityFolder_/service/_entityFile_.service.ts.ejs +++ b/generators/angular/templates/src/main/webapp/app/entities/_entityFolder_/service/_entityFile_.service.ts.ejs @@ -24,16 +24,19 @@ if (paginationPagination || paginationInfiniteScroll) { _%> import { inject, Injectable } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable<%_ if (searchEngineAny) { _%>, asapScheduler, scheduled<%_ } _%> } from 'rxjs'; +import { +<%_ if (builtInUserManagement || anyFieldIsDateDerived) { _%> + map, +<%_ } _%> + Observable, +<%_ if (searchEngineAny) { _%> + asapScheduler, + scheduled, +<%_ } _%> +} from 'rxjs'; -<%_ if (searchEngineAny) { - if (anyFieldIsDateDerived) { _%> -import { catchError, map } from 'rxjs/operators'; -<% } else { %> +<%_ if (searchEngineAny) { _%> import { catchError } from 'rxjs/operators'; -<%_ } - } else if (anyFieldIsDateDerived) { _%> -import { map } from 'rxjs/operators'; <%_ } _%> <%_ if (anyFieldIsDateDerived) { _%> @@ -93,11 +96,15 @@ export class <%= entityAngularName %>Service { create(<%= entityInstance %>: New<%= entityAngularName %>): Observable { <%_ if (anyFieldIsDateDerived) { _%> const copy = this.convertDateFromClient(<%= entityInstance %>); + <%_ } _%> + <%_ if (builtInUserManagement) { _%> + const copy = this.convertUserManagementFromClient(<%= entityInstance %>); <%_ } _%> return this.http.post<<%= entityRestName %>>(this.resourceUrl, - <% if (anyFieldIsDateDerived) { %> copy <% } else { %> <%= entityInstance %> <% } %>, + <% if (anyFieldIsDateDerived || builtInUserManagement) { %> copy <% } else { %> <%= entityInstance %> <% } %>, { observe: 'response' }) - <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseFromServer(res)))<% } %>; + <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseFromServer(res)))<% } %> + <% if (builtInUserManagement) { %>.pipe(map(res => this.convertUserManagementResponseFromServer(res)))<% } %>; } <%_ } _%> <%_ if (!readOnly && updatableEntity) { _%> @@ -105,9 +112,12 @@ export class <%= entityAngularName %>Service { update(<%= entityInstance %>: I<%= entityAngularName %>): Observable { <%_ if (anyFieldIsDateDerived) { _%> const copy = this.convertDateFromClient(<%= entityInstance %>); + <%_ } _%> + <%_ if (builtInUserManagement) { _%> + const copy = this.convertUserManagementFromClient(<%= entityInstance %>); <%_ } _%> return this.http.put<<%= entityRestName %>>(`${this.resourceUrl}/${this.get<%= entityAngularName %>Identifier(<%= entityInstance %>)}`, - <% if (anyFieldIsDateDerived) { %> copy <% } else { %> <%= entityInstance %> <% } %>, + <% if (anyFieldIsDateDerived || builtInUserManagement) { %> copy <% } else { %> <%= entityInstance %> <% } %>, { observe: 'response' }) <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseFromServer(res)))<% } %>; } @@ -115,23 +125,29 @@ export class <%= entityAngularName %>Service { partialUpdate(<%= entityInstance %>: PartialUpdate<%= entityAngularName %>): Observable { <%_ if (anyFieldIsDateDerived) { _%> const copy = this.convertDateFromClient(<%= entityInstance %>); + <%_ } _%> + <%_ if (builtInUserManagement) { _%> + const copy = this.convertUserManagementFromClient(<%= entityInstance %>); <%_ } _%> return this.http.patch<<%= entityRestName %>>(`${this.resourceUrl}/${this.get<%= entityAngularName %>Identifier(<%= entityInstance %>)}`, - <% if (anyFieldIsDateDerived) { %> copy <% } else { %> <%= entityInstance %> <% } %>, + <% if (anyFieldIsDateDerived || builtInUserManagement) { %> copy <% } else { %> <%= entityInstance %> <% } %>, { observe: 'response' }) - <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseFromServer(res)))<% } %>; + <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseFromServer(res)))<% } %> + <% if (builtInUserManagement) { %>.pipe(map(res => this.convertUserManagementResponseFromServer(res)))<% } %>; } <%_ } _%> find(id: <%= tsKeyType %>): Observable { return this.http.get<<%= entityRestName %>>(`${this.resourceUrl}/${id}`, { observe: 'response' }) - <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseFromServer(res)))<% } %>; + <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseFromServer(res)))<% } %> + <% if (builtInUserManagement) { %>.pipe(map(res => this.convertUserManagementResponseFromServer(res)))<% } %>; } query(req?: any): Observable { const options = createRequestOption(req); return this.http.get<<%= entityRestName %>[]>(this.resourceUrl, { params: options, observe: 'response' }) - <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseArrayFromServer(res)))<% } %>; + <% if (anyFieldIsDateDerived) { %>.pipe(map(res => this.convertResponseArrayFromServer(res)))<% } %> + <% if (builtInUserManagement) { %>.pipe(map(res => this.convertUserManagementResponseArrayFromServer(res)))<% } %>; } <%_ if (!readOnly) { _%> @@ -144,7 +160,11 @@ export class <%= entityAngularName %>Service { search(req: <%= searchType %>): Observable { const options = createRequestOption(req); return this.http.get<<%= entityRestName %>[]>(this.resourceSearchUrl, { params: options, observe: 'response' }) - .pipe(<% if (anyFieldIsDateDerived) { %>map(res => this.convertResponseArrayFromServer(res)), <% } %>catchError(() => scheduled([new HttpResponse[]>()], asapScheduler))); + .pipe( + <% if (anyFieldIsDateDerived) { %>map(res => this.convertResponseArrayFromServer(res)), <% } %> + <% if (builtInUserManagement) { %>.pipe(map(res => this.convertUserManagementResponseArrayFromServer(res)))<% } %> + catchError(() => scheduled([new HttpResponse[]>()], asapScheduler)), + ); } <%_ } _%> @@ -209,4 +229,32 @@ export class <%= entityAngularName %>Service { }); } <%_ } _%> +<%_ if (builtInUserManagement) { _%> + + protected convertUserManagementFromClient<% if (!readOnly) { %> | New<%= entityAngularName %><% } %><% if (!readOnly && updatableEntity) { %> | PartialUpdate<%= entityAngularName %><% } %>>(<%= entityInstance %>: T): any { + return { + ...<%= entityInstance %>, + authorities: <%= entityInstance %>.authorities?.map(authority => authority.name), + }; + } + + protected convertUserManagementFromServer(rest<%= entityAngularName %>: <%= entityRestName %>): I<%= entityAngularName %> { + return { + ...rest<%= entityAngularName %>, + authorities: rest<%= entityAngularName %>.authorities?.map(authority => ({ name: authority as unknown as string })), + } + } + + protected convertUserManagementResponseFromServer(res: HttpResponse<<%= entityRestName %>>): HttpResponse> { + return res.clone({ + body: res.body ? this.convertUserManagementFromServer(res.body) : null, + }); + } + + protected convertUserManagementResponseArrayFromServer(res: HttpResponse<<%= entityRestName %>[]>): HttpResponse[]> { + return res.clone({ + body: res.body ? res.body.map(item => this.convertUserManagementFromServer(item)) : null, + }); + } +<%_ } _%> } diff --git a/generators/angular/templates/src/main/webapp/app/layouts/navbar/navbar.component.html.ejs b/generators/angular/templates/src/main/webapp/app/layouts/navbar/navbar.component.html.ejs index e6c558341cd1..0dbf4a26a99a 100644 --- a/generators/angular/templates/src/main/webapp/app/layouts/navbar/navbar.component.html.ejs +++ b/generators/angular/templates/src/main/webapp/app/layouts/navbar/navbar.component.html.ejs @@ -116,7 +116,7 @@ <%_ } _%> -<%_ if (generateUserManagement) { _%> +<%_ if (generateUserManagement && userManagement.skipClient) { _%>
  • diff --git a/generators/app/__snapshots__/generator.spec.ts.snap b/generators/app/__snapshots__/generator.spec.ts.snap index 7c88493a8d0c..d0c8d5854eb4 100644 --- a/generators/app/__snapshots__/generator.spec.ts.snap +++ b/generators/app/__snapshots__/generator.spec.ts.snap @@ -749,6 +749,7 @@ exports[`generator - app with default config should match snapshot 1`] = ` "upperFirstCamelCaseBaseName": "Jhipster", "useNpmWrapper": true, "user": Any, + "userManagement": Any, "webappEnumerationsDir": "src/main/webapp/app/entities/enumerations/", "webappLoginRegExp": "^[a-zA-Z0-9!$&*+=?^_\`{|}~.-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*$|^[_.@A-Za-z0-9-]+$", "websocket": "no", @@ -1322,6 +1323,7 @@ exports[`generator - app with gateway should match snapshot 1`] = ` "upperFirstCamelCaseBaseName": "Jhipster", "useNpmWrapper": true, "user": Any, + "userManagement": Any, "webappEnumerationsDir": "src/main/webapp/app/entities/enumerations/", "webappLoginRegExp": "^[a-zA-Z0-9!$&*+=?^_\`{|}~.-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*$|^[_.@A-Za-z0-9-]+$", "websocket": "no", diff --git a/generators/app/generator.spec.ts b/generators/app/generator.spec.ts index b33a4d3ac856..22e9ec605046 100644 --- a/generators/app/generator.spec.ts +++ b/generators/app/generator.spec.ts @@ -55,6 +55,7 @@ describe(`generator - ${generator}`, () => { expect(runResult.generator.sharedData.getApplication()).toMatchSnapshot({ user: expect.any(Object), authority: expect.any(Object), + userManagement: expect.any(Object), jhipsterPackageJson: expect.any(Object), }); }); @@ -75,6 +76,7 @@ describe(`generator - ${generator}`, () => { expect(runResult.generator.sharedData.getApplication()).toMatchSnapshot({ user: expect.any(Object), authority: expect.any(Object), + userManagement: expect.any(Object), jhipsterPackageJson: expect.any(Object), jwtSecretKey: expect.any(String), }); diff --git a/generators/base-application/generator.spec.ts b/generators/base-application/generator.spec.ts index 6bdaa1c48d4d..63b986d17ace 100644 --- a/generators/base-application/generator.spec.ts +++ b/generators/base-application/generator.spec.ts @@ -233,7 +233,7 @@ describe(`generator - ${generator}`, () => { const entitiesArg = { ...controlArg, ...applicationArg, - entities: [expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object)], + entities: [expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object)], }; expect(initializing).toBeCalledWith(controlArg); @@ -248,35 +248,39 @@ describe(`generator - ${generator}`, () => { expect(configuringEachEntity).toHaveBeenNthCalledWith(2, { ...entityConfiguringArg, entityName: 'Two' }); expect(configuringEachEntity).toHaveBeenNthCalledWith(3, { ...entityConfiguringArg, entityName: 'Three' }); - expect(preparingEachEntity).toBeCalledTimes(5); + expect(preparingEachEntity).toBeCalledTimes(6); expect(preparingEachEntity).toHaveBeenNthCalledWith(1, { ...entityArg, entityName: 'User' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'Authority' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'One' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'Two' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Three' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'UserManagement' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'Authority' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'One' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Two' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(6, { ...entityArg, entityName: 'Three' }); - expect(preparingEachEntityField).toBeCalledTimes(9); + expect(preparingEachEntityField).toBeCalledTimes(21); expect(preparingEachEntityField).toHaveBeenNthCalledWith(1, { ...fieldArg, description: 'User#id' }); expect(preparingEachEntityField).toHaveBeenNthCalledWith(2, { ...fieldArg, description: 'User#login' }); expect(preparingEachEntityField).toHaveBeenNthCalledWith(3, { ...fieldArg, description: 'User#firstName' }); expect(preparingEachEntityField).toHaveBeenNthCalledWith(4, { ...fieldArg, description: 'User#lastName' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(5, { ...fieldArg, description: 'Authority#name' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(6, { ...fieldArg, description: 'One#id' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(7, { ...fieldArg, description: 'Two#id' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(8, { ...fieldArg, description: 'Two#name' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(9, { ...fieldArg, description: 'Three#id' }); - - expect(preparingEachEntityRelationship).toBeCalledTimes(3); - expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(1, { ...relationshipArg, description: 'One#two' }); - expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(2, { ...relationshipArg, description: 'Two#one' }); - expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(3, { ...relationshipArg, description: 'Two#three' }); - - expect(postPreparingEachEntity).toBeCalledTimes(5); + // Ommit UserManagement fields + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 4, { ...fieldArg, description: 'Authority#name' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 3, { ...fieldArg, description: 'One#id' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 2, { ...fieldArg, description: 'Two#id' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 1, { ...fieldArg, description: 'Two#name' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21, { ...fieldArg, description: 'Three#id' }); + + expect(preparingEachEntityRelationship).toBeCalledTimes(4); + // Ommit UserManagement relationships + expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(2, { ...relationshipArg, description: 'One#two' }); + expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(3, { ...relationshipArg, description: 'Two#one' }); + expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(4, { ...relationshipArg, description: 'Two#three' }); + + expect(postPreparingEachEntity).toBeCalledTimes(6); expect(postPreparingEachEntity).toHaveBeenNthCalledWith(1, { ...entityArg, entityName: 'User' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'Authority' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'One' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'Two' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Three' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'UserManagement' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'Authority' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'One' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Two' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(6, { ...entityArg, entityName: 'Three' }); expect(defaultTask).toBeCalledWith(entitiesArg); expect(writingEntities).toBeCalledWith(entitiesArg); @@ -471,7 +475,7 @@ describe(`generator - ${generator}`, () => { const entitiesArg = { ...applicationArg, - entities: [expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object)], + entities: [expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object), expect.any(Object)], }; const writingEntitiesArg = { @@ -495,35 +499,39 @@ describe(`generator - ${generator}`, () => { expect(configuringEachEntity).toHaveBeenNthCalledWith(2, { ...entityConfiguringArg, entityName: 'Two' }); expect(configuringEachEntity).toHaveBeenNthCalledWith(3, { ...entityConfiguringArg, entityName: 'Three' }); - expect(preparingEachEntity).toBeCalledTimes(5); + expect(preparingEachEntity).toBeCalledTimes(6); expect(preparingEachEntity).toHaveBeenNthCalledWith(1, { ...entityArg, entityName: 'User' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'Authority' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'One' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'Two' }); - expect(preparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Three' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'UserManagement' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'Authority' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'One' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Two' }); + expect(preparingEachEntity).toHaveBeenNthCalledWith(6, { ...entityArg, entityName: 'Three' }); - expect(preparingEachEntityField).toBeCalledTimes(9); + expect(preparingEachEntityField).toBeCalledTimes(21); expect(preparingEachEntityField).toHaveBeenNthCalledWith(1, { ...fieldArg, description: 'User#id' }); expect(preparingEachEntityField).toHaveBeenNthCalledWith(2, { ...fieldArg, description: 'User#login' }); expect(preparingEachEntityField).toHaveBeenNthCalledWith(3, { ...fieldArg, description: 'User#firstName' }); expect(preparingEachEntityField).toHaveBeenNthCalledWith(4, { ...fieldArg, description: 'User#lastName' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(5, { ...fieldArg, description: 'Authority#name' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(6, { ...fieldArg, description: 'One#id' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(7, { ...fieldArg, description: 'Two#id' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(8, { ...fieldArg, description: 'Two#name' }); - expect(preparingEachEntityField).toHaveBeenNthCalledWith(9, { ...fieldArg, description: 'Three#id' }); - - expect(preparingEachEntityRelationship).toBeCalledTimes(3); - expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(1, { ...relationshipArg, description: 'One#two' }); - expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(2, { ...relationshipArg, description: 'Two#one' }); - expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(3, { ...relationshipArg, description: 'Two#three' }); - - expect(postPreparingEachEntity).toBeCalledTimes(5); + // Ommit UserManagement fields + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 4, { ...fieldArg, description: 'Authority#name' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 3, { ...fieldArg, description: 'One#id' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 2, { ...fieldArg, description: 'Two#id' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21 - 1, { ...fieldArg, description: 'Two#name' }); + expect(preparingEachEntityField).toHaveBeenNthCalledWith(21, { ...fieldArg, description: 'Three#id' }); + + expect(preparingEachEntityRelationship).toBeCalledTimes(4); + // Ommit UserManagement relationships + expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(2, { ...relationshipArg, description: 'One#two' }); + expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(3, { ...relationshipArg, description: 'Two#one' }); + expect(preparingEachEntityRelationship).toHaveBeenNthCalledWith(4, { ...relationshipArg, description: 'Two#three' }); + + expect(postPreparingEachEntity).toBeCalledTimes(6); expect(postPreparingEachEntity).toHaveBeenNthCalledWith(1, { ...entityArg, entityName: 'User' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'Authority' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'One' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'Two' }); - expect(postPreparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Three' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(2, { ...entityArg, entityName: 'UserManagement' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(3, { ...entityArg, entityName: 'Authority' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(4, { ...entityArg, entityName: 'One' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(5, { ...entityArg, entityName: 'Two' }); + expect(postPreparingEachEntity).toHaveBeenNthCalledWith(6, { ...entityArg, entityName: 'Three' }); expect(defaultTask).toBeCalledWith(entitiesArg); diff --git a/generators/base-application/support/prepare-entity.ts b/generators/base-application/support/prepare-entity.ts index 1a8b4d9a0d9c..2f12f161c6a8 100644 --- a/generators/base-application/support/prepare-entity.ts +++ b/generators/base-application/support/prepare-entity.ts @@ -77,6 +77,7 @@ const BASE_TEMPLATE_DATA = { entityAuthority: undefined, entityReadAuthority: undefined, adminEntity: undefined, + builtInUserManagement: undefined, requiresPersistableImplementation: false, updatableEntity: undefined, @@ -209,8 +210,11 @@ export default function prepareEntity(entityWithConfig, generator, application) }); mutateData(entityWithConfig, { - entityFileName: data => - data.entityFileName ?? kebabCase(entityWithConfig.entityNameCapitalized + upperFirst(entityWithConfig.entityAngularJSSuffix)), + __override__: false, + entityFileName: data => kebabCase(data.entityNameCapitalized + upperFirst(data.entityAngularJSSuffix)), + entityAngularName: data => data.entityClass + upperFirstCamelCase(entityWithConfig.entityAngularJSSuffix), + entityAngularNamePlural: data => pluralize(data.entityAngularName), + entityApiUrl: data => data.entityNamePluralizedAndSpinalCased, }); entityWithConfig.entityFolderName = entityWithConfig.clientRootFolder ? `${entityWithConfig.clientRootFolder}/${entityWithConfig.entityFileName}` @@ -220,11 +224,8 @@ export default function prepareEntity(entityWithConfig, generator, application) entityWithConfig.entityPluralFileName = entityWithConfig.entityNamePluralizedAndSpinalCased + entityWithConfig.entityAngularJSSuffix; entityWithConfig.entityServiceFileName = entityWithConfig.entityFileName; - entityWithConfig.entityAngularName = entityWithConfig.entityClass + upperFirstCamelCase(entityWithConfig.entityAngularJSSuffix); - entityWithConfig.entityAngularNamePlural = pluralize(entityWithConfig.entityAngularName); entityWithConfig.entityReactName = entityWithConfig.entityClass + upperFirstCamelCase(entityWithConfig.entityAngularJSSuffix); - entityWithConfig.entityApiUrl = entityWithConfig.entityNamePluralizedAndSpinalCased; entityWithConfig.entityStateName = kebabCase(entityWithConfig.entityAngularName); entityWithConfig.entityUrl = entityWithConfig.entityStateName; @@ -248,9 +249,10 @@ export default function prepareEntity(entityWithConfig, generator, application) const { microserviceName, entityFileName, microfrontend } = entityWithConfig; entityWithConfig.entityApi = microserviceName ? `services/${microserviceName.toLowerCase()}/` : ''; entityWithConfig.entityPage = - microfrontend && microserviceName && entityWithConfig.applicationType === MICROSERVICE + entityWithConfig.entityPage ?? + (microfrontend && microserviceName && entityWithConfig.applicationType === MICROSERVICE ? `${microserviceName.toLowerCase()}/${entityFileName}` - : `${entityFileName}`; + : `${entityFileName}`); const hasBuiltInUserField = entityWithConfig.relationships.some(relationship => relationship.otherEntity.builtInUser); entityWithConfig.saveUserSnapshot = diff --git a/generators/base-application/support/prepare-relationship.js b/generators/base-application/support/prepare-relationship.js index 492fe5cbcab5..04e83ec5d245 100644 --- a/generators/base-application/support/prepare-relationship.js +++ b/generators/base-application/support/prepare-relationship.js @@ -90,6 +90,7 @@ export default function prepareRelationship(entityWithConfig, relationship, gene }); } else if ( !ignoreMissingRequiredRelationship && + !relationship.relationshipIgnoreBackReference && entityWithConfig.databaseType !== NEO4J && entityWithConfig.databaseType !== DATABASE_NO && (relationship.relationshipType === 'one-to-many' || relationship.ownerSide === false) diff --git a/generators/base-application/types.d.ts b/generators/base-application/types.d.ts index c5753e191fe5..e507cca66c5c 100644 --- a/generators/base-application/types.d.ts +++ b/generators/base-application/types.d.ts @@ -56,13 +56,16 @@ type ApplicationType = DeterministicOptionWithDerivedProperties< type UserManagement = | { skipUserManagement: true; + generateUserManagement: false; generateBuiltInUserEntity?: false; generateBuiltInAuthorityEntity: false; } | { skipUserManagement: false; generateBuiltInUserEntity?: boolean; + generateUserManagement: true; user: any; + userManagement: any; generateBuiltInAuthorityEntity: boolean; authority: any; }; @@ -76,6 +79,7 @@ type Oauth2Application = { generateBuiltInUserEntity?: boolean; user: any; generateBuiltInAuthorityEntity: false; + generateUserManagement: false; }; type SessionApplication = UserManagement & { diff --git a/generators/base/support/config.ts b/generators/base/support/config.ts index e695f70f57b9..ce4954a7aaad 100644 --- a/generators/base/support/config.ts +++ b/generators/base/support/config.ts @@ -74,11 +74,22 @@ export const pickFields = (source: Record, fields: (string * { prop: ({ prop }) => prop + '-bar', prop2: 'won\'t override' }, * ); */ -export const mutateData = (context: Record, ...mutations: Record[]) => { +export const mutateData = ( + context: Record, + ...mutations: Array< + Record & { + /** Set to false if you don't want functions to override the value */ + __override__?: boolean; + } + > +) => { for (const mutation of mutations) { - for (const [key, value] of Object.entries(mutation)) { + const override = mutation.__override__; + for (const [key, value] of Object.entries(mutation).filter(([key]) => key !== '__override__')) { if (typeof value === 'function') { - context[key] = value(context); + if (override !== false || context[key] === undefined) { + context[key] = value(context); + } } else if (context[key] === undefined) { context[key] = value; } diff --git a/generators/bootstrap-application-base/generator.ts b/generators/bootstrap-application-base/generator.ts index 657537e2d638..78b1fc4ad7b5 100644 --- a/generators/bootstrap-application-base/generator.ts +++ b/generators/bootstrap-application-base/generator.ts @@ -33,7 +33,7 @@ import { prepareField as prepareFieldForTemplates, prepareRelationship, } from '../base-application/support/index.js'; -import { createAuthorityEntity, createUserEntity } from './utils.js'; +import { createAuthorityEntity, createUserEntity, createUserManagementEntity } from './utils.js'; import { JAVA_DOCKER_DIR } from '../generator-constants.js'; import { GENERATOR_BOOTSTRAP, GENERATOR_BOOTSTRAP_APPLICATION_BASE, GENERATOR_COMMON, GENERATOR_PROJECT_NAME } from '../generator-list.js'; import { packageJson } from '../../lib/index.js'; @@ -233,6 +233,24 @@ export default class BootstrapApplicationBase extends BaseApplicationGenerator { application.user = user; } }, + loadUserManagement({ application, entitiesToLoad }) { + if (application.generateBuiltInUserEntity && application.generateUserManagement) { + if (this.sharedData.hasEntity('UserManagement')) { + throw new Error("Fail to bootstrap 'User', already exists."); + } + + const customUserManagement = entitiesToLoad.find(entityToLoad => entityToLoad.entityName === 'UserManagement'); + const customUserManagementData: any = customUserManagement?.entityStorage.getAll() ?? {}; + + const userManagement = createUserManagementEntity.call( + this, + { ...customUserManagementData, ...customUserManagementData.annotations }, + application, + ); + this.sharedData.setEntity('UserManagement', userManagement); + application.userManagement = userManagement; + } + }, loadAuthority({ application, entitiesToLoad }) { if (application.generateBuiltInAuthorityEntity) { const authority = 'Authority'; diff --git a/generators/bootstrap-application-base/utils.js b/generators/bootstrap-application-base/utils.js index d54dae22e664..97c271e4853b 100644 --- a/generators/bootstrap-application-base/utils.js +++ b/generators/bootstrap-application-base/utils.js @@ -19,13 +19,54 @@ import * as _ from 'lodash-es'; import { Validations, authenticationTypes, databaseTypes, fieldTypes } from '../../jdl/jhipster/index.js'; import { loadRequiredConfigIntoEntity } from '../base-application/support/index.js'; +import { PaginationTypes } from '../../jdl/jhipster/entity-options.js'; import { LOGIN_REGEX, LOGIN_REGEX_JS } from '../generator-constants.js'; +import { getDatabaseTypeData } from '../server/support/database.js'; const { CASSANDRA } = databaseTypes; const { OAUTH2 } = authenticationTypes; const { CommonDBTypes } = fieldTypes; -const { STRING: TYPE_STRING } = CommonDBTypes; +const { STRING: TYPE_STRING, BOOLEAN: TYPE_BOOLEAN, INSTANT } = CommonDBTypes; + +export const auditableEntityFields = () => [ + { + fieldName: 'createdBy', + fieldType: TYPE_STRING, + readonly: true, + skipServer: true, + builtIn: true, + fieldValidateRules: [Validations.MAXLENGTH], + fieldValidateRulesMaxlength: 50, + autoGenerate: true, + }, + { + fieldName: 'createdDate', + fieldType: INSTANT, + readonly: true, + skipServer: true, + builtIn: true, + autoGenerate: true, + }, + { + fieldName: 'lastModifiedBy', + fieldType: TYPE_STRING, + readonly: true, + skipServer: true, + builtIn: true, + fieldValidateRules: [Validations.MAXLENGTH], + fieldValidateRulesMaxlength: 50, + autoGenerate: true, + }, + { + fieldName: 'lastModifiedDate', + fieldType: INSTANT, + readonly: true, + skipServer: true, + builtIn: true, + autoGenerate: true, + }, +]; const authorityEntityName = 'Authority'; @@ -41,10 +82,12 @@ export function createUserEntity(customUserData = {}, application) { } } + const cassandraOrNoDatabase = application.databaseTypeNo || application.databaseTypeCassandra; // Create entity definition for built-in entity to make easier to deal with relationships. const user = { name: 'User', builtIn: true, + changelogDate: '00000000000100', entityTableName: `${application.jhiTablePrefix}_user`, relationships: [], fields: userEntityDefinition ? userEntityDefinition.fields || [] : [], @@ -57,7 +100,10 @@ export function createUserEntity(customUserData = {}, application) { entityPersistenceLayer: false, entityRestLayer: false, entitySearchLayer: false, - hasImageField: !application.databaseTypeNo && !application.databaseTypeCassandra, + hasImageField: !cassandraOrNoDatabase, + pagination: cassandraOrNoDatabase ? PaginationTypes.NO : PaginationTypes.PAGINATION, + auditableEntity: !cassandraOrNoDatabase, + i18nKeyPrefix: 'userManagement', ...customUserData, }; @@ -77,14 +123,15 @@ export function createUserEntity(customUserData = {}, application) { fieldValidateRulesMaxlength, fieldTranslationKey: 'global.field.id', fieldNameHumanized: 'ID', + readonly: true, id: true, builtIn: true, }, { fieldName: 'login', fieldType: TYPE_STRING, - fieldValidateRules: [Validations.REQUIRED, Validations.MAX, Validations.PATTERN], - fieldValidateRulesMax: 50, + fieldValidateRules: [Validations.REQUIRED, Validations.UNIQUE, Validations.MAXLENGTH, Validations.PATTERN], + fieldValidateRulesMaxlength: 50, fieldValidateRulesPattern: LOGIN_REGEX_JS, fieldValidateRulesPatternJava: LOGIN_REGEX, builtIn: true, @@ -92,18 +139,103 @@ export function createUserEntity(customUserData = {}, application) { { fieldName: 'firstName', fieldType: TYPE_STRING, + fieldValidateRules: [Validations.MAXLENGTH], + fieldValidateRulesMaxlength: 50, builtIn: true, }, { fieldName: 'lastName', fieldType: TYPE_STRING, + fieldValidateRules: [Validations.MAXLENGTH], + fieldValidateRulesMaxlength: 50, + builtIn: true, + }, + { + fieldName: 'email', + fieldType: TYPE_STRING, + fieldValidateRules: [Validations.REQUIRED, Validations.UNIQUE, Validations.MAXLENGTH, Validations.MINLENGTH], + fieldValidateRulesMinlength: 5, + fieldValidateRulesMaxlength: 191, builtIn: true, }, + ...(user.hasImageField + ? [ + { + fieldName: 'imageUrl', + fieldType: TYPE_STRING, + fieldValidateRules: [Validations.MAXLENGTH], + fieldValidateRulesMaxlength: 256, + builtIn: true, + }, + ] + : []), + { + fieldName: 'activated', + fieldType: TYPE_BOOLEAN, + builtIn: true, + autoGenerate: true, + }, + ...(application.enableTranslation + ? [ + { + fieldName: 'langKey', + fieldType: TYPE_STRING, + fieldValidateRules: [Validations.MAXLENGTH], + fieldValidateRulesMaxlength: 10, + builtIn: true, + }, + ] + : []), ]); return user; } +export function createUserManagementEntity(customUserManagementData = {}, application) { + const user = createUserEntity.call(this, {}, application); + for (const field of user.fields) { + // Login is used as the id field in rest api. + if (field.fieldName === 'login') { + field.id = true; + } else if (field.fieldName === 'id') { + field.id = false; + field.fieldValidateRules = [Validations.REQUIRED]; + // Set id type fallback since it's not id anymore and will not be calculated. + field.fieldType = field.fieldType ?? getDatabaseTypeData(application.databaseType).defaultPrimaryKeyType; + } + } + + const userManagement = { + ...user, + name: 'UserManagement', + skipClient: true, + skipServer: true, + changelogDate: '00000000000150', + clientRootFolder: 'admin', + entityAngularName: 'UserManagement', + entityApiUrl: 'admin/users', + entityFileName: 'user-management', + entityPage: 'user-management', + ...customUserManagementData, + adminEntity: true, + builtInUser: false, + builtInUserManagement: true, + }; + + if (application.generateBuiltInAuthorityEntity) { + addOrExtendRelationships(userManagement.relationships, [ + { + otherEntityName: 'Authority', + relationshipName: 'authority', + relationshipType: 'many-to-many', + relationshipIgnoreBackReference: true, + }, + ]); + } + + return userManagement; +} + export function createAuthorityEntity(customAuthorityData = {}, application) { const entityDefinition = this.getEntityConfig(authorityEntityName)?.getAll(); if (entityDefinition) { @@ -121,6 +253,7 @@ export function createAuthorityEntity(customAuthorityData = {}, application) { entitySuffix: '', clientRootFolder: 'admin', builtIn: true, + changelogDate: '00000000000200', adminEntity: true, entityTableName: `${application.jhiTablePrefix}_authority`, relationships: [], @@ -173,3 +306,17 @@ function addOrExtendFields(fields, fieldsToAdd) { } } } + +function addOrExtendRelationships(relationships, relationshipsToAdd) { + relationshipsToAdd = [].concat(relationshipsToAdd); + for (const relationshipToAdd of relationshipsToAdd) { + const { relationshipName: newrelationshipName } = relationshipToAdd; + let relationship = relationships.find(relationship => relationship.relationshipName === newrelationshipName); + if (!relationship) { + relationship = { ...relationshipToAdd }; + relationships.push(relationship); + } else { + _.defaults(relationship, relationshipToAdd); + } + } +} diff --git a/generators/bootstrap-application/generator.spec.ts b/generators/bootstrap-application/generator.spec.ts index 09394cb92e04..fa434aa846cb 100644 --- a/generators/bootstrap-application/generator.spec.ts +++ b/generators/bootstrap-application/generator.spec.ts @@ -46,10 +46,6 @@ const expectedField = () => ({ reference: expect.any(Object), }); -const expectedRelationship = () => ({ - otherEntity: expect.any(Object), -}); - const expectedPrimaryKeyId = () => ({ field: expect.any(Object), }); @@ -78,7 +74,7 @@ const expectedEntity = entity => ({ otherDtoReferences: expect.any(Array), fields: entity.fields.map(expectedField), - relationships: entity.relationships.map(expectedRelationship), + relationships: expect.any(Array), primaryKey: expectedPrimaryKey(entity.primaryKey), reactiveOtherEntities: expect.any(Set), reactiveUniqueEntityTypes: expect.any(Set), @@ -163,6 +159,7 @@ describe(`generator - ${generator}`, () => { expect(Object.keys(runResult.generator.sharedData.getEntitiesMap())).toMatchInlineSnapshot(` [ "User", + "UserManagement", "Authority", "EntityA", ] @@ -195,10 +192,12 @@ describe(`generator - ${generator}`, () => { "anyFieldIsZonedDateTime": false, "anyPropertyHasValidation": true, "applicationType": "monolith", + "auditableEntity": true, "authenticationType": "jwt", "baseName": "jhipster", "builtIn": true, "builtInUser": true, + "builtInUserManagement": undefined, "changelogDate": "20220129025420", "changelogDateForRecent": 2022-01-29T02:54:20.000Z, "clientFramework": "angular", @@ -357,7 +356,7 @@ describe(`generator - ${generator}`, () => { "blobContentTypeText": false, "builtIn": true, "columnName": "login", - "columnType": "varchar(255)", + "columnType": "varchar(50)", "createRandexp": Any, "entity": Any, "fieldInJavaBeanMethod": "Login", @@ -367,7 +366,7 @@ describe(`generator - ${generator}`, () => { "fieldNameCapitalized": "Login", "fieldNameHumanized": "Login", "fieldNameUnderscored": "login", - "fieldTranslationKey": "jhipsterApp.user.login", + "fieldTranslationKey": "userManagement.login", "fieldType": "String", "fieldTypeAnyBlob": false, "fieldTypeBigDecimal": false, @@ -395,23 +394,24 @@ describe(`generator - ${generator}`, () => { "fieldValidate": true, "fieldValidateRules": [ "required", - "max", + "unique", + "maxlength", "pattern", ], - "fieldValidateRulesMax": 50, + "fieldValidateRulesMaxlength": 50, "fieldValidateRulesPattern": "^[a-zA-Z0-9!$&*+=?^_\`{|}~.-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*$|^[_.@A-Za-z0-9-]+$", "fieldValidateRulesPatternAngular": "^[a-zA-Z0-9!$&*+=?^_\`{|}~.-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*$|^[_.@A-Za-z0-9-]+$", "fieldValidateRulesPatternJava": "^(?>[a-zA-Z0-9!$&*+=?^_\`{|}~.-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$", "fieldValidateRulesPatternReact": "^[a-zA-Z0-9!$&*+=?^_\`{|}~.-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*$|^[_.@A-Za-z0-9-]+$", - "fieldValidationMax": true, + "fieldValidationMax": false, "fieldValidationMaxBytes": false, - "fieldValidationMaxLength": false, + "fieldValidationMaxLength": true, "fieldValidationMin": false, "fieldValidationMinBytes": false, "fieldValidationMinLength": false, "fieldValidationPattern": true, "fieldValidationRequired": true, - "fieldValidationUnique": false, + "fieldValidationUnique": true, "fieldWithContentType": false, "filterableField": true, "generateFakeData": Any, @@ -420,6 +420,7 @@ describe(`generator - ${generator}`, () => { "javaValueSample1": ""login1"", "javaValueSample2": ""login2"", "loadColumnType": "string", + "maxlength": 50, "nullable": false, "path": [ "login", @@ -431,7 +432,8 @@ describe(`generator - ${generator}`, () => { "shouldCreateContentType": false, "shouldDropDefaultValue": false, "tsType": "string", - "unique": false, + "unique": true, + "uniqueConstraintName": "ux_jhi_user__login", "uniqueValue": [], }, { @@ -440,7 +442,7 @@ describe(`generator - ${generator}`, () => { "blobContentTypeText": false, "builtIn": true, "columnName": "first_name", - "columnType": "varchar(255)", + "columnType": "varchar(50)", "createRandexp": Any, "entity": Any, "fieldInJavaBeanMethod": "FirstName", @@ -450,7 +452,7 @@ describe(`generator - ${generator}`, () => { "fieldNameCapitalized": "FirstName", "fieldNameHumanized": "First Name", "fieldNameUnderscored": "first_name", - "fieldTranslationKey": "jhipsterApp.user.firstName", + "fieldTranslationKey": "userManagement.firstName", "fieldType": "String", "fieldTypeAnyBlob": false, "fieldTypeBigDecimal": false, @@ -475,13 +477,17 @@ describe(`generator - ${generator}`, () => { "fieldTypeTimed": false, "fieldTypeUUID": false, "fieldTypeZonedDateTime": false, - "fieldValidate": false, + "fieldValidate": true, + "fieldValidateRules": [ + "maxlength", + ], + "fieldValidateRulesMaxlength": 50, "fieldValidateRulesPatternAngular": undefined, "fieldValidateRulesPatternJava": undefined, "fieldValidateRulesPatternReact": undefined, "fieldValidationMax": false, "fieldValidationMaxBytes": false, - "fieldValidationMaxLength": false, + "fieldValidationMaxLength": true, "fieldValidationMin": false, "fieldValidationMinBytes": false, "fieldValidationMinLength": false, @@ -496,6 +502,7 @@ describe(`generator - ${generator}`, () => { "javaValueSample1": ""firstName1"", "javaValueSample2": ""firstName2"", "loadColumnType": "string", + "maxlength": 50, "nullable": true, "path": [ "firstName", @@ -516,7 +523,7 @@ describe(`generator - ${generator}`, () => { "blobContentTypeText": false, "builtIn": true, "columnName": "last_name", - "columnType": "varchar(255)", + "columnType": "varchar(50)", "createRandexp": Any, "entity": Any, "fieldInJavaBeanMethod": "LastName", @@ -526,7 +533,7 @@ describe(`generator - ${generator}`, () => { "fieldNameCapitalized": "LastName", "fieldNameHumanized": "Last Name", "fieldNameUnderscored": "last_name", - "fieldTranslationKey": "jhipsterApp.user.lastName", + "fieldTranslationKey": "userManagement.lastName", "fieldType": "String", "fieldTypeAnyBlob": false, "fieldTypeBigDecimal": false, @@ -551,13 +558,17 @@ describe(`generator - ${generator}`, () => { "fieldTypeTimed": false, "fieldTypeUUID": false, "fieldTypeZonedDateTime": false, - "fieldValidate": false, + "fieldValidate": true, + "fieldValidateRules": [ + "maxlength", + ], + "fieldValidateRulesMaxlength": 50, "fieldValidateRulesPatternAngular": undefined, "fieldValidateRulesPatternJava": undefined, "fieldValidateRulesPatternReact": undefined, "fieldValidationMax": false, "fieldValidationMaxBytes": false, - "fieldValidationMaxLength": false, + "fieldValidationMaxLength": true, "fieldValidationMin": false, "fieldValidationMinBytes": false, "fieldValidationMinLength": false, @@ -572,6 +583,7 @@ describe(`generator - ${generator}`, () => { "javaValueSample1": ""lastName1"", "javaValueSample2": ""lastName2"", "loadColumnType": "string", + "maxlength": 50, "nullable": true, "path": [ "lastName", @@ -586,6 +598,328 @@ describe(`generator - ${generator}`, () => { "unique": false, "uniqueValue": [], }, + { + "blobContentTypeAny": false, + "blobContentTypeImage": false, + "blobContentTypeText": false, + "builtIn": true, + "columnName": "email", + "columnType": "varchar(191)", + "createRandexp": Any, + "entity": Any, + "fieldInJavaBeanMethod": "Email", + "fieldIsEnum": false, + "fieldName": "email", + "fieldNameAsDatabaseColumn": "email", + "fieldNameCapitalized": "Email", + "fieldNameHumanized": "Email", + "fieldNameUnderscored": "email", + "fieldTranslationKey": "userManagement.email", + "fieldType": "String", + "fieldTypeAnyBlob": false, + "fieldTypeBigDecimal": false, + "fieldTypeBinary": false, + "fieldTypeBlob": false, + "fieldTypeBoolean": false, + "fieldTypeByteBuffer": false, + "fieldTypeBytes": false, + "fieldTypeCharSequence": true, + "fieldTypeDouble": false, + "fieldTypeDuration": false, + "fieldTypeFloat": false, + "fieldTypeImageBlob": false, + "fieldTypeInstant": false, + "fieldTypeInteger": false, + "fieldTypeLocalDate": false, + "fieldTypeLong": false, + "fieldTypeNumeric": false, + "fieldTypeString": true, + "fieldTypeTemporal": false, + "fieldTypeTextBlob": false, + "fieldTypeTimed": false, + "fieldTypeUUID": false, + "fieldTypeZonedDateTime": false, + "fieldValidate": true, + "fieldValidateRules": [ + "required", + "unique", + "maxlength", + "minlength", + ], + "fieldValidateRulesMaxlength": 191, + "fieldValidateRulesMinlength": 5, + "fieldValidateRulesPatternAngular": undefined, + "fieldValidateRulesPatternJava": undefined, + "fieldValidateRulesPatternReact": undefined, + "fieldValidationMax": false, + "fieldValidationMaxBytes": false, + "fieldValidationMaxLength": true, + "fieldValidationMin": false, + "fieldValidationMinBytes": false, + "fieldValidationMinLength": true, + "fieldValidationPattern": false, + "fieldValidationRequired": true, + "fieldValidationUnique": true, + "fieldWithContentType": false, + "filterableField": true, + "generateFakeData": Any, + "javaFieldType": "String", + "javaValueGenerator": "UUID.randomUUID().toString()", + "javaValueSample1": ""email1"", + "javaValueSample2": ""email2"", + "loadColumnType": "string", + "maxlength": 191, + "nullable": false, + "path": [ + "email", + ], + "propertyName": "email", + "propertyNameCapitalized": "Email", + "reference": Any, + "relationshipsPath": [], + "shouldCreateContentType": false, + "shouldDropDefaultValue": false, + "tsType": "string", + "unique": true, + "uniqueConstraintName": "ux_jhi_user__email", + "uniqueValue": [], + }, + { + "blobContentTypeAny": false, + "blobContentTypeImage": false, + "blobContentTypeText": false, + "builtIn": true, + "columnName": "image_url", + "columnType": "varchar(256)", + "createRandexp": Any, + "entity": Any, + "fieldInJavaBeanMethod": "ImageUrl", + "fieldIsEnum": false, + "fieldName": "imageUrl", + "fieldNameAsDatabaseColumn": "image_url", + "fieldNameCapitalized": "ImageUrl", + "fieldNameHumanized": "Image Url", + "fieldNameUnderscored": "image_url", + "fieldTranslationKey": "userManagement.imageUrl", + "fieldType": "String", + "fieldTypeAnyBlob": false, + "fieldTypeBigDecimal": false, + "fieldTypeBinary": false, + "fieldTypeBlob": false, + "fieldTypeBoolean": false, + "fieldTypeByteBuffer": false, + "fieldTypeBytes": false, + "fieldTypeCharSequence": true, + "fieldTypeDouble": false, + "fieldTypeDuration": false, + "fieldTypeFloat": false, + "fieldTypeImageBlob": false, + "fieldTypeInstant": false, + "fieldTypeInteger": false, + "fieldTypeLocalDate": false, + "fieldTypeLong": false, + "fieldTypeNumeric": false, + "fieldTypeString": true, + "fieldTypeTemporal": false, + "fieldTypeTextBlob": false, + "fieldTypeTimed": false, + "fieldTypeUUID": false, + "fieldTypeZonedDateTime": false, + "fieldValidate": true, + "fieldValidateRules": [ + "maxlength", + ], + "fieldValidateRulesMaxlength": 256, + "fieldValidateRulesPatternAngular": undefined, + "fieldValidateRulesPatternJava": undefined, + "fieldValidateRulesPatternReact": undefined, + "fieldValidationMax": false, + "fieldValidationMaxBytes": false, + "fieldValidationMaxLength": true, + "fieldValidationMin": false, + "fieldValidationMinBytes": false, + "fieldValidationMinLength": false, + "fieldValidationPattern": false, + "fieldValidationRequired": false, + "fieldValidationUnique": false, + "fieldWithContentType": false, + "filterableField": true, + "generateFakeData": Any, + "javaFieldType": "String", + "javaValueGenerator": "UUID.randomUUID().toString()", + "javaValueSample1": ""imageUrl1"", + "javaValueSample2": ""imageUrl2"", + "loadColumnType": "string", + "maxlength": 256, + "nullable": true, + "path": [ + "imageUrl", + ], + "propertyName": "imageUrl", + "propertyNameCapitalized": "ImageUrl", + "reference": Any, + "relationshipsPath": [], + "shouldCreateContentType": false, + "shouldDropDefaultValue": false, + "tsType": "string", + "unique": false, + "uniqueValue": [], + }, + { + "autoGenerate": true, + "blobContentTypeAny": false, + "blobContentTypeImage": false, + "blobContentTypeText": false, + "builtIn": true, + "columnName": "activated", + "columnType": "boolean", + "createRandexp": Any, + "entity": Any, + "fieldInJavaBeanMethod": "Activated", + "fieldIsEnum": false, + "fieldName": "activated", + "fieldNameAsDatabaseColumn": "activated", + "fieldNameCapitalized": "Activated", + "fieldNameHumanized": "Activated", + "fieldNameUnderscored": "activated", + "fieldTranslationKey": "userManagement.activated", + "fieldType": "Boolean", + "fieldTypeAnyBlob": false, + "fieldTypeBigDecimal": false, + "fieldTypeBinary": false, + "fieldTypeBlob": false, + "fieldTypeBoolean": true, + "fieldTypeByteBuffer": false, + "fieldTypeBytes": false, + "fieldTypeCharSequence": false, + "fieldTypeDouble": false, + "fieldTypeDuration": false, + "fieldTypeFloat": false, + "fieldTypeImageBlob": false, + "fieldTypeInstant": false, + "fieldTypeInteger": false, + "fieldTypeLocalDate": false, + "fieldTypeLong": false, + "fieldTypeNumeric": false, + "fieldTypeString": false, + "fieldTypeTemporal": false, + "fieldTypeTextBlob": false, + "fieldTypeTimed": false, + "fieldTypeUUID": false, + "fieldTypeZonedDateTime": false, + "fieldValidate": false, + "fieldValidateRulesPatternAngular": undefined, + "fieldValidateRulesPatternJava": undefined, + "fieldValidateRulesPatternReact": undefined, + "fieldValidationMax": false, + "fieldValidationMaxBytes": false, + "fieldValidationMaxLength": false, + "fieldValidationMin": false, + "fieldValidationMinBytes": false, + "fieldValidationMinLength": false, + "fieldValidationPattern": false, + "fieldValidationRequired": false, + "fieldValidationUnique": false, + "fieldWithContentType": false, + "filterableField": true, + "generateFakeData": Any, + "javaFieldType": "Boolean", + "loadColumnType": "boolean", + "nullable": true, + "path": [ + "activated", + ], + "propertyName": "activated", + "propertyNameCapitalized": "Activated", + "reference": Any, + "relationshipsPath": [], + "shouldCreateContentType": false, + "shouldDropDefaultValue": false, + "tsType": "boolean", + "unique": false, + "uniqueValue": [], + }, + { + "blobContentTypeAny": false, + "blobContentTypeImage": false, + "blobContentTypeText": false, + "builtIn": true, + "columnName": "lang_key", + "columnType": "varchar(10)", + "createRandexp": Any, + "entity": Any, + "fieldInJavaBeanMethod": "LangKey", + "fieldIsEnum": false, + "fieldName": "langKey", + "fieldNameAsDatabaseColumn": "lang_key", + "fieldNameCapitalized": "LangKey", + "fieldNameHumanized": "Lang Key", + "fieldNameUnderscored": "lang_key", + "fieldTranslationKey": "userManagement.langKey", + "fieldType": "String", + "fieldTypeAnyBlob": false, + "fieldTypeBigDecimal": false, + "fieldTypeBinary": false, + "fieldTypeBlob": false, + "fieldTypeBoolean": false, + "fieldTypeByteBuffer": false, + "fieldTypeBytes": false, + "fieldTypeCharSequence": true, + "fieldTypeDouble": false, + "fieldTypeDuration": false, + "fieldTypeFloat": false, + "fieldTypeImageBlob": false, + "fieldTypeInstant": false, + "fieldTypeInteger": false, + "fieldTypeLocalDate": false, + "fieldTypeLong": false, + "fieldTypeNumeric": false, + "fieldTypeString": true, + "fieldTypeTemporal": false, + "fieldTypeTextBlob": false, + "fieldTypeTimed": false, + "fieldTypeUUID": false, + "fieldTypeZonedDateTime": false, + "fieldValidate": true, + "fieldValidateRules": [ + "maxlength", + ], + "fieldValidateRulesMaxlength": 10, + "fieldValidateRulesPatternAngular": undefined, + "fieldValidateRulesPatternJava": undefined, + "fieldValidateRulesPatternReact": undefined, + "fieldValidationMax": false, + "fieldValidationMaxBytes": false, + "fieldValidationMaxLength": true, + "fieldValidationMin": false, + "fieldValidationMinBytes": false, + "fieldValidationMinLength": false, + "fieldValidationPattern": false, + "fieldValidationRequired": false, + "fieldValidationUnique": false, + "fieldWithContentType": false, + "filterableField": true, + "generateFakeData": Any, + "javaFieldType": "String", + "javaValueGenerator": "UUID.randomUUID().toString()", + "javaValueSample1": ""langKey1"", + "javaValueSample2": ""langKey2"", + "loadColumnType": "string", + "maxlength": 10, + "nullable": true, + "path": [ + "langKey", + ], + "propertyName": "langKey", + "propertyNameCapitalized": "LangKey", + "reference": Any, + "relationshipsPath": [], + "shouldCreateContentType": false, + "shouldDropDefaultValue": false, + "tsType": "string", + "unique": false, + "uniqueValue": [], + }, ], "fieldsContainNoOwnerOneToOne": false, "fluentMethods": true, @@ -593,8 +927,8 @@ describe(`generator - ${generator}`, () => { "generateFakeData": Any, "hasCyclicRequiredRelationship": false, "hasImageField": true, - "i18nAlertHeaderPrefix": "jhipsterApp.user", - "i18nKeyPrefix": "jhipsterApp.user", + "i18nAlertHeaderPrefix": "userManagement", + "i18nKeyPrefix": "userManagement", "implementsEagerLoadApis": false, "importApiModelProperty": false, "isUsingMapsId": false, @@ -616,10 +950,10 @@ describe(`generator - ${generator}`, () => { "otherRelationships": [], "packageFolder": "com/mycompany/myapp/", "packageName": "com.mycompany.myapp", - "pagination": "no", + "pagination": "pagination", "paginationInfiniteScroll": false, - "paginationNo": true, - "paginationPagination": false, + "paginationNo": false, + "paginationPagination": true, "persistClass": "User", "persistInstance": "user", "persistableOtherEntities": [], @@ -667,7 +1001,7 @@ describe(`generator - ${generator}`, () => { "reactiveUniqueEntityTypes": Any, "readOnly": false, "regularEagerRelations": Any, - "relationships": [], + "relationships": Any, "relationshipsByOtherEntity": {}, "relationshipsContainEagerLoad": false, "relationshipsContainOtherSideIgnore": false, @@ -727,6 +1061,7 @@ describe(`generator - ${generator}`, () => { "applicationType": "monolith", "authenticationType": "jwt", "baseName": "jhipster", + "builtInUserManagement": undefined, "changelogDate": "20220129025419", "changelogDateForRecent": 2022-01-29T02:54:19.000Z, "clientFramework": "angular", @@ -951,7 +1286,7 @@ describe(`generator - ${generator}`, () => { "reactiveUniqueEntityTypes": Any, "readOnly": false, "regularEagerRelations": Any, - "relationships": [], + "relationships": Any, "relationshipsByOtherEntity": {}, "relationshipsContainEagerLoad": false, "relationshipsContainOtherSideIgnore": false, @@ -1064,6 +1399,7 @@ describe(`generator - ${generator}`, () => { "applicationType": "monolith", "authenticationType": "jwt", "baseName": "jhipster", + "builtInUserManagement": undefined, "changelogDate": "20220129025419", "changelogDateForRecent": 2022-01-29T02:54:19.000Z, "clientFramework": "angular", @@ -1288,7 +1624,7 @@ describe(`generator - ${generator}`, () => { "reactiveUniqueEntityTypes": Any, "readOnly": false, "regularEagerRelations": Any, - "relationships": [], + "relationships": Any, "relationshipsByOtherEntity": {}, "relationshipsContainEagerLoad": false, "relationshipsContainOtherSideIgnore": false, diff --git a/generators/languages/entity-files.js b/generators/languages/entity-files.js index a88aa498e073..0c78eabd71bb 100644 --- a/generators/languages/entity-files.js +++ b/generators/languages/entity-files.js @@ -17,6 +17,7 @@ * limitations under the License. */ import { getEnumInfo } from '../base-application/support/index.js'; +import { CLIENT_MAIN_SRC_DIR } from '../generator-constants.js'; /** * The default is to use a file path string. It implies use of the template method. @@ -35,6 +36,17 @@ export const entityClientI18nFiles = { ], }; +export const userTranslationfiles = { + userTranslationfiles: [ + { + from: context => `${CLIENT_MAIN_SRC_DIR}/i18n/${context.lang}/`, + to: context => `${context.clientSrcDir}/i18n/${context.lang}/`, + transform: false, + templates: ['user-management.json'], + }, + ], +}; + export const enumClientI18nFiles = { enumBaseFiles: [ { @@ -82,13 +94,20 @@ export function writeEntityFiles() { async writeClientFiles({ application, entities }) { if (application.skipClient) return; const entitiesToWriteTranslationFor = entities.filter(entity => !entity.skipClient && !entity.builtInUser); + if (application.userManagement && application.userManagement.skipClient) { + entitiesToWriteTranslationFor.push(application.userManagement); + } // Copy each const { clientSrcDir, frontendAppName } = application; const languagesToApply = application.enableTranslation ? this.languagesToApply : [...new Set([application.nativeLanguage, 'en'])]; for (const entity of entitiesToWriteTranslationFor) { for (const lang of languagesToApply) { - await this.writeFiles({ sections: entityClientI18nFiles, context: { ...entity, clientSrcDir, frontendAppName, lang } }); + if (entity.builtInUserManagement) { + await this.writeFiles({ sections: userTranslationfiles, context: { ...entity, clientSrcDir, frontendAppName, lang } }); + } else { + await this.writeFiles({ sections: entityClientI18nFiles, context: { ...entity, clientSrcDir, frontendAppName, lang } }); + } } } }, diff --git a/generators/languages/files.js b/generators/languages/files.js index cc8547c65509..eaa4e34ee1d1 100644 --- a/generators/languages/files.js +++ b/generators/languages/files.js @@ -25,16 +25,7 @@ export const clientI18nFiles = { from: context => `${CLIENT_MAIN_SRC_DIR}/i18n/${context.lang}/`, to: context => `${context.clientSrcDir}/i18n/${context.lang}/`, transform: false, - templates: [ - 'error.json', - 'login.json', - 'home.json', - 'password.json', - 'register.json', - 'sessions.json', - 'settings.json', - 'user-management.json', - ], + templates: ['error.json', 'login.json', 'home.json', 'password.json', 'register.json', 'sessions.json', 'settings.json'], }, { condition: ctx => ctx.clientFrameworkVue && ctx.enableTranslation && !ctx.microfrontend, diff --git a/generators/languages/translation-data.js b/generators/languages/translation-data.js index cc3abacdf55c..d82778bb0def 100644 --- a/generators/languages/translation-data.js +++ b/generators/languages/translation-data.js @@ -94,6 +94,10 @@ export default class TranslationData { if (!data) { return translatedValue; } + /* workaround custom translation for UserManagement */ + if (translatedValue.includes('{{ login }}') && !data.login) { + data.login = data.id; + } const compiledTemplate = _.template(translatedValue, { interpolate: /{{([\s\S]+?)}}/g }); return compiledTemplate(data); } diff --git a/generators/spring-boot/templates/src/main/java/_package_/_entityPackage_/web/rest/UserResource.java.ejs b/generators/spring-boot/templates/src/main/java/_package_/_entityPackage_/web/rest/UserResource.java.ejs index 45f2688d74e0..44280a97c8e3 100644 --- a/generators/spring-boot/templates/src/main/java/_package_/_entityPackage_/web/rest/UserResource.java.ejs +++ b/generators/spring-boot/templates/src/main/java/_package_/_entityPackage_/web/rest/UserResource.java.ejs @@ -204,10 +204,10 @@ public class UserResource { * @throws EmailAlreadyUsedException {@code 400 (Bad Request)} if the email is already in use. * @throws LoginAlreadyUsedException {@code 400 (Bad Request)} if the login is already in use. */ - @PutMapping("/users") + @PutMapping({ "/users", "/users/{login}" }) @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") <%_ if (reactive) { _%> - public Mono>> updateUser(@Valid @RequestBody <%= user.adminUserDto %> userDTO) { + public Mono>> updateUser(@PathVariable(name = "login", required = false) @Pattern(regexp = Constants.LOGIN_REGEX) String login, @Valid @RequestBody <%= user.adminUserDto %> userDTO) { log.debug("REST request to update User : {}", userDTO); return userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()) .filter(user -> !user.getId().equals(userDTO.getId())) @@ -232,7 +232,7 @@ public class UserResource { .body(user) ); <%_ } else { _%> - public ResponseEntity<<%= user.adminUserDto %>> updateUser(@Valid @RequestBody <%= user.adminUserDto %> userDTO) { + public ResponseEntity<<%= user.adminUserDto %>> updateUser(@PathVariable(name = "login", required = false) @Pattern(regexp = Constants.LOGIN_REGEX) String login, @Valid @RequestBody <%= user.adminUserDto %> userDTO) { log.debug("REST request to update User : {}", userDTO); Optional<<%= user.persistClass %>> existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); if (existingUser.isPresent() && (!existingUser.orElseThrow().getId().equals(userDTO.getId()))) {