diff --git a/generators/base-application/support/prepare-entity.spec.ts b/generators/base-application/support/prepare-entity.spec.ts index 33fe38159305..fe9e2cb7fe4f 100644 --- a/generators/base-application/support/prepare-entity.spec.ts +++ b/generators/base-application/support/prepare-entity.spec.ts @@ -23,6 +23,8 @@ import { formatDateForChangelog } from '../../base/support/index.js'; import { prepareEntityPrimaryKeyForTemplates, entityDefaultConfig } from './prepare-entity.js'; import BaseGenerator from '../../base/index.js'; import { getConfigWithDefaults } from '../../../jdl/jhipster/index.js'; +import { JDLSecurityType, PrivilegeActionType, RoleActionType } from '../../../jdl/models/jdl-security-type.js'; +import { prepareEntity } from './index.js'; describe('generator - base-application - support - prepareEntity', () => { const defaultGenerator = { jhipsterConfig: getConfigWithDefaults() }; @@ -301,4 +303,159 @@ describe('generator - base-application - support - prepareEntity', () => { }); }); }); + + describe('prepare entity security', () => { + describe('with no security', () => { + it('prepareEntity should not add security', () => { + const entity = { + ...entityDefaultConfig, + name: 'Entity', + changelogDate: formatDateForChangelog(new Date()), + fields: [{ fieldName: 'id', fieldType: 'CustomType', path: ['id'], relationshipsPath: [] }], + }; + const result = prepareEntity(entity, defaultGenerator, 'testApp'); + expect(result.security).to.be.undefined; + expect(result.hasSecurity).to.equal(false); + expect(result.hasRolesSecurity).to.equal(false); + }); + + it('prepareEntity should add role based security', () => { + const entity = { + ...entityDefaultConfig, + name: 'Entity', + changelogDate: formatDateForChangelog(new Date()), + fields: [{ fieldName: 'id', fieldType: 'CustomType', path: ['id'], relationshipsPath: [] }], + secure: { + securityType: JDLSecurityType.Roles, + roles: [ + { role: 'ROLE_ALLOWED', actionList: [RoleActionType.Post, RoleActionType.Put, RoleActionType.Get] }, + { role: 'ROLE_FORBIDDEN', actionList: [RoleActionType.Get] }, + ], + }, + }; + const result = prepareEntity(entity, defaultGenerator, 'testApp'); + expect(result.security).to.exist; + expect(result.hasSecurity).to.equal(true); + expect(result.hasRolesSecurity).to.equal(true); + expect(result.security.securityType).to.equal(JDLSecurityType.Roles); + expect(result.security.roles).to.deep.equal({ + get: '"ROLE_ALLOWED", "ROLE_FORBIDDEN"', + put: '"ROLE_ALLOWED"', + post: '"ROLE_ALLOWED"', + delete: '"DUMMY_ROLE_NO_ACCESS"', + }); + expect(result.security.allowedRoles).to.exist; + expect(result.security.allowedRoles).to.deep.equal({ + delete: [], + get: ['ROLE_ALLOWED', 'ROLE_FORBIDDEN'], + post: ['ROLE_ALLOWED'], + put: ['ROLE_ALLOWED'], + }); + expect(result.security.forbiddenRoles).to.exist; + expect(result.security.forbiddenRoles).to.deep.equal({ + delete: ['ROLE_ALLOWED', 'ROLE_FORBIDDEN'], + get: [], + post: ['ROLE_FORBIDDEN'], + put: ['ROLE_FORBIDDEN'], + }); + }); + + it('prepareEntity should add privilege based security', () => { + const entity = { + ...entityDefaultConfig, + name: 'Entity', + changelogDate: formatDateForChangelog(new Date()), + fields: [{ fieldName: 'id', fieldType: 'CustomType', path: ['id'], relationshipsPath: [] }], + secure: { + securityType: JDLSecurityType.Privileges, + privileges: [{ action: PrivilegeActionType.Read, privList: ['Admin'] }], + }, + }; + const result = prepareEntity(entity, defaultGenerator, 'testApp'); + expect(result.security).to.exist; + expect(result.hasSecurity).to.equal(true); + expect(result.hasPrivilegeSecurity).to.equal(true); + expect(result.security.securityType).to.equal(JDLSecurityType.Privileges); + expect(result.security.privileges).to.deep.equal({ read: '"ADMIN"' }); + }); + + it('prepareEntity should add organizational security', () => { + const entity = { + ...entityDefaultConfig, + name: 'Entity', + changelogDate: formatDateForChangelog(new Date()), + fields: [{ fieldName: 'id', fieldType: 'CustomType', path: ['id'], relationshipsPath: [] }], + secure: { + securityType: JDLSecurityType.OrganizationalSecurity, + organizationalSecurity: { resource: 'TestResource' }, + }, + }; + const result = prepareEntity(entity, defaultGenerator, 'testApp'); + expect(result.security).to.exist; + expect(result.hasSecurity).to.equal(true); + expect(result.hasOrganizationalSecurity).to.equal(true); + expect(result.security.securityType).to.equal(JDLSecurityType.OrganizationalSecurity); + expect(result.security.organizationalSecurity).to.deep.equal({ resource: 'TestResource' }); + }); + + it('prepareEntity should add parent based security', () => { + const entity = { + ...entityDefaultConfig, + name: 'Entity', + changelogDate: formatDateForChangelog(new Date()), + fields: [{ fieldName: 'id', fieldType: 'CustomType', path: ['id'], relationshipsPath: [] }], + secure: { + securityType: JDLSecurityType.ParentPrivileges, + parentPrivileges: { parent: 'TestParent', field: 'TestField' }, + }, + }; + const result = prepareEntity(entity, defaultGenerator, 'testApp'); + expect(result.security).to.exist; + expect(result.hasSecurity).to.equal(true); + expect(result.hasParentSecurity).to.equal(true); + expect(result.security.securityType).to.equal(JDLSecurityType.ParentPrivileges); + expect(result.security.parentPrivileges).to.deep.equal({ + field: 'TestField', + parent: 'TestParent', + parentEntityApiUrl: 'test-parents', + parentEntityClass: 'TestParent', + parentEntityInstance: 'testParent', + parentEntityName: 'TestParent', + parentFieldName: 'TestField', + parentFieldNameUpper: 'TestField', + parentInstanceName: 'testParent', + }); + }); + + it('prepareEntity should add relational based security', () => { + const entity = { + ...entityDefaultConfig, + name: 'Entity', + changelogDate: formatDateForChangelog(new Date()), + fields: [{ fieldName: 'id', fieldType: 'CustomType', path: ['id'], relationshipsPath: [] }], + secure: { + securityType: JDLSecurityType.RelPrivileges, + relPrivileges: { + fromEntity: 'FromEntity', + fromField: 'FromField', + toEntity: 'ToEntity', + toField: 'toField', + }, + comment: 'comment', + }, + }; + const result = prepareEntity(entity, defaultGenerator, 'testApp'); + expect(result.security).to.exist; + expect(result.hasSecurity).to.equal(true); + expect(result.hasRelationSecurity).to.equal(true); + expect(result.security.securityType).to.equal(JDLSecurityType.RelPrivileges); + expect(result.security.relPrivileges).to.deep.equal({ + fromEntity: 'FromEntity', + fromField: 'FromField', + toEntity: 'ToEntity', + toField: 'toField', + }); + }); + }); + }); }); diff --git a/generators/base-application/support/prepare-entity.ts b/generators/base-application/support/prepare-entity.ts index 6a3f7770627d..4762dab83466 100644 --- a/generators/base-application/support/prepare-entity.ts +++ b/generators/base-application/support/prepare-entity.ts @@ -44,6 +44,7 @@ import { fieldIsEnum } from './field-utils.js'; import { Entity } from '../types/index.js'; import type CoreGenerator from '../../base-core/generator.js'; +import { JDLSecurityType } from '../../../jdl/models/jdl-security-type.js'; const { sortedUniq, intersection } = _; @@ -268,10 +269,199 @@ export default function prepareEntity(entityWithConfig, generator, application) return Object.fromEntries(fieldEntries); }; _derivedProperties(entityWithConfig); + defineEntitySecurity(entityWithConfig); return entityWithConfig; } +/** + * Defines role based security for an entity. + * + * @param {any} entityWithConfig - The entity with security configuration. + */ +function defineRoleBasedSecurity(entityWithConfig: any) { + entityWithConfig.hasSecurity = true; + entityWithConfig.hasRolesSecurity = true; + const allRoles: any[] = []; + const allowedRolesAsString: any = { get: [], put: [], post: [], delete: [] }; + const allowedRoles: any = { get: [], put: [], post: [], delete: [] }; + for (let i = 0; i < entityWithConfig.secure.roles.length; i++) { + const role = `"${entityWithConfig.secure.roles[i].role}"`; + allRoles.push(entityWithConfig.secure.roles[i].role); + for (let j = 0; j < entityWithConfig.secure.roles[i].actionList.length; j++) { + const action = _.lowerCase(entityWithConfig.secure.roles[i].actionList[j]); + if (!allowedRolesAsString[action].includes(role)) { + allowedRolesAsString[action].push(role); + allowedRoles[action].push(entityWithConfig.secure.roles[i].role); + } + } + } + const forbiddenRoles: any = {}; + forbiddenRoles.get = allRoles.filter(item => allowedRoles.get.indexOf(item) < 0); + forbiddenRoles.put = allRoles.filter(item => allowedRoles.put.indexOf(item) < 0); + forbiddenRoles.post = allRoles.filter(item => allowedRoles.post.indexOf(item) < 0); + forbiddenRoles.delete = allRoles.filter(item => allowedRoles.delete.indexOf(item) < 0); + entityWithConfig.security.roles = { + get: allowedRolesAsString.get.length > 0 ? allowedRolesAsString.get.join(', ') : '"DUMMY_ROLE_NO_ACCESS"', + put: allowedRolesAsString.put.length > 0 ? allowedRolesAsString.put.join(', ') : '"DUMMY_ROLE_NO_ACCESS"', + post: allowedRolesAsString.post.length > 0 ? allowedRolesAsString.post.join(', ') : '"DUMMY_ROLE_NO_ACCESS"', + delete: allowedRolesAsString.delete.length > 0 ? allowedRolesAsString.delete.join(', ') : '"DUMMY_ROLE_NO_ACCESS"', + }; + entityWithConfig.security.allowedRoles = { + get: [...allowedRoles.get], + put: [...allowedRoles.put], + post: [...allowedRoles.post], + delete: [...allowedRoles.delete], + }; + entityWithConfig.security.forbiddenRoles = { + get: [...forbiddenRoles.get], + put: [...forbiddenRoles.put], + post: [...forbiddenRoles.post], + delete: [...forbiddenRoles.delete], + }; +} + +/** + * Defines privilege-based security for an entity with configuration. + * + * @param {Object} entityWithConfig - The entity with configuration. + * @return {void} + */ +function definePrivilegeBasedSecurity(entityWithConfig: any) { + entityWithConfig.hasSecurity = true; + entityWithConfig.hasPrivilegeSecurity = true; + const privileges = {}; + for (let i = 0; i < entityWithConfig.secure.privileges.length; i++) { + const action = _.lowerCase(entityWithConfig.secure.privileges[i].action); + if (entityWithConfig.secure.privileges[i].privList.length > 0) { + privileges[action] = entityWithConfig.secure.privileges[i].privList + .map(x => `"${x}"`) + .join(', ') + .toUpperCase(); + } + } + entityWithConfig.security.privileges = privileges; +} + +/** + * Sets the organizational security for the given entity with configuration. + * + * @param {any} entityWithConfig - The entity object with configuration. + * @return {void} + */ +function defineOrganizationalSecurity(entityWithConfig: any) { + entityWithConfig.hasSecurity = true; + entityWithConfig.hasOrganizationalSecurity = true; + entityWithConfig.security.organizationalSecurity = entityWithConfig.secure.organizationalSecurity; +} + +/** + * Defines parent based security for the given entity. + * + * @param {any} entityWithConfig - The entity object with configuration. + */ +function defineParentBasedSecurity(entityWithConfig: any) { + entityWithConfig.hasSecurity = true; + entityWithConfig.hasParentSecurity = true; + const parentPrivileges = entityWithConfig.secure.parentPrivileges; + entityWithConfig.security.parentPrivileges = parentPrivileges; + entityWithConfig.security.roles = { + get: 'ROLE_ADMIN', + put: 'ROLE_ADMIN', + post: 'ROLE_ADMIN', + delete: 'ROLE_ADMIN', + }; + entityWithConfig.security.parentPrivileges.parentEntityClass = _.upperFirst(parentPrivileges.parent); + entityWithConfig.security.parentPrivileges.parentEntityName = parentPrivileges.parent; + entityWithConfig.security.parentPrivileges.parentEntityApiUrl = _.kebabCase(_.lowerFirst(pluralize(parentPrivileges.parent))); + entityWithConfig.security.parentPrivileges.parentEntityInstance = _.lowerFirst(parentPrivileges.parent); + entityWithConfig.security.parentPrivileges.parentInstanceName = _.lowerFirst(parentPrivileges.parent); + entityWithConfig.security.parentPrivileges.parentFieldNameUpper = _.upperFirst(parentPrivileges.field); + entityWithConfig.security.parentPrivileges.parentFieldName = parentPrivileges.field; +} + +/** + * Sets up relational security for the given entity with configuration. + * + * @param {any} entityWithConfig - The entity with its security configuration. + * @return {void} + */ +function defineRelationalSecurity(entityWithConfig: any) { + entityWithConfig.hasSecurity = true; + entityWithConfig.hasRelationSecurity = true; + entityWithConfig.security.relPrivileges = entityWithConfig.secure.relPrivileges; +} + +/** + * Retrieves the security type associated with the given entity configuration. + * @param {any} entityWithConfig - The entity with its configuration. + * @return {JDLSecurityType} - The security type of the entity. Returns JDLSecurityType.None if no security type is defined. + */ +function getSecurityType(entityWithConfig: any): JDLSecurityType { + return entityWithConfig.secure?.securityType || JDLSecurityType.None; +} + +/** + * Checks if the given security type is of "None" type. + * + * @param {JDLSecurityType} securityType - The security type to be checked. + * @return {boolean} - True if the security type is "None", false otherwise. + */ +function isNoneSecurityType(securityType: JDLSecurityType): boolean { + return securityType === JDLSecurityType.None; +} + +/** + * Assigns a security type to an entity object and initialize security object on the entity. + * + * @param {any} entityWithConfig - The entity object with configuration. + * @param {JDLSecurityType} securityType - The security type to assign. + * @return {void} + */ +function assignEntitySecurity(entityWithConfig: any, securityType: JDLSecurityType): void { + entityWithConfig.security = { + securityType, + }; +} + +/** + * Defines the security for the given entity. Security is defined through 'secure' property in the entity configuration. + * + * @param {Object} entityWithConfig - The entity with its configuration. + * @throws {Error} If the entity has an unknown security type. + */ +function defineEntitySecurity(entityWithConfig: any): void { + const securityType = getSecurityType(entityWithConfig); + + if (isNoneSecurityType(securityType)) { + entityWithConfig.hasSecurity = false; + entityWithConfig.hasRolesSecurity = false; + return; + } + + assignEntitySecurity(entityWithConfig, securityType); + + switch (securityType) { + case JDLSecurityType.Roles: + defineRoleBasedSecurity(entityWithConfig); + break; + case JDLSecurityType.Privileges: + definePrivilegeBasedSecurity(entityWithConfig); + break; + case JDLSecurityType.OrganizationalSecurity: + defineOrganizationalSecurity(entityWithConfig); + break; + case JDLSecurityType.ParentPrivileges: + defineParentBasedSecurity(entityWithConfig); + break; + case JDLSecurityType.RelPrivileges: + defineRelationalSecurity(entityWithConfig); + break; + default: + throw new Error(`Entity ${entityWithConfig.name()} has defined unknown security type: ${securityType}`); + } +} + export function derivedPrimaryKeyProperties(primaryKey) { _.defaults(primaryKey, { hasUUID: primaryKey.fields && primaryKey.fields.some(field => field.fieldType === UUID), diff --git a/generators/bootstrap-application/generator.spec.ts b/generators/bootstrap-application/generator.spec.ts index 2bf57a0c4ffb..5120455964c4 100644 --- a/generators/bootstrap-application/generator.spec.ts +++ b/generators/bootstrap-application/generator.spec.ts @@ -575,6 +575,8 @@ describe(`generator - ${generator}`, () => { "fluentMethods": true, "frontendAppName": "jhipsterApp", "generateFakeData": Any, + "hasRolesSecurity": false, + "hasSecurity": false, "i18nAlertHeaderPrefix": "jhipsterApp.user", "i18nKeyPrefix": "jhipsterApp.user", "implementsEagerLoadApis": false, @@ -852,6 +854,8 @@ describe(`generator - ${generator}`, () => { "fluentMethods": true, "frontendAppName": "jhipsterApp", "generateFakeData": Any, + "hasRolesSecurity": false, + "hasSecurity": false, "i18nAlertHeaderPrefix": "jhipsterApp.entityA", "i18nKeyPrefix": "jhipsterApp.entityA", "implementsEagerLoadApis": false, @@ -1184,6 +1188,8 @@ describe(`generator - ${generator}`, () => { "fluentMethods": true, "frontendAppName": "jhipsterApp", "generateFakeData": Any, + "hasRolesSecurity": false, + "hasSecurity": false, "i18nAlertHeaderPrefix": "jhipsterApp.entityA", "i18nKeyPrefix": "jhipsterApp.entityA", "implementsEagerLoadApis": false, diff --git a/generators/server/entity-files.js b/generators/server/entity-files.js index 61b10ad0f49e..47f87c7e5bed 100644 --- a/generators/server/entity-files.js +++ b/generators/server/entity-files.js @@ -39,7 +39,7 @@ export const restFiles = { ], restTestFiles: [ { - condition: generator => !generator.embedded, + condition: generator => !generator.embedded && !generator.hasSecurity, path: SERVER_TEST_SRC_DIR, templates: [ { @@ -56,6 +56,24 @@ export const restFiles = { }, ], }, + { + condition: generator => !generator.embedded && generator.hasRolesSecurity, + path: SERVER_TEST_SRC_DIR, + templates: [ + { + file: '_package_/_entityPackage_/web/rest/_entityClass_secRoles_ResourceIT.java', + options: { + context: { + _, + chalkRed: chalk.red, + fs, + SERVER_TEST_SRC_DIR, + }, + }, + renameTo: generator => `${generator.entityAbsoluteFolder}/web/rest/${generator.entityClass}ResourceIT.java`, + }, + ], + }, ], }; diff --git a/generators/server/templates/src/main/java/_package_/_entityPackage_/web/rest/_entityClass_Resource.java.ejs b/generators/server/templates/src/main/java/_package_/_entityPackage_/web/rest/_entityClass_Resource.java.ejs index 165af1ab2318..f19fef4b42c6 100644 --- a/generators/server/templates/src/main/java/_package_/_entityPackage_/web/rest/_entityClass_Resource.java.ejs +++ b/generators/server/templates/src/main/java/_package_/_entityPackage_/web/rest/_entityClass_Resource.java.ejs @@ -125,6 +125,9 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; <%_ } _%> <%_ } _%> +<%_ if (hasRolesSecurity) { _%> +import org.springframework.security.access.annotation.Secured; +<%_ } _%> /** * REST controller for managing {@link <%= entityAbsoluteClass %>}. @@ -161,9 +164,15 @@ public class <%= entityClass %>Resource { * * @param <%= instanceName %> the <%= instanceName %> to create. * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new <%= instanceName %>, or with status {@code 400 (Bad Request)} if the <%= entityInstance %> has already an ID. + <%_ if (hasRolesSecurity) { _%> + * If the user does not have authority to access the endpoint it will return status {@code 403 (Forbidden)} . + <%_ } _%> * @throws URISyntaxException if the Location URI syntax is incorrect. */ @PostMapping("") + <%_ if (hasRolesSecurity && security.roles.post) { _%> + @Secured({<%- security.roles.post %>}) + <%_ }_%> public <% if (reactive) { %>Mono<<% } %>ResponseEntity<<%= instanceType %>><% if (reactive) { %>><% } %> create<%= entityClass %>(<% if (anyPropertyHasValidation) { %>@Valid <% } %>@RequestBody <%= instanceType %> <%= instanceName %>) throws URISyntaxException { log.debug("REST request to save <%= entityClass %> : {}", <%= instanceName %>); if (<%= instanceName %>.get<%= primaryKey.nameCapitalized %>() != null) { @@ -214,10 +223,16 @@ public class <%= entityClass %>Resource { * @param <%= instanceName %> the <%= instanceName %> to update. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated <%= instanceName %>, * or with status {@code 400 (Bad Request)} if the <%= instanceName %> is not valid, + <%_ if (hasRolesSecurity) { _%> + * or with status {@code 403 (Forbidden)} if the user does not have authority to access the endpoint, + <%_ } _%> * or with status {@code 500 (Internal Server Error)} if the <%= instanceName %> couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ @PutMapping("/{<%= primaryKey.name %>}") + <%_ if (hasRolesSecurity && security.roles.put) { _%> + @Secured({<%- security.roles.put %>}) + <%_ }_%> public <% if (reactive) { %>Mono<<% } %>ResponseEntity<<%= instanceType %>><% if (reactive) { %>><% } %> update<%= entityClass %>( @PathVariable(value = "<%= primaryKey.name %>", required = false) final <%= primaryKey.type %> <%= primaryKey.name %>, <% if (anyPropertyHasValidation) { %>@Valid <% } %>@RequestBody <%= instanceType %> <%= instanceName %> @@ -279,11 +294,17 @@ public class <%= entityClass %>Resource { * @param <%= instanceName %> the <%= instanceName %> to update. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated <%= instanceName %>, * or with status {@code 400 (Bad Request)} if the <%= instanceName %> is not valid, + <%_ if (hasRolesSecurity) { _%> + * or with status {@code 403 (Forbidden)} if the user does not have authority to access the endpoint, + <%_ } _%> * or with status {@code 404 (Not Found)} if the <%= instanceName %> is not found, * or with status {@code 500 (Internal Server Error)} if the <%= instanceName %> couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ @PatchMapping(value = "/{<%= primaryKey.name %>}", consumes = {"application/json", "application/merge-patch+json"}) + <%_ if (hasRolesSecurity && security.roles.put) { _%> + @Secured({<%- security.roles.put %>}) + <%_ }_%> public <% if (reactive) { %>Mono<<% } %>ResponseEntity<<%= instanceType %>><% if (reactive) { %>><% } %> partialUpdate<%= entityClass %>( @PathVariable(value = "<%= primaryKey.name %>", required = false) final <%= primaryKey.type %> <%= primaryKey.name %>, <% if (anyPropertyHasValidation) { %>@NotNull <% } %>@RequestBody <%= instanceType %> <%= instanceName %>) throws URISyntaxException { @@ -358,12 +379,18 @@ public class <%= entityClass %>Resource { * @param filter the filter of the request. <%_ } _%> * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of <%= entityInstancePlural %> in body. +<%_ if (hasRolesSecurity) { _%> + * If the user does not have authority to access the endpoint it will return status {@code 403 (Forbidden)} . +<%_ } _%> */ <%_ if (reactive) { _%> @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE) <%_ } else { _%> @GetMapping("") <%_ } _%> + <%_ if (hasRolesSecurity && security.roles.get) { _%> + @Secured({<%- security.roles.get %>}) + <%_ }_%> <%_ if (databaseTypeSql && isUsingMapsId && !viaService) { _%> @Transactional(readOnly = true) <%_ } _%> @@ -373,8 +400,14 @@ public class <%= entityClass %>Resource { /** * {@code GET /<%= entityApiUrl %>} : get all the <%= entityInstancePlural %> as a stream. * @return the {@link Flux} of <%= entityInstancePlural %>. + <%_ if (hasRolesSecurity) { _%> + * If the user does not have authority to access the endpoint it will return status {@code 403 (Forbidden)} . + <%_ } _%> */ @GetMapping(value = "", produces = MediaType.APPLICATION_NDJSON_VALUE) + <%_ if (hasRolesSecurity && security.roles.get) { _%> + @Secured({<%- security.roles.get %>}) + <%_ }_%> <%_ if (databaseTypeSql && isUsingMapsId && !viaService) { _%> @Transactional(readOnly = true) <%_ } _%> @@ -393,8 +426,14 @@ public class <%= entityClass %>Resource { * * @param id the id of the <%= instanceName %> to retrieve. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the <%= instanceName %>, or with status {@code 404 (Not Found)}. + <%_ if (hasRolesSecurity) { _%> + * If the user does not have authority to access the endpoint it will return status {@code 403 (Forbidden)} . + <%_ } _%> */ @GetMapping("/{id}") + <%_ if (hasRolesSecurity && security.roles.get) { _%> + @Secured({<%- security.roles.get %>}) + <%_ }_%> <%_ if (databaseTypeSql && isUsingMapsId && !viaService) { _%> @Transactional(readOnly = true) <%_ } _%> @@ -409,8 +448,14 @@ public class <%= entityClass %>Resource { * * @param id the id of the <%= instanceName %> to delete. * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. + <%_ if (hasRolesSecurity) { _%> + * If the user does not have authority to access the endpoint it will return status {@code 403 (Forbidden)} . + <%_ } _%> */ @DeleteMapping("/{id}") + <%_ if (hasRolesSecurity && security.roles.delete) { _%> + @Secured({<%- security.roles.delete %>}) + <%_ }_%> public <% if (reactive) { %>Mono<<% } %>ResponseEntity<% if (reactive) { %>><% } %> delete<%= entityClass %>(@PathVariable("id") <%= primaryKey.type %> id) { log.debug("REST request to delete <%= entityClass %> : {}", id); <%- include('../../_partials_entity_/delete_template', {viaService: viaService, fromResource: true}); -%> @@ -443,7 +488,15 @@ public class <%= entityClass %>Resource { <%_ } _%> <%_ } _%> * @return the result of the search. + <%_ if (hasRolesSecurity) { _%> + * If the user does not have authority to access the endpoint it will return status {@code 403 (Forbidden)} . + <%_ } _%> */ + <%_ if (hasRolesSecurity && security.roles.get) { _%> + @GetMapping("/_search") + @Secured({<%- security.roles.get %>})<%- include('../../_partials_entity_/search_template', {viaService}); -%> + <%_ } else { _%> @GetMapping("/_search")<%- include('../../_partials_entity_/search_template', {viaService}); -%> + <%_ } _%> <%_ } _%> } diff --git a/generators/server/templates/src/test/java/_package_/_entityPackage_/web/rest/_entityClass_secRoles_ResourceIT.java.ejs b/generators/server/templates/src/test/java/_package_/_entityPackage_/web/rest/_entityClass_secRoles_ResourceIT.java.ejs new file mode 100644 index 000000000000..c2538f16b312 --- /dev/null +++ b/generators/server/templates/src/test/java/_package_/_entityPackage_/web/rest/_entityClass_secRoles_ResourceIT.java.ejs @@ -0,0 +1,2318 @@ +<%# + Copyright 2013-2023 the original author or authors from the JHipster project. + + This file is part of the JHipster project, see https://www.jhipster.tech/ + for more information. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +#%> +package <%= entityAbsolutePackage %>.web.rest; + +<%_ +var filterTestableRelationships = reactive ? reactiveEagerRelations : relationships; +const fieldsToTest = fields.filter(field => !field.id && !field.autoGenerate && !field.transient); +let mapsIdEntity; +let mapsIdEntityInstance; +let mapsIdRepoInstance; +if (isUsingMapsId) { + mapsIdEntity = mapsIdAssoc.otherEntityNameCapitalized; + mapsIdEntityInstance = mapsIdEntity.charAt(0).toLowerCase() + mapsIdEntity.slice(1); + mapsIdRepoInstance = `${mapsIdEntityInstance}Repository`; +} + +let callBlock = ''; +let callListBlock = ''; +if (reactive) { + callBlock = ".block()"; + callListBlock = ".collectList().block()"; +} +let saveMethod = 'save'; +if (!reactive && databaseTypeSql) { + saveMethod = 'saveAndFlush'; +} +let createEntityPrefix = ''; +let createEntityPostfix = ''; +if (databaseTypeSql && reactive) { + createEntityPrefix = 'em.insert('; + createEntityPostfix = ').block()'; +} +let idValue = `${persistInstance}.get${primaryKey.nameCapitalized}()`; +if (primaryKey.typeLong || primaryKey.typeInteger) { + idValue = idValue + '.intValue()'; +} else if (primaryKey.typeUUID) { + idValue = idValue + '.toString()'; +} +let transactionalAnnotation = ''; +if (databaseTypeSql && !reactive) { + transactionalAnnotation = '\n @Transactional'; +} + +_%> +<%_ +// prepare roles security variables +let getDefaultRole = "ROLE_NAME"; +let postDefaultRole = "ROLE_NAME"; +let putDefaultRole = "ROLE_NAME"; +let deleteDefaultRole = "ROLE_NAME"; +let missingActions = []; +let getTestPrefix = "// @Test"; +let postTestPrefix = "// @Test"; +let putTestPrefix = "// @Test"; +let deleteTestPrefix = "// @Test"; +let allowedGetRoles = [ ... security.allowedRoles.get ]; +let allowedPostRoles = [ ... security.allowedRoles.post ]; +let allowedPutRoles = [ ... security.allowedRoles.put ]; +let allowedDeleteRoles = [ ... security.allowedRoles.delete ]; +if (security.allowedRoles.get.length > 0) { + getTestPrefix = "@Test"; + getDefaultRole = security.allowedRoles.get[0]; +} else { + missingActions.push("GET"); + allowedGetRoles.push("ROLE_NAME"); +} +if (security.allowedRoles.post.length > 0) { + postTestPrefix = "@Test"; + postDefaultRole = security.allowedRoles.post[0]; +} else { + missingActions.push("POST"); + allowedPostRoles.push("ROLE_NAME"); +} +if (security.allowedRoles.put.length > 0) { + putTestPrefix = "@Test"; + putDefaultRole = security.allowedRoles.put[0]; +} else { + missingActions.push("PUT"); + allowedPutRoles.push("ROLE_NAME"); +} +if (security.allowedRoles.delete.length > 0) { + deleteTestPrefix = "@Test"; + deleteDefaultRole = security.allowedRoles.delete[0]; +} else { + missingActions.push("DELETE"); + allowedDeleteRoles.push("ROLE_NAME"); +} +let missingActionsWarning = undefined; +if (missingActions.length > 0) { + missingActionsWarning = "// TODO: define a role(s) with the permission to do " + missingActions.join(', ') + entityInstance + " and uncomment the test(s) below" +} +_%> +<%_ if (missingActionsWarning) { _%> +// +<%= missingActionsWarning %> +// + +<%_ } _%> +<%_ if (entityAbsolutePackage !== packageName) { _%> +import <%= packageName %>.web.rest.TestUtil; +<%_ } _%> +import <%= packageName %>.IntegrationTest; +import <%= entityAbsolutePackage %>.domain.<%= persistClass %>; +<%_ +var imported = []; +for (relationship of relationships) { // import entities in required relationships + const relationshipValidate = relationship.relationshipValidate; + const otherEntityNameCapitalized = relationship.otherEntityNameCapitalized; + const isUsingMapsIdL1 = relationship.id; + if (imported.indexOf(otherEntityNameCapitalized) === -1) { + if ((relationshipValidate !== null && relationshipValidate === true) || jpaMetamodelFiltering || (isUsingMapsIdL1 === true)) { _%> +import <%= entityAbsolutePackage %>.domain.<%= relationship.otherEntity.persistClass %>; +<%_ + imported.push(otherEntityNameCapitalized); + } + } +} _%> +<%_ +if(jpaMetamodelFiltering && reactive) { + filterTestableRelationships.forEach(relationship => { _%> +import <%= entityAbsolutePackage %>.repository.<%= relationship.otherEntityNameCapitalized %>Repository; +<%_ }); + } _%> +<%_ if (saveUserSnapshot) { _%> +import <%= entityAbsolutePackage %>.repository.UserRepository; +<%_ } _%> +import <%= entityAbsolutePackage %>.repository.<%= entityClass %>Repository; +<%_ if (databaseTypeSql && reactive) { _%> +import <%= packageName %>.repository.EntityManager; +<%_ } _%> +<%_ if (isUsingMapsId && (!dtoMapstruct && serviceNo)) { _%> +import <%= entityAbsolutePackage %>.repository.<%= mapsIdAssoc.otherEntityNameCapitalized %>Repository; +<%_ } _%> +<%_ if (searchEngineElasticsearch) { _%> +import <%= entityAbsolutePackage %>.repository.search.<%= entityClass %>SearchRepository; +<%_ } _%> +<%_ if (!serviceNo && implementsEagerLoadApis) { _%> +import <%= entityAbsolutePackage %>.service.<%= entityClass %>Service; +<%_ } _%> +<%_ if (dtoMapstruct) { _%> +import <%= entityAbsolutePackage %>.service.dto.<%= dtoClass %>; +import <%= entityAbsolutePackage %>.service.mapper.<%= entityClass %>Mapper; +<%_ } _%> +<%_ if (jpaMetamodelFiltering && !reactive) { _%> +import <%= entityAbsolutePackage %>.service.criteria.<%= entityClass %>Criteria; +<%_ } _%> +<%_ if (searchEngineElasticsearch) { _%> +import org.assertj.core.util.IterableUtil; +import org.apache.commons.collections4.IterableUtils; +import java.util.concurrent.TimeUnit; +<%_ } _%> +<%_ if ((databaseTypeSql && reactive) || searchEngineElasticsearch) { _%> +import org.junit.jupiter.api.AfterEach; +<%_ } _%> +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +<%_ if (implementsEagerLoadApis || databaseTypeNeo4j) { _%> +import org.mockito.Mock; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +<%_ } _%> +import org.springframework.beans.factory.annotation.Autowired; +<%_ if (reactive) { _%> +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +<%_ } else { _%> +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +<%_ } _%> +<%_ if (searchEngineElasticsearch && !paginationNo || implementsEagerLoadApis) { _%> +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.PageRequest; +<%_ } _%> +import org.springframework.http.MediaType; +<%_ if (searchEngineCouchbase) { _%> +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Timeout; +<%_ } _%> +<%_ if (databaseTypeCouchbase) { _%> +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.TestSecurityContextHolder; +<%_ } _%> +import org.springframework.security.test.context.support.WithMockUser; +<%_ if (reactive) { _%> +import org.springframework.test.web.reactive.server.WebTestClient; +<%_ } _%> +<%_ if (!reactive) { _%> +import org.springframework.test.web.servlet.MockMvc; + <%_ if (databaseTypeSql) { _%> +import org.springframework.transaction.annotation.Transactional; + <%_ } _%> +<%_ } _%> +<%_ if (anyFieldIsBlobDerived) { _%> +import org.springframework.util.Base64Utils; +<%_ } _%> +<%_ if (reactive && (implementsEagerLoadApis || searchEngineElasticsearch)) { _%> +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +<%_ } _%> +<%_ if (databaseTypeSql && !reactive) { _%> +import jakarta.persistence.EntityManager; +<%_ } _%> +<%_ if (anyFieldIsBigDecimal) { _%> +import java.math.BigDecimal; +<%_ } _%> +<%_ if (anyFieldIsBlobDerived && databaseTypeCassandra) { _%> +import java.nio.ByteBuffer; +<%_ } _%> +<%_ if (reactive || anyFieldIsDuration) { _%> +import java.time.Duration; +<%_ } _%> +<%_ if (anyFieldIsLocalDate) { _%> +import java.time.LocalDate; +<%_ } _%> +<%_ if (anyFieldIsInstant || anyFieldIsZonedDateTime) { _%> +import java.time.Instant; +<%_ } _%> +<%_ if (anyFieldIsZonedDateTime) { _%> +import java.time.ZonedDateTime; +import java.time.ZoneOffset; +<%_ } _%> +<%_ if (anyFieldIsLocalDate || anyFieldIsZonedDateTime) { _%> +import java.time.ZoneId; +<%_ } _%> +<%_ if (anyFieldIsInstant) { _%> +import java.time.temporal.ChronoUnit; +<%_ } _%> +<%_ if (!reactive && implementsEagerLoadApis) { _%> +import java.util.ArrayList; +<%_ } _%> +<%_ if (searchEngineElasticsearch && !reactive) { _%> +import java.util.Collections; + <%_ if (paginationNo) { _%> +import java.util.stream.Stream; + <%_ } _%> +<%_ } _%> +import java.util.List; +<%_ if (anyFieldIsUUID || primaryKey.typeString || otherEntityPrimaryKeyTypesIncludesUUID) { _%> +import java.util.UUID; +<%_ } _%> +<%_ if (!embedded && (primaryKey.hasLong || primaryKey.hasInteger)) { _%> +import java.util.Random; + <%_ if (primaryKey.hasLong) { _%> +import java.util.concurrent.atomic.AtomicLong; + <%_ } else if (primaryKey.hasInteger) { _%> +import java.util.concurrent.atomic.AtomicInteger; + <%_ } _%> +<%_ } _%> + +<%_ if (anyFieldIsBigDecimal) { _%> +import static <%= packageName %>.web.rest.TestUtil.sameNumber; +<%_ } _%> +<%_ if (anyFieldIsZonedDateTime) { _%> +import static <%= packageName %>.web.rest.TestUtil.sameInstant; +<%_ } _%> +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +<%_ if (reactive) { _%> +import static org.hamcrest.Matchers.is; +<%_ } _%> +<%_ if (authenticationUsesCsrf) { _%> + <%_ if (reactive) { _%> +import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; + <%_ } else { _%> +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; + <%_ } _%> +<%_ } _%> +<%_ if (searchEngineElasticsearch || implementsEagerLoadApis) { _%> +import static org.mockito.Mockito.*; +<%_ } _%> +<%_ if (!reactive) { _%> +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +<%_ } _%> +<%_ if (searchEngineElasticsearch) { _%> +import static org.awaitility.Awaitility.await; +<%_ } _%> +<%_ for (const field of fields.filter(field => !field.transient)) { + if (field.fieldIsEnum) { _%> +import <%= entityAbsolutePackage %>.domain.enumeration.<%= field.fieldType %>; +<%_ } +} _%> +/** + * Integration tests for the {@link <%= entityClass %>Resource} REST controller. + */ +@IntegrationTest +<%_ if (implementsEagerLoadApis) { _%> +@ExtendWith(MockitoExtension.class) +<%_ } _%> +<%_ if (reactive) { _%> +@AutoConfigureWebTestClient(timeout = IntegrationTest.DEFAULT_ENTITY_TIMEOUT) +<%_ } else { _%> +@AutoConfigureMockMvc +<%_ } _%> +@WithMockUser +class <%= entityClass %>ResourceIT { +<%_ +for (field of fieldsToTest) { +const defaultValueName = 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase(); +const updatedValueName = 'UPDATED_' + field.fieldNameUnderscored.toUpperCase(); +const smallerValueName = 'SMALLER_' + field.fieldNameUnderscored.toUpperCase(); +const needsSmallerValueName = jpaMetamodelFiltering && field.filterableField + && (field.fieldTypeNumeric || field.fieldTypeDuration || field.fieldTypeLocalDate || field.fieldTypeZonedDateTime); + +let defaultValue = 1; +let updatedValue = 2; + +if (field.fieldValidate === true) { + if (field.fieldValidationMax) { + defaultValue = field.fieldValidateRulesMax; + updatedValue = parseInt(field.fieldValidateRulesMax) - 1; + } + if (field.fieldValidationMin) { + defaultValue = field.fieldValidateRulesMin; + updatedValue = parseInt(field.fieldValidateRulesMin) + 1; + } + if (field.fieldValidationMinBytes) { + defaultValue = field.fieldValidateRulesMinbytes; + updatedValue = field.fieldValidateRulesMinbytes; + } + if (field.fieldValidationMaxBytes) { + updatedValue = field.fieldValidateRulesMaxbytes; + } +} + +const fieldType = field.fieldType; +const isEnum = field.fieldIsEnum; +let enumValue1; +let enumValue2; +if (isEnum) { + const enumValues = field.enumValues; + enumValue1 = enumValues[0]; + if (enumValues.length > 1) { + enumValue2 = enumValues[1]; + } else { + enumValue2 = enumValue1; + } +} + +if (field.fieldTypeString || field.blobContentTypeText) { + // Generate Strings, using the min and max string length if they are configured + let sampleTextString = ""; + let updatedTextString = ""; + let sampleTextLength = 10; + if (field.fieldValidateRulesMinlength > sampleTextLength) { + sampleTextLength = field.fieldValidateRulesMinlength; + } + if (field.fieldValidateRulesMaxlength < sampleTextLength) { + sampleTextLength = field.fieldValidateRulesMaxlength; + } + for (let i = 0; i < sampleTextLength; i++) { + sampleTextString += "A"; + updatedTextString += "B"; + } + if (field.fieldValidateRulesPattern !== undefined) { + // Generate Strings, using pattern + try { + const patternRegExp = new RegExp(field.fieldValidateRulesPattern); + const randExp = field.createRandexp(); + // set infinite repetitions max range + if (!patternRegExp.test(sampleTextString.replace(/\\"/g, '"').replace(/\\\\/g, '\\'))) { + sampleTextString = randExp.gen().replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + } + if (!patternRegExp.test(updatedTextString.replace(/\\"/g, '"').replace(/\\\\/g, '\\'))) { + updatedTextString = randExp.gen().replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + } + } catch (error) { + log(this.chalkRed('Error generating test value for entity "' + entityClass + + '" field "' + field.fieldName + '" with pattern "' + field.fieldValidateRulesPattern + + '", generating default values for this field. Detailed error message: "' + error.message + '".')); + } + if (sampleTextString === updatedTextString) { + updatedTextString = updatedTextString + "B"; + log(this.chalkRed('Randomly generated first and second test values for entity "' + entityClass + + '" field "' + field.fieldName + '" with pattern "' + field.fieldValidateRulesPattern + + '" in file "' + entityClass + 'ResourceIT" where equal, added symbol "B" to second value.')); + } + } _%> + + private static final String <%= defaultValueName %> = "<%- sampleTextString %>"; + private static final String <%= updatedValueName %> = "<%- updatedTextString %>"; + <%_ } else if (field.fieldTypeInteger) { _%> + + private static final Integer <%= defaultValueName %> = <%= defaultValue %>; + private static final Integer <%= updatedValueName %> = <%= updatedValue %>; + <%_ if (needsSmallerValueName) { _%> + private static final Integer <%= smallerValueName %> = <%= defaultValue %> - 1; + <%_ } _%> + <%_ } else if (field.fieldTypeLong) { _%> + + private static final Long <%= defaultValueName %> = <%= defaultValue %>L; + private static final Long <%= updatedValueName %> = <%= updatedValue %>L; + <%_ if (needsSmallerValueName) { _%> + private static final Long <%= smallerValueName %> = <%= defaultValue %>L - 1L; + <%_ } _%> + <%_ } else if (field.fieldTypeFloat) { _%> + + private static final <%= fieldType %> <%= defaultValueName %> = <%= defaultValue %>F; + private static final <%= fieldType %> <%= updatedValueName %> = <%= updatedValue %>F; + <%_ if (needsSmallerValueName) { _%> + private static final <%= fieldType %> <%= smallerValueName %> = <%= defaultValue %>F - 1F; + <%_ } _%> + <%_ } else if (field.fieldTypeDouble) { _%> + + private static final <%= fieldType %> <%= defaultValueName %> = <%= defaultValue %>D; + private static final <%= fieldType %> <%= updatedValueName %> = <%= updatedValue %>D; + <%_ if (needsSmallerValueName) { _%> + private static final <%= fieldType %> <%= smallerValueName %> = <%= defaultValue %>D - 1D; + <%_ } _%> + <%_ } else if (field.fieldTypeBigDecimal) { _%> + + private static final BigDecimal <%= defaultValueName %> = new BigDecimal(<%= defaultValue %>); + private static final BigDecimal <%= updatedValueName %> = new BigDecimal(<%= updatedValue %>); + <%_ if (needsSmallerValueName) { _%> + private static final BigDecimal <%= smallerValueName %> = new BigDecimal(<%= defaultValue %> - 1); + <%_ } _%> + <%_ } else if (field.fieldTypeUUID) { _%> + + private static final UUID <%= defaultValueName %> = UUID.randomUUID(); + private static final UUID <%= updatedValueName %> = UUID.randomUUID(); + <%_ } else if (field.fieldTypeLocalDate) { _%> + + private static final LocalDate <%= defaultValueName %> = LocalDate.ofEpochDay(0L); + private static final LocalDate <%= updatedValueName %> = LocalDate.now(ZoneId.systemDefault()); + <%_ if (needsSmallerValueName) { _%> + private static final LocalDate <%= smallerValueName %> = LocalDate.ofEpochDay(-1L); + <%_ } _%> + <%_ } else if (field.fieldTypeInstant) { _%> + + private static final Instant <%= defaultValueName %> = Instant.ofEpochMilli(0L); + private static final Instant <%= updatedValueName %> = Instant.now().truncatedTo(ChronoUnit.MILLIS); + <%_ if (needsSmallerValueName) { _%> + private static final Instant <%= smallerValueName %> = Instant.ofEpochMilli(-1L); + <%_ } _%> + <%_ } else if (field.fieldTypeZonedDateTime) { _%> + + private static final ZonedDateTime <%= defaultValueName %> = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); + private static final ZonedDateTime <%= updatedValueName %> = ZonedDateTime.now(ZoneId.systemDefault()).withNano(0); + <%_ if (needsSmallerValueName) { _%> + private static final ZonedDateTime <%= smallerValueName %> = ZonedDateTime.ofInstant(Instant.ofEpochMilli(-1L), ZoneOffset.UTC); + <%_ } _%> + <%_ } else if (field.fieldTypeDuration) { _%> + + private static final Duration <%= defaultValueName %> = Duration.ofHours(6); + private static final Duration <%= updatedValueName %> = Duration.ofHours(12); + <%_ if (needsSmallerValueName) { _%> + private static final Duration <%= smallerValueName %> = Duration.ofHours(5); + <%_ } _%> + <%_ } else if (field.fieldTypeBoolean) { _%> + + private static final Boolean <%= defaultValueName %> = false; + private static final Boolean <%= updatedValueName %> = true; + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + + <%_ if (!databaseTypeCassandra) { _%> + private static final byte[] <%= defaultValueName %> = TestUtil.createByteArray(1, "0"); + private static final byte[] <%= updatedValueName %> = TestUtil.createByteArray(1, "1"); + <%_ } else { _%> + private static final ByteBuffer <%= defaultValueName %> = ByteBuffer.wrap(TestUtil.createByteArray(1, "0")); + private static final ByteBuffer <%= updatedValueName %> = ByteBuffer.wrap(TestUtil.createByteArray(1, "1")); + <%_ } _%> + private static final String <%= defaultValueName %>_CONTENT_TYPE = "image/jpg"; + private static final String <%= updatedValueName %>_CONTENT_TYPE = "image/png"; + <%_ } else if (isEnum) { _%> + + private static final <%= fieldType %> <%= defaultValueName %> = <%= fieldType %>.<%= enumValue1.name %>; + private static final <%= fieldType %> <%= updatedValueName %> = <%= fieldType %>.<%= enumValue2.name %>; + <%_ } +} _%> + + private static final String ENTITY_API_URL = "/api/<%= entityApiUrl %>"; + private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{<%= primaryKey.name %>}"; +<%_ if (searchEngineAny) { _%> + private static final String ENTITY_SEARCH_API_URL = "/api/<%= entityApiUrl %>/_search"; +<%_ } _%> +<%_ if (!embedded && (primaryKey.hasLong || primaryKey.hasInteger)) { _%> + + private static Random random = new Random(); + <%_ if (primaryKey.hasLong) { _%> + private static AtomicLong longCount = new AtomicLong(random.nextInt() + ( 2 * Integer.MAX_VALUE )); + <%_ } else if (primaryKey.hasInteger) { _%> + private static AtomicInteger intCount = new AtomicInteger(random.nextInt() + ( 2 * Short.MAX_VALUE )); + <%_ } _%> +<%_ } _%> + + @Autowired + private <%= entityClass %>Repository <%= entityInstance %>Repository; +<%_ if (isUsingMapsId && (!dtoMapstruct && serviceNo)) { _%> + @Autowired + private <%= mapsIdEntity %>Repository <%= mapsIdRepoInstance %>; +<%_ } _%> +<%_ if (saveUserSnapshot) { _%> + + @Autowired + private UserRepository userRepository; +<%_ } _%> +<%_ if (implementsEagerLoadApis) { _%> + + @Mock + private <%= entityClass %>Repository <%= entityInstance %>RepositoryMock; +<%_ } _%> +<%_ if (dtoMapstruct) { _%> + + @Autowired + private <%= entityClass %>Mapper <%= entityInstance %>Mapper; +<%_ } if (!serviceNo) { _%> + <%_ if (implementsEagerLoadApis) { _%> + + @Mock + private <%= entityClass %>Service <%= entityInstance %>ServiceMock; + <%_ } _%> +<%_ } if (searchEngineElasticsearch) { _%> + + @Autowired + private <%= entityClass %>SearchRepository <%= entityInstance %>SearchRepository; +<%_ } _%> +<%_ if (databaseTypeSql) { _%> + + @Autowired + private EntityManager em; +<%_ } _%> + + @Autowired +<%_ if (reactive) { _%> + private WebTestClient webTestClient; +<%_ } else { _%> + private MockMvc rest<%= entityClass %>MockMvc; +<%_ } _%> + + private <%= persistClass %> <%= persistInstance %>; + +<%_ if(jpaMetamodelFiltering && reactive) { +filterTestableRelationships.forEach((relationship) => { _%> + @Autowired + private <%= relationship.otherEntity.persistClass %>Repository <%= relationship.otherEntity.persistInstance %>Repository; +<%_ }); +}_%> +<%_ ['DEFAULT_', 'UPDATED_'].forEach((fieldStatus) => { _%> + /** + * Create an <% if (fieldStatus === 'UPDATED_') { %>updated <% } %>entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static <%= persistClass %> create<% if (fieldStatus === 'UPDATED_') { _%>Updated<%_ } %>Entity(<% if (databaseTypeSql) { %>EntityManager em<% } %>) { + <%_ if (fluentMethods) { _%> + <%= persistClass %> <%= persistInstance %> = new <%= persistClass %>()<%_ if (reactive && databaseTypeSql && primaryKey.typeUUID && !isUsingMapsId) { _%> + .<%= primaryKey.name %>(UUID.randomUUID()) + <%_ } _%><% for (field of fieldsToTest) { %> + .<%= field.fieldName %>(<%= fieldStatus + field.fieldNameUnderscored.toUpperCase() %>)<% if (field.fieldTypeBinary && !field.blobContentTypeText) { %> + .<%= field.fieldName %>ContentType(<%= fieldStatus + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE)<% } %><% } %>; + <%_ } else { _%> + <%= persistClass %> <%= persistInstance %> = new <%= persistClass %>(); + <%_ if (reactive && databaseTypeSql && primaryKey.typeUUID && !isUsingMapsId) { _%> + <%= persistInstance %>.set<%= primaryKey.fields[0].fieldInJavaBeanMethod %>(UUID.randomUUID()); + <%_ } _%> + <%_ for (field of fieldsToTest) { _%> + <%= persistInstance %>.set<%= field.fieldInJavaBeanMethod %>(<%= fieldStatus + field.fieldNameUnderscored.toUpperCase() %>); + <%_ if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= persistInstance %>.set<%= field.fieldInJavaBeanMethod %>ContentType(<%= fieldStatus + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE); + <%_ } _%> + <%_ } _%> + <%_ } _%> + <%_ + const alreadyGeneratedEntities = []; + for (relationship of relationships) { + const relationshipValidate = relationship.relationshipValidate; + const otherEntityName = relationship.otherEntityName; + const otherEntityNameCapitalized = relationship.otherEntityNameCapitalized; + const relationshipNameCapitalizedPlural = relationship.relationshipNameCapitalizedPlural; + const relationshipNameCapitalized = relationship.relationshipNameCapitalized; + const mapsIdUse = relationship.id; + if ((relationshipValidate !== null && relationshipValidate) || mapsIdUse) { _%> + // Add required entity + <%_ if (alreadyGeneratedEntities.indexOf(otherEntityName) == -1) { _%> + <%_ if (relationship.otherEntityUser) { /* TODO or other entity has no unique fields */ _%> + <%= relationship.otherEntity.persistClass %> <%= otherEntityName %> = <%= createEntityPrefix %><%= otherEntityNameCapitalized %>ResourceIT.createEntity(<% if (databaseTypeSql) { %>em<% } %>)<%= createEntityPostfix %>; + <%_ if (databaseTypeSql && !reactive) { _%> + em.persist(<%= otherEntityName %>); + em.flush(); + <%_ } _%> + <%_ if (databaseTypeMongodb) { _%> + <%= otherEntityName %>.set<%= primaryKey.nameCapitalized %>("fixed-id-for-tests"); + <%_ } _%> + <%_ } else { _%> + <%= relationship.otherEntity.persistClass %> <%= otherEntityName %>; + <%_ if (databaseTypeSql && !reactive) { _%> + <%_ if (!isUsingMapsId || fieldStatus !== "UPDATED_") { _%> + if (TestUtil.findAll(em, <%= relationship.otherEntity.persistClass %>.class).isEmpty()) { + <%_ } _%> + <%= otherEntityName %> = <%= createEntityPrefix %><%= otherEntityNameCapitalized %>ResourceIT.create<% if (fieldStatus === 'UPDATED_') { %>Updated<% } %>Entity(em)<%= createEntityPostfix %>; + em.persist(<%= otherEntityName %>); + em.flush(); + <%_ if (!isUsingMapsId || fieldStatus !== "UPDATED_") { _%> + } else { + <%= otherEntityName %> = TestUtil.findAll(em, <%= relationship.otherEntity.persistClass %>.class).get(0); + } + <%_ } _%> + <%_ } else { _%> + <%= otherEntityName %> = <%= createEntityPrefix %><%= otherEntityNameCapitalized %>ResourceIT.create<% if (fieldStatus === 'UPDATED_') { %>Updated<% } %>Entity(<% if (databaseType === 'sql') { %>em<% } %>)<%= createEntityPostfix %>; + <%_ } _%> + <%_ if (databaseTypeMongodb) { _%> + <%= otherEntityName %>.set<%= primaryKey.nameCapitalized %>("fixed-id-for-tests"); + <%_ } _%> + <%_ } _%> + <%_ } _%> + <%_ if (relationship.relationshipManyToMany || relationship.relationshipOneToMany) { _%> + <%= persistInstance %>.get<%= relationshipNameCapitalizedPlural %>().add(<%= otherEntityName %>); + <%_ } else { _%> + <%= persistInstance %>.set<%= relationshipNameCapitalized %>(<%= otherEntityName %>); + <%_ } _%> + <%_ alreadyGeneratedEntities.push(otherEntityName) _%> + <%_ } _%> + <%_ } _%> + return <%= persistInstance %>; + } +<%_ }); _%> + +<%_ if (databaseTypeSql && reactive) { + const alreadyGeneratedDeletionCalls = []; +_%> + public static void deleteEntities(EntityManager em) { + try { + <%_ relationships.forEach(function(rel) { + if (rel.shouldWriteJoinTable) { _%> + em.deleteAll("<%= rel.joinTable.name %>").block(); + <%_ } _%> + <%_ }); _%> + em.deleteAll(<%= persistClass %>.class).block(); + } catch (Exception e) { + // It can fail, if other entities are still referring this - it will be removed later. + } + <%_ relationships.forEach(function(rel) { + if ((rel.relationshipValidate || rel.id) && !alreadyGeneratedDeletionCalls.includes(rel.otherEntityName)) { _%> + <%= rel.otherEntityNameCapitalized %>ResourceIT.deleteEntities(em); + <%_ alreadyGeneratedDeletionCalls.push(rel.otherEntityName); + } + }); _%> + } + + @AfterEach + public void cleanup() { + deleteEntities(em); + } + +<%_ } _%> +<%_ if (searchEngineElasticsearch) { _%> + @AfterEach + public void cleanupElasticSearchRepository() { + <%= entityInstance %>SearchRepository.deleteAll()<%= callBlock %>; + assertThat(<%= entityInstance %>SearchRepository.count()<%= callBlock %>).isEqualTo(0); + } + +<%_ } _%> +<%_ if (reactive && authenticationUsesCsrf) { _%> + @BeforeEach + public void setupCsrf() { + webTestClient = webTestClient.mutateWith(csrf()); + } + +<%_ } _%> + @BeforeEach + public void initTest() { +<%_ if (databaseTypeMongodb || databaseTypeCouchbase || databaseTypeCassandra || databaseTypeNeo4j) { _%> + <%= entityInstance %>Repository.deleteAll()<%= callBlock %>; +<%_ } else if (databaseTypeSql && reactive) { _%> + deleteEntities(em); +<%_ } _%> + <%= persistInstance %> = createEntity(<% if (databaseTypeSql) { %>em<% } %>); + } +<%_ if (!readOnly) { _%> + + void create<%= entityClass %>IsAllowed() throws Exception { + int databaseSizeBeforeCreate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%_ + // overwrite the id field again with null + // the create method here is supposed to be used for other tests as well, + // which may expect an id to be set (at least in the reactive stack) + if (reactive && databaseTypeSql && primaryKey.typeUUID && !isUsingMapsId) { _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(null); + <%_ } _%> + // Create the <%= entityClass %> + <%_ if (dtoMapstruct) { _%> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + <%_ } _%> + <%_ if (reactive) { _%> + webTestClient.post().uri(ENTITY_API_URL) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isCreated(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(post(ENTITY_API_URL)<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isCreated()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeCreate + 1); + <%_ if (searchEngineElasticsearch) { _%> + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore + 1); + }); + <%_ } _%> + <%= persistClass %> test<%= entityClass %> = <%= entityInstance %>List.get(<%= entityInstance %>List.size() - 1); + <%_ for (const field of fieldsToTest) { + if (field.fieldTypeZonedDateTime) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>ContentType()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE); + <%_ } else if (field.fieldTypeBigDecimal) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualByComparingTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } + } _%> + <%_ if (isUsingMapsId) { _%> + + // Validate the id for MapsId, the ids must be same + assertThat(test<%= entityClass %>.get<%= primaryKey.nameCapitalized %>()).isEqualTo(<%_ if (dtoMapstruct) { _%><%= dtoInstance %><%_ } else { _%>test<%= entityClass %><%_ } _%>.get<%= mapsIdEntity %>().get<%= primaryKey.nameCapitalized %>()); + <%_ } _%> + } + <%_ allowedPostRoles.forEach(secRole => { _%> + + <%= postTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void create<%= entityClass %>_with_<%= secRole %>() throws Exception { + create<%= entityClass %>IsAllowed(); + } + <%_ }); _%> + + void create<%= entityClass %>IsForbidden() throws Exception { + int databaseSizeBeforeCreate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%_ + // overwrite the id field again with null + // the create method here is supposed to be used for other tests as well, + // which may expect an id to be set (at least in the reactive stack) + if (reactive && databaseTypeSql && primaryKey.typeUUID && !isUsingMapsId) { _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(null); + <%_ } _%> + // Create the <%= entityClass %> + <%_ if (dtoMapstruct) { _%> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + <%_ } _%> + <%_ if (reactive) { _%> + webTestClient.post().uri(ENTITY_API_URL) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isForbidden(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(post(ENTITY_API_URL)<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isForbidden()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeCreate); + <%_ if (searchEngineElasticsearch) { _%> + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + }); + <%_ } _%> + } + <%_ security.forbiddenRoles.post.forEach(secRole => { _%> + + @Test<%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void create<%= entityClass %>_with_<%= secRole %>() throws Exception { + create<%= entityClass %>IsForbidden(); + } + <%_ }); _%> + + void create<%= entityClass %>WithExistingId() throws Exception { + // Create the <%= entityClass %> with an existing ID + <%_ if (primaryKey.typeUUID && databaseTypeSql) { _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ } else { _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<% if (primaryKey.typeUUID) { %>UUID.randomUUID()<% } else if (primaryKey.typeLong) { %>1L<% } else if (primaryKey.typeInteger) { %>1<% } else { %>"existing_id"<% } %>); + <%_ } _%> + <%_ if (dtoMapstruct) { _%> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + <%_ } _%> + + int databaseSizeBeforeCreate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + + // An entity with an existing ID cannot be created, so this API call must fail + <%_ if (reactive) { _%> + webTestClient.post().uri(ENTITY_API_URL) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isBadRequest(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(post(ENTITY_API_URL)<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isBadRequest()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeCreate); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + } + + <%= getTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void create<%= entityClass %>WithExistingId_with_<%= getDefaultRole %>() throws Exception { + create<%= entityClass %>WithExistingId(); + } + + <%_ if (databaseTypeSql && isUsingMapsId) { _%> + void update<%= entityClass %>MapsIdAssociationWithNewIdIsAllowed() throws Exception { + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ let alreadyGeneratedEntities = []; _%> + int databaseSizeBeforeCreate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%_ for (relationship of relationships) { + const otherEntityName = relationship.otherEntityName; + const otherEntityNameCapitalized = relationship.otherEntityNameCapitalized; + const mapsIdUse = relationship.id; + if (mapsIdUse) { _%> + // Add a new parent entity + <%_ if (alreadyGeneratedEntities.indexOf(otherEntityName) == -1) { _%> + <%= relationship.otherEntity.persistClass %> <%= otherEntityName %> = <%= otherEntityNameCapitalized %>ResourceIT.create<% if (!relationship.otherEntityUser) { _%>Updated<%_ } %>Entity(<% if (databaseTypeSql) { %>em<% } %>); + <%_ if (databaseTypeSql && !reactive) { _%> + em.persist(<%= otherEntityName %>); + em.flush(); + <%_ } _%> + <%_ } _%> + <%_ alreadyGeneratedEntities.push(otherEntityName) _%> + <%_ } _%> + <%_ break; } _%> + + // Load the <%= entityInstance %> + <%= persistClass %> updated<%= persistClass %> = <%= entityInstance %>Repository.findById(<%= persistInstance %>.get<%= primaryKey.nameCapitalized %>())<%= reactive ? '.block()' : '.orElseThrow()' %>; + assertThat(updated<%= persistClass %>).isNotNull(); + <%_ if (databaseTypeSql && !reactive) { _%> + // Disconnect from session so that the updates on updated<%= persistClass %> are not directly saved in db + em.detach(updated<%= persistClass %>); + <%_ } _%> + + // Update the <%= mapsIdEntity %> with new association value + updated<%= persistClass %>.set<%= mapsIdEntity %>(<%= alreadyGeneratedEntities.pop() %>); + <%_ if (dtoMapstruct) { _%> + <%= dtoClass %> updated<%= dtoClass %> = <%= entityInstance %>Mapper.toDto(updated<%= persistClass %>); + assertThat(updated<%= dtoClass %>).isNotNull(); + <%_ } _%> + + // Update the entity + <%_ if (reactive) { _%> + webTestClient.put().uri(ENTITY_API_URL_ID, <%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>.get<%= primaryKey.nameCapitalized %>()) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>)) + .exchange() + .expectStatus().isOk(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(put(ENTITY_API_URL_ID, <%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>.get<%= primaryKey.nameCapitalized %>())<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>))) + .andExpect(status().isOk()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeCreate); + <%= persistClass %> test<%= entityClass %> = <%= entityInstance %>List.get(<%= entityInstance %>List.size() - 1); + + // Validate the id for MapsId, the ids must be same + // Uncomment the following line for assertion. However, please note that there is a known issue and uncommenting will fail the test. + // Please look at https://github.com/jhipster/generator-jhipster/issues/9100. You can modify this test as necessary. + // assertThat(test<%= entityClass %>.get<%= primaryKey.nameCapitalized %>()).isEqualTo(test<%= entityClass %>.get<%= mapsIdEntity %>().get<%= primaryKey.nameCapitalized %>()); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + } + <%_ allowedPutRoles.forEach(secRole => { _%> + + <%= putTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void update<%= entityClass %>MapsIdAssociationWithNewId_with_<%= secRole %>() throws Exception { + update<%= entityClass %>MapsIdAssociationWithNewIdIsAllowed(); + } + <%_ }); _%> + + void update<%= entityClass %>MapsIdAssociationWithNewIdIsForbidden() throws Exception { + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ alreadyGeneratedEntities = []; _%> + int databaseSizeBeforeCreate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%_ for (relationship of relationships) { + const otherEntityName = relationship.otherEntityName; + const otherEntityNameCapitalized = relationship.otherEntityNameCapitalized; + const mapsIdUse = relationship.id; + if (mapsIdUse) { _%> + // Add a new parent entity + <%_ if (alreadyGeneratedEntities.indexOf(otherEntityName) == -1) { _%> + <%= relationship.otherEntity.persistClass %> <%= otherEntityName %> = <%= otherEntityNameCapitalized %>ResourceIT.create<% if (!relationship.otherEntityUser) { _%>Updated<%_ } %>Entity(<% if (databaseTypeSql) { %>em<% } %>); + <%_ if (databaseTypeSql && !reactive) { _%> + em.persist(<%= otherEntityName %>); + em.flush(); + <%_ } _%> + <%_ } _%> + <%_ alreadyGeneratedEntities.push(otherEntityName) _%> + <%_ } _%> + <%_ break; } _%> + + // Load the <%= entityInstance %> + <%= persistClass %> updated<%= persistClass %> = <%= entityInstance %>Repository.findById(<%= persistInstance %>.get<%= primaryKey.nameCapitalized %>())<%= reactive ? '.block()' : '.orElseThrow()' %>; + assertThat(updated<%= persistClass %>).isNotNull(); + <%_ if (databaseTypeSql && !reactive) { _%> + // Disconnect from session so that the updates on updated<%= persistClass %> are not directly saved in db + em.detach(updated<%= persistClass %>); + <%_ } _%> + + // Update the <%= mapsIdEntity %> with new association value + updated<%= persistClass %>.set<%= mapsIdEntity %>(<%= alreadyGeneratedEntities.pop() %>); + <%_ if (dtoMapstruct) { _%> + <%= dtoClass %> updated<%= dtoClass %> = <%= entityInstance %>Mapper.toDto(updated<%= persistClass %>); + assertThat(updated<%= dtoClass %>).isNotNull(); + <%_ } _%> + + // Update the entity + <%_ if (reactive) { _%> + webTestClient.put().uri(ENTITY_API_URL_ID, <%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>.get<%= primaryKey.nameCapitalized %>()) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>)) + .exchange() + .expectStatus().isForbidden(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(put(ENTITY_API_URL_ID, <%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>.get<%= primaryKey.nameCapitalized %>())<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%_ if (dtoMapstruct) { _%>updated<%= dtoClass %> <%_ } else { _%> updated<%= persistClass %> <%_ } _%>))) + .andExpect(status().isForbidden()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeCreate); + <%= persistClass %> test<%= entityClass %> = <%= entityInstance %>List.get(<%= entityInstance %>List.size() - 1); + + // Validate the id for MapsId, the ids must be same + // Uncomment the following line for assertion. However, please note that there is a known issue and uncommenting will fail the test. + // Please look at https://github.com/jhipster/generator-jhipster/issues/9100. You can modify this test as necessary. + // assertThat(test<%= entityClass %>.get<%= primaryKey.nameCapitalized %>()).isEqualTo(test<%= entityClass %>.get<%= mapsIdEntity %>().get<%= primaryKey.nameCapitalized %>()); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + } + <%_ security.forbiddenRoles.put.forEach(secRole => { _%> + + @Test<%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void update<%= entityClass %>MapsIdAssociationWithNewId_with_<%= secRole %>() throws Exception { + update<%= entityClass %>MapsIdAssociationWithNewIdIsForbidden(); + } + <%_ }); _%> + + <%_ } _%> + <%_ for (field of fieldsToTest) { _%> + <%_ if (field.fieldValidate) { + let required = false; + if (!field.fieldTypeBytes && field.fieldValidate && field.fieldValidationRequired) { + required = true; + } _%> + <%_ if (required) { _%> + + void check<%= field.fieldInJavaBeanMethod %>IsRequired() throws Exception { + int databaseSizeBeforeTest = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + // set the field null + <%= persistInstance %>.set<%= field.fieldInJavaBeanMethod %>(null); + + // Create the <%= entityClass %>, which fails.<% if (dtoMapstruct) { %> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>);<% } %> + + + <%_ if (reactive) { _%> + webTestClient.post().uri(ENTITY_API_URL) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isBadRequest(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(post(ENTITY_API_URL)<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isBadRequest()); + <%_ } _%> + + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeTest); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + + } + + <%= postTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= postDefaultRole %>"}) + void check<%= field.fieldInJavaBeanMethod %>IsRequired_with_<%= postDefaultRole %>() throws Exception { + check<%= field.fieldInJavaBeanMethod %>IsRequired(); + } + <%_ } _%> + <%_ } _%> + <%_ } _%> +<%_ } _%> +<%_ if (!jpaMetamodelFiltering && reactive && paginationNo) { _%> + + void getAll<%= entityClassPlural %>AsStreamIsAllowed() { + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= entityInstance %>Repository.save(<%= persistInstance %>)<%= callBlock %>; + + List<<%= persistClass %>> <%= entityInstance %>List = webTestClient.get().uri(ENTITY_API_URL) + .accept(MediaType.APPLICATION_NDJSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentTypeCompatibleWith(MediaType.APPLICATION_NDJSON) + .returnResult(<%= restClass %>.class) + .getResponseBody() + <%_ if (dtoMapstruct) { _%> + .map(<%= entityInstance %>Mapper::toEntity) + <%_ } _%> + .filter(<%= persistInstance %>::equals) + .collectList() + .block(Duration.ofSeconds(5)); + + assertThat(<%= entityInstance %>List).isNotNull(); + assertThat(<%= entityInstance %>List).hasSize(1); + <%= persistClass %> test<%= entityClass %> = <%= entityInstance %>List.get(0); + <%_ for (const field of fieldsToTest) { + if (field.fieldTypeZonedDateTime) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else if ((field.fieldTypeBinary) && !field.blobContentTypeText) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>ContentType()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE); + <%_ } else if (field.fieldTypeBigDecimal) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualByComparingTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } + } _%> + } +<%_ allowedGetRoles.forEach(secRole => { _%> + + <%= getTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void get<%= entityClass %>AllAsStream_with_<%= secRole %>() throws Exception { + get<%= entityClass %>AllAsStreamIsAllowed(); + } +<%_ }); _%> + + void getAll<%= entityClassPlural %>AsStreamIsForbidden() { + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= entityInstance %>Repository.save(<%= persistInstance %>)<%= callBlock %>; + + List<<%= persistClass %>> <%= entityInstance %>List = webTestClient.get().uri(ENTITY_API_URL) + .accept(MediaType.APPLICATION_NDJSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentTypeCompatibleWith(MediaType.APPLICATION_NDJSON) + .returnResult(<%= restClass %>.class) + .getResponseBody() + <%_ if (dtoMapstruct) { _%> + .map(<%= entityInstance %>Mapper::toEntity) + <%_ } _%> + .filter(<%= persistInstance %>::equals) + .collectList() + .block(Duration.ofSeconds(5)); + + assertThat(<%= entityInstance %>List).isNotNull(); + assertThat(<%= entityInstance %>List).hasSize(1); + <%= persistClass %> test<%= entityClass %> = <%= entityInstance %>List.get(0); + <%_ for (const field of fieldsToTest) { + if (field.fieldTypeZonedDateTime) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else if ((field.fieldTypeBinary) && !field.blobContentTypeText) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>ContentType()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE); + <%_ } else if (field.fieldTypeBigDecimal) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualByComparingTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } + } _%> + } +<%_ security.forbiddenRoles.get.forEach(secRole => { _%> + + @Test<%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void get<%= entityClass %>AllAsStream_with_<%= secRole %>() throws Exception { + get<%= entityClass %>AllAsStreamIsForbidden(); + } +<%_ }); _%> + +<%_ } _%> + + void getAll<%= entityClassPlural %>IsAllowed() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database +<%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> +<%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List +<%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_API_URL + "?sort=<%= primaryKey.name %>,desc") + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody() +<%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL<% if (!databaseTypeCassandra) { %> + "?sort=<%= primaryKey.name %>,desc"<% } %>)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) +<%_ } _%> +<%_ if (databaseTypeSql || databaseTypeMongodb || databaseTypeCouchbase || databaseTypeCassandra) { _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.[*].<%= primaryKey.name %>").value(hasItem(<%= idValue %>))<%= !reactive ? ')' : '' %><%_ } _%><% for (field of fieldsToTest) { %> + <%_ if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.[*].<%= field.fieldName %>ContentType").value(hasItem(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE))<%= !reactive ? ')' : '' %> + <%_ } _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.[*].<%= field.fieldName %>").value(hasItem(<% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %>Base64Utils.encodeToString(<% } else + if (field.fieldTypeZonedDateTime) { %>sameInstant(<% } else + if (field.fieldTypeBigDecimal) { %>sameNumber(<% } %><%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %><% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %><% + if (databaseTypeCassandra) { %>.array()<% } %>)<% } else + if (field.fieldTypeInteger) { %><% } else + if (field.fieldTypeLong) { %>.intValue()<% } else + if (field.fieldTypeFloat || field.fieldTypeDouble) { %>.doubleValue()<% } else + if (field.fieldTypeBigDecimal) { %>)<% } else + if (field.fieldTypeBoolean) { %>.booleanValue()<% } else + if (field.fieldTypeZonedDateTime) { %>)<% } else + if (!field.fieldTypeString) { %>.toString()<% } %>))<%= !reactive ? ')' : '' %><%_ } _%>; + } +<%_ allowedGetRoles.forEach(secRole => { _%> + + <%= getTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void getAll<%= entityClassPlural %>IsAllowed_with_<%= secRole %>() throws Exception { + getAll<%= entityClassPlural %>IsAllowed(); + } +<%_ }); _%> + + void getAll<%= entityClassPlural %>IsForbidden() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database +<%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> +<%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List +<%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_API_URL + "?sort=<%= primaryKey.name %>,desc") + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isForbidden(); +<%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL<% if (!databaseTypeCassandra) { %> + "?sort=<%= primaryKey.name %>,desc"<% } %>)) + .andExpect(status().isForbidden()); +<%_ } _%> + } +<%_ security.forbiddenRoles.get.forEach(secRole => { _%> + + @Test<%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void getAll<%= entityClassPlural %>IsForbidden_with_<%= secRole %>() throws Exception { + getAll<%= entityClassPlural %>IsForbidden(); + } +<%_ }); _%> +<% if (implementsEagerLoadApis && !databaseTypeNeo4j && !databaseTypeCouchbase) { %> + + @SuppressWarnings({"unchecked"}) + void getAll<%= entityClassPlural %>WithEagerRelationshipsIsEnabled() <% if (!reactive) { %>throws Exception <% } %>{ + <%_ if (!serviceNo) { _%> + when(<%= entityInstance %>ServiceMock.findAllWithEagerRelationships(any())).thenReturn(<% if (reactive) { %>Flux.empty()<% } else { %>new PageImpl(new ArrayList<>())<% }%>); + <%_ } else { _%> + when(<%= entityInstance %>RepositoryMock.findAllWithEagerRelationships(any())).thenReturn(<% if (reactive) { %>Flux.empty()<% } else { %>new PageImpl(new ArrayList<>())<% }%>); + <%_ } _%> + + <%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_API_URL + "?eagerload=true") + .exchange() + .expectStatus().isOk(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL + "?eagerload=true")) + .andExpect(status().isOk()); + <%_ } _%> + + <%_ if (!serviceNo) { _%> + verify(<%= entityInstance %>ServiceMock, times(1)).findAllWithEagerRelationships(any()); + <%_ } else { _%> + verify(<%= entityInstance %>RepositoryMock, times(1)).findAllWithEagerRelationships(any()); + <%_ } _%> + } + + @SuppressWarnings({"unchecked"}) + void getAll<%= entityClassPlural %>WithEagerRelationshipsIsNotEnabled() <% if (!reactive) { %>throws Exception <% } %>{ + <%_ if (!serviceNo) { _%> + when(<%= entityInstance %>ServiceMock.findAllWithEagerRelationships(any())).thenReturn(<% if (reactive) { %>Flux.empty()<% } else { %>new PageImpl(new ArrayList<>())<% }%>); + <%_ } else { _%> + when(<%= entityInstance %>RepositoryMock.findAllWithEagerRelationships(any())).thenReturn(<% if (reactive) { %>Flux.empty()<% } else { %>new PageImpl(new ArrayList<>())<% }%>); + <%_ } _%> + + <%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_API_URL + "?eagerload=false") + .exchange() + .expectStatus().isOk(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL + "?eagerload=false")) + .andExpect(status().isOk()); + <%_ } _%> + verify(<%= entityInstance %>RepositoryMock, times(1)).findAll<% if (reactive) { %>WithEagerRelationships(any()<% } else { %>(any(Pageable.class)<% } %>); + } +<%_ } _%> + + void get<%= entityClass %>IsAllowed() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database +<%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> +<%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get the <%= entityInstance %> +<%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody() +<%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) +<%_ } _%> +<%_ if (databaseTypeSql || databaseTypeMongodb || databaseTypeCouchbase || databaseTypeCassandra) { _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.<%= primaryKey.name %>").value(<%= reactive ? 'is(' : '' %><%= idValue %>))<%_ } _%><% for (field of fieldsToTest) { %> + <%_ if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.<%= field.fieldName %>ContentType").value(<%= reactive ? 'is(' : '' %><%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE)) + <%_ } _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.<%= field.fieldName %>").value(<%= reactive ? 'is(' : '' %><% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %>Base64Utils.encodeToString(<% } else + if (field.fieldTypeZonedDateTime) { %>sameInstant(<% } else + if (field.fieldTypeBigDecimal) { %>sameNumber(<% } %><%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %><% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %><% + if (databaseTypeCassandra) { %>.array()<% } %>)<% } else + if (field.fieldTypeInteger) { %><% } else + if (field.fieldTypeLong) { %>.intValue()<% } else + if (field.fieldTypeFloat || field.fieldTypeDouble) { %>.doubleValue()<% } else + if (field.fieldTypeBigDecimal) { %>)<% } else + if (field.fieldTypeBoolean) { %>.booleanValue()<% } else + if (field.fieldTypeZonedDateTime) { %>)<% } else + if (!field.fieldTypeString) { %>.toString()<% } %>))<%_ } _%>; + } +<%_ allowedGetRoles.forEach(secRole => { _%> + + <%= getTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void get<%= entityClass %>IsAllowed_with_<%= secRole %>() throws Exception { + get<%= entityClass %>IsAllowed(); + } +<%_ }); _%> + + void get<%= entityClass %>IsForbidden() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database +<%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> +<%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get the <%= entityInstance %> +<%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isForbidden(); +<%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>())) + .andExpect(status().isForbidden()); +<%_ } _%> + } +<%_ security.forbiddenRoles.get.forEach(secRole => { _%> + + @Test<%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void get<%= entityClass %>IsForbidden_with_<%= secRole %>() throws Exception { + get<%= entityClass %>IsForbidden(); + } +<%_ }); _%> +<%_ if (jpaMetamodelFiltering) { _%> + + void get<%= entityClassPlural %>ByIdFiltering() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + <%= primaryKey.type %> id = <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>(); + + default<%= entityClass %>ShouldBeFound("<%= primaryKey.name %>.equals=" + id); + default<%= entityClass %>ShouldNotBeFound("<%= primaryKey.name %>.notEquals=" + id); + + <%_ if (primaryKey.typeLong || primaryKey.typeInteger) { _%> + default<%= entityClass %>ShouldBeFound("<%= primaryKey.name %>.greaterThanOrEqual=" + id); + default<%= entityClass %>ShouldNotBeFound("<%= primaryKey.name %>.greaterThan=" + id); + + default<%= entityClass %>ShouldBeFound("<%= primaryKey.name %>.lessThanOrEqual=" + id); + default<%= entityClass %>ShouldNotBeFound("<%= primaryKey.name %>.lessThan=" + id); + <%_ } _%> + } + + <%= getTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void get<%= entityClassPlural %>ByIdFiltering_with_<%= getDefaultRole %>() throws Exception { + get<%= entityClassPlural %>ByIdFiltering(); + } + + <%_ fieldsToTest.forEach((searchBy) => { /* we can't filter by all the fields. */_%> + <%_ if (searchBy.filterableField) { _%> + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>IsEqualToSomething() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> equals to <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.equals=" + <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> equals to <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.equals=" + <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + } + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>IsInShouldWork() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> in <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %> or <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.in=" + <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %> + "," + <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> equals to <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.in=" + <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + } + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>IsNullOrNotNull() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is not null + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.specified=true"); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is null + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.specified=false"); + } + <%_ } _%> + <%_ if (searchBy.fieldTypeString) { _%> + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>ContainsSomething() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> contains <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.contains=" + <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> contains <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.contains=" + <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + } + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>NotContainsSomething() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> does not contain <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.doesNotContain=" + <%= 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> does not contain <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.doesNotContain=" + <%= 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase() %>); + } + + <%_ } + // the range criteria + if (searchBy.fieldTypeNumeric || searchBy.fieldTypeDuration || searchBy.fieldTypeLocalDate || searchBy.fieldTypeZonedDateTime) { + var defaultValue = 'DEFAULT_' + searchBy.fieldNameUnderscored.toUpperCase(); + var biggerValue = 'UPDATED_' + searchBy.fieldNameUnderscored.toUpperCase(); + var smallerValue = 'SMALLER_' + searchBy.fieldNameUnderscored.toUpperCase(); + if (searchBy.fieldValidate && searchBy.fieldValidationMax) { + // if maximum is specified the updated variable is smaller than the default one! + if (searchBy.fieldTypeBigDecimal) { + biggerValue = '(' + defaultValue + '.add(BigDecimal.ONE))'; + } else if (searchBy.fieldTypeDuration || searchBy.fieldTypeLocalDate || searchBy.fieldTypeZonedDateTime) { + biggerValue = '(' + defaultValue + '.plus(1, ChronoUnit.DAYS))'; + } else { + biggerValue = '(' + defaultValue + ' + 1)'; + } + } + _%> + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>IsGreaterThanOrEqualToSomething() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is greater than or equal to <%= defaultValue %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.greaterThanOrEqual=" + <%= defaultValue %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is greater than or equal to <%= biggerValue %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.greaterThanOrEqual=" + <%= biggerValue %>); + } + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>IsLessThanOrEqualToSomething() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is less than or equal to <%= defaultValue %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.lessThanOrEqual=" + <%= defaultValue %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is less than or equal to <%= smallerValue %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.lessThanOrEqual=" + <%= smallerValue %>); + } + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>IsLessThanSomething() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is less than <%= defaultValue %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.lessThan=" + <%= defaultValue %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is less than <%= biggerValue %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.lessThan=" + <%= biggerValue %>); + } + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= searchBy.fieldInJavaBeanMethod %>IsGreaterThanSomething() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is greater than <%= defaultValue %> + default<%= entityClass %>ShouldNotBeFound("<%= searchBy.fieldName %>.greaterThan=" + <%= defaultValue %>); + + // Get all the <%= entityInstance %>List where <%= searchBy.fieldName %> is greater than <%= smallerValue %> + default<%= entityClass %>ShouldBeFound("<%= searchBy.fieldName %>.greaterThan=" + <%= smallerValue %>); + } + + <%_ } _%> + <%_ }); _%> + <%_ filterTestableRelationships.forEach((relationship) => { _%> + + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getAll<%= entityClassPlural %>By<%= relationship.relationshipNameCapitalized %>IsEqualToSomething() <% if (!reactive) { %>throws Exception <% } %>{ + <%_ if ((relationship.relationshipValidate && relationship.relationshipOneToOne) || relationship.id) { _%> + // Get already existing entity + <%= relationship.otherEntity.persistClass %> <%= relationship.relationshipFieldName %> = <%= persistInstance %>.get<%= relationship.relationshipNameCapitalized %>(); + <%_ } else { _%> + <%_ if (databaseTypeSql && !reactive) { _%> + <%= relationship.otherEntity.persistClass %> <%= relationship.relationshipFieldName %>; + if (TestUtil.findAll(em, <%= relationship.otherEntity.persistClass %>.class).isEmpty()) { + <%= entityInstance %>Repository.saveAndFlush(<%= persistInstance %>); + <%= relationship.relationshipFieldName %> = <%= createEntityPrefix %><%= relationship.otherEntityNameCapitalized %>ResourceIT.createEntity(em); + } else { + <%= relationship.relationshipFieldName %> = TestUtil.findAll(em, <%= relationship.otherEntity.persistClass %>.class).get(0); + } + <%_ } else { _%> + <%= relationship.otherEntity.persistClass %> <%= relationship.relationshipFieldName %> = <%= relationship.otherEntityNameCapitalized %>ResourceIT.createEntity(em); + <%_ } _%> + <%_ if(reactive) { _%> + <%= relationship.otherEntity.persistInstance %>Repository.<%= saveMethod %>(<%= relationship.relationshipFieldName %>)<%= callBlock %>; + <%_ } else { _%> + em.persist(<%= relationship.relationshipFieldName %>); + em.flush(); + <%_ } _%> + <%_ if (!reactive && (relationship.relationshipManyToMany || relationship.relationshipOneToMany)) { _%> + <%= persistInstance %>.add<%= relationship.relationshipNameCapitalized %>(<%= relationship.relationshipFieldName %>); + <%_ } else if (!reactive) { _%> + <%= persistInstance %>.set<%= relationship.relationshipNameCapitalized %>(<%= relationship.relationshipFieldName %>); + <%_ if (!relationship.ownerSide) { _%> + <%= relationship.relationshipFieldName %>.set<%= relationship.otherEntityRelationshipNameCapitalized %>(<%= persistInstance %>); + <%_ } _%> + <%_ } else { _%> + <%= relationship.otherEntity.primaryKey.type %> <%= relationship.relationshipFieldName %>Id = <%= relationship.relationshipFieldName %>.get<%= relationship.otherEntity.primaryKey.nameCapitalized %>(); + <%= persistInstance %>.set<%= relationship.relationshipNameCapitalized %>Id(<%= relationship.relationshipFieldName %>Id); + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ } _%> + <%_ } _%> + <%_ if(!reactive) { _%> + <%= entityInstance %>Repository.saveAndFlush(<%= persistInstance %>); + <%= relationship.otherEntity.primaryKey.type %> <%= relationship.relationshipFieldName %>Id = <%= relationship.relationshipFieldName %>.get<%= relationship.otherEntity.primaryKey.nameCapitalized %>(); + <%_ } _%> + // Get all the <%= entityInstance %>List where <%= relationship.relationshipFieldName %> equals to <%= relationship.relationshipFieldName %>Id + default<%= entityClass %>ShouldBeFound("<%= relationship.relationshipFieldName %>Id.equals=" + <%= relationship.relationshipFieldName %>Id); + + <%_ + const initInvalidPrimaryKey = { + 'String' : '"invalid-id"', + 'Long' : '(' + relationship.relationshipFieldName + 'Id + 1)', + 'UUID' : 'UUID.randomUUID()' + }[relationship.otherEntity.primaryKey.type]; + _%> + // Get all the <%= entityInstance %>List where <%= relationship.relationshipFieldName %> equals to <%- initInvalidPrimaryKey %> + default<%= entityClass %>ShouldNotBeFound("<%= relationship.relationshipFieldName %>Id.equals=" + <%- initInvalidPrimaryKey %>); + } + + <%_ }); _%> + /** + * Executes the search, and checks that the default entity is returned. + */ + <%_ if (reactive) { _%> + private void default<%= entityClass %>ShouldBeFound(String filter) { + webTestClient.get().uri(ENTITY_API_URL + "?sort=<%= primaryKey.name %>,desc&" + filter) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody() + .jsonPath("$.[*].<%= primaryKey.name %>").value(hasItem(<%= idValue %>))<% for (field of fieldsToTest) { %> + <%_ if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + .jsonPath("$.[*].<%= field.fieldName %>ContentType").value(hasItem(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE)) + <%_ } _%> + .jsonPath("$.[*].<%= field.fieldName %>").value(hasItem(<% if + (field.fieldTypeBinary && !field.blobContentTypeText) { %>Base64Utils.encodeToString(<% } else + if (field.fieldTypeZonedDateTime) { %>sameInstant(<% } else + if (field.fieldTypeBigDecimal) { %>sameNumber(<% } %><%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %><% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %><% if (databaseTypeCassandra) { %>.array()<% } %>)<% } else + if (field.fieldTypeInteger) { %><% } else + if (field.fieldTypeLong) { %>.intValue()<% } else + if (field.fieldTypeFloat || field.fieldTypeDouble) { %>.doubleValue()<% } else + if (field.fieldTypeBigDecimal) { %>)<% } else + if (field.fieldTypeBoolean) { %>.booleanValue()<% } else + if (field.fieldTypeZonedDateTime) { %>)<% } else + if (!field.fieldTypeString) { %>.toString()<% } %>))<%_ } _%>; + + // Check, that the count call also returns 1 + webTestClient.get().uri(ENTITY_API_URL + "/count?sort=<%= primaryKey.name %>,desc&" + filter) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody() + <%_ if(reactive) { _%> + .jsonPath("$") + .value(is(1)); + <%_ } else { _%> + .json("1"); + <%_ } _%> + } + <%_ } else { _%> + private void default<%= entityClass %>ShouldBeFound(String filter) throws Exception { + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL + "?sort=<%= primaryKey.name %>,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + <%_ + const primaryKeyConversion = { + 'Long' : '.intValue()', + 'UUID' : '.toString()' + }[primaryKey.type] || ''; + _%> + .andExpect(jsonPath("$.[*].<%= primaryKey.name %>").value(hasItem(<%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()<%= primaryKeyConversion %>)))<% fieldsToTest.forEach((field) => { %> + <%_ if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + .andExpect(jsonPath("$.[*].<%= field.fieldName %>ContentType").value(hasItem(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE))) + <%_ } _%> + .andExpect(jsonPath("$.[*].<%= field.fieldName %>").value(hasItem(<% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %>Base64Utils.encodeToString(<% } else + if (field.fieldTypeZonedDateTime) { %>sameInstant(<% } else + if (field.fieldTypeBigDecimal) { %>sameNumber(<% } %><%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %><% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %><% + if (databaseTypeCassandra) { %>.array()<% } %>)<% } else + if (field.fieldTypeInteger) { %><% } else + if (field.fieldTypeLong) { %>.intValue()<% } else + if (field.fieldTypeFloat || field.fieldTypeDouble) { %>.doubleValue()<% } else + if (field.fieldTypeBigDecimal) { %>)<% } else + if (field.fieldTypeBoolean) { %>.booleanValue()<% } else + if (field.fieldTypeZonedDateTime) { %>)<% } else + if (!field.fieldTypeString) { %>.toString()<% } %>)))<% }); %>; + + // Check, that the count call also returns 1 + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL + "/count?sort=<%= primaryKey.name %>,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(content().string("1")); + } + <%_ } _%> + + /** + * Executes the search, and checks that the default entity is not returned. + */ + <%_ if (reactive) { _%> + private void default<%= entityClass %>ShouldNotBeFound(String filter) { + webTestClient.get().uri(ENTITY_API_URL + "?sort=<%= primaryKey.name %>,desc&" + filter) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody() + .jsonPath("$").isArray() + .jsonPath("$").isEmpty(); + + // Check, that the count call also returns 0 + webTestClient.get().uri(ENTITY_API_URL + "/count?sort=<%= primaryKey.name %>,desc&" + filter) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody() + <%_ if(reactive) { _%> + .jsonPath("$") + .value(is(0)); + <%_ } else { _%> + .json("0"); + <%_ } _%> + } + <%_ } else { _%> + private void default<%= entityClass %>ShouldNotBeFound(String filter) throws Exception { + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL + "?sort=<%= primaryKey.name %>,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isEmpty()); + + // Check, that the count call also returns 0 + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL + "/count?sort=<%= primaryKey.name %>,desc&" + filter)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(content().string("0")); + } + <%_ } _%> + +<%_ } _%> + <%= getTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= getDefaultRole %>"}) + void getNonExisting<%= entityClass %>() <% if (!reactive) { %>throws Exception <% } %>{ + // Get the <%= entityInstance %> +<%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_API_URL_ID, <% if (primaryKey.typeInteger) { %>Integer.MAX_VALUE<% } else if (primaryKey.typeLong || primaryKey.typeString) { %>Long.MAX_VALUE<% } else if (primaryKey.typeUUID) { %>UUID.randomUUID().toString()<% } %>) + .accept(MediaType.APPLICATION_PROBLEM_JSON) + .exchange() + .expectStatus().isNotFound(); +<%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_API_URL_ID, <% if (primaryKey.typeInteger) { %>Integer.MAX_VALUE<% } else if (primaryKey.typeLong || primaryKey.typeString) { %>Long.MAX_VALUE<% } else if (primaryKey.typeUUID) { %>UUID.randomUUID().toString()<% } %>)) + .andExpect(status().isNotFound()); +<%_ } _%> + } +<%_ if (!readOnly) { _%> + + <%= putTestPrefix %><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void putExisting<%= entityClass %>() throws Exception { + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + <%= entityInstance %>SearchRepository.save(<%= persistInstance %>)<%= callBlock %>; + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + + // Update the <%= entityInstance %> + <%= persistClass %> updated<%= persistClass %> = <%= entityInstance %>Repository.findById(<%= persistInstance %>.get<%= primaryKey.nameCapitalized %>())<%= reactive ? '.block()' : '.orElseThrow()' %>; + <%_ if (databaseTypeSql && !reactive) { _%> + // Disconnect from session so that the updates on updated<%= persistClass %> are not directly saved in db + em.detach(updated<%= persistClass %>); + <%_ } _%> + <%_ if (fluentMethods && fieldsToTest.length > 0) { _%> + updated<%= persistClass %><% for (field of fieldsToTest) { %> + .<%= field.fieldName %>(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>)<% if (field.fieldTypeBinary && !field.blobContentTypeText) { %> + .<%= field.fieldName %>ContentType(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE)<% } %><% } %>; + <%_ } else { _%> + <%_ for (field of fieldsToTest) { _%> + updated<%= persistClass %>.set<%= field.fieldInJavaBeanMethod %>(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + updated<%= persistClass %>.set<%= field.fieldInJavaBeanMethod %>ContentType(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE); + <%_ } _%> + <%_ } _%> + <%_ } _%> + <%_ if (dtoMapstruct) { _%> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(updated<%= persistClass %>); + <%_ } _%> + + <%_ if (reactive) { _%> + webTestClient.put().uri(ENTITY_API_URL_ID, <%= (dtoMapstruct ? dtoInstance : 'updated' + persistClass) %>.get<%= primaryKey.nameCapitalized %>()) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= (dtoMapstruct ? dtoInstance : 'updated' + persistClass) %>)) + .exchange() + .expectStatus().isOk(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(put(ENTITY_API_URL_ID, <%= (dtoMapstruct ? dtoInstance : 'updated' + persistClass) %>.get<%= primaryKey.nameCapitalized %>())<% if (authenticationUsesCsrf) { %>.with(csrf())<% } %> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= (dtoMapstruct ? dtoInstance : 'updated' + persistClass) %>))) + .andExpect(status().isOk()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeUpdate); + <%= persistClass %> test<%= entityClass %> = <%= entityInstance %>List.get(<%= entityInstance %>List.size() - 1); + <%_ for (const field of fieldsToTest) { _%> + <%_ if (field.fieldTypeZonedDateTime) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>ContentType()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE); + <%_ } else if (field.fieldTypeBigDecimal) { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualByComparingTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else { _%> + assertThat(test<%= entityClass %>.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } _%> + <%_ } _%> + <%_ if (searchEngineElasticsearch) { _%> + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + List<<%= persistClass %>> <%= entityInstance %>SearchList = IterableUtils.toList(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%= persistClass %> test<%= entityClass %>Search = <%= entityInstance %>SearchList.get(searchDatabaseSizeAfter - 1); + <%_ for (const field of fieldsToTest) { _%> + <%_ if (field.fieldTypeZonedDateTime) { _%> + assertThat(test<%= entityClass %>Search.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + assertThat(test<%= entityClass %>Search.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + assertThat(test<%= entityClass %>Search.get<%= field.fieldInJavaBeanMethod %>ContentType()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE); + <%_ } else if (field.fieldTypeBigDecimal) { _%> + assertThat(test<%= entityClass %>Search.get<%= field.fieldInJavaBeanMethod %>()).isEqualByComparingTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } else { _%> + assertThat(test<%= entityClass %>Search.get<%= field.fieldInJavaBeanMethod %>()).isEqualTo(<%= 'UPDATED_' + field.fieldNameUnderscored.toUpperCase() %>); + <%_ } _%> + <%_ } _%> + }); + <%_ } _%> + } + + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void putNonExisting<%= entityClass %>() throws Exception { + int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(primaryKey.type) %>); + + <%_ if (dtoMapstruct) { _%> + // Create the <%= entityClass %> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + + <%_ } _%> + // If the entity doesn't have an ID, it will throw BadRequestAlertException + <%_ if (reactive) { _%> + webTestClient.put().uri(ENTITY_API_URL_ID, <%= restInstance %>.get<%= primaryKey.nameCapitalized %>()) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isBadRequest(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(put(ENTITY_API_URL_ID, <%= restInstance %>.get<%= primaryKey.nameCapitalized %>())<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isBadRequest()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeUpdate); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + + <%_ } _%> + } + + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void putWithIdMismatch<%= entityClass %>() throws Exception { + int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(primaryKey.type) %>); + + <%_ if (dtoMapstruct) { _%> + // Create the <%= entityClass %> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + + <%_ } _%> + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + <%_ if (reactive) { _%> + webTestClient.put().uri(ENTITY_API_URL_ID, <%- this.getJavaValueGeneratorForType(primaryKey.type) %>) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isBadRequest(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(put(ENTITY_API_URL_ID, <%- this.getJavaValueGeneratorForType(primaryKey.type) %>)<% if (authenticationUsesCsrf) { %>.with(csrf())<% } %> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isBadRequest()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeUpdate); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + } + + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void putWithMissingIdPathParam<%= entityClass %>() throws Exception { + int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(primaryKey.type) %>); + + <%_ if (dtoMapstruct) { _%> + // Create the <%= entityClass %> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + + <%_ } _%> + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + <%_ if (reactive) { _%> + webTestClient.put().uri(ENTITY_API_URL) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isEqualTo(405); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(put(ENTITY_API_URL)<% if (authenticationUsesCsrf) { %>.with(csrf())<% } %> + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isMethodNotAllowed()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeUpdate); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + } + + <%_ + const prepareFieldForPatchTest = (field, includeCb) => { + const includeField = includeCb(); + const fieldNameUnderscoreUppercased = field.fieldNameUnderscored.toUpperCase(); + const updateWithValue = includeField ? `UPDATED_${fieldNameUnderscoreUppercased}` : 'null'; + const testWithConstant = includeField ? `UPDATED_${fieldNameUnderscoreUppercased}` : `DEFAULT_${fieldNameUnderscoreUppercased}`; + return { includeField, updateWithValue, testWithConstant, ...field}; + }; + _%> + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void partialUpdate<%= entityClass %>WithPatch() throws Exception { + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + <%_ const fieldsToIncludeInPartialPatchTest = fieldsToTest.map(field => prepareFieldForPatchTest(field, () => faker.datatype.boolean())); _%> +<%- include('/_global_partials_entity_/it_patch_update.partial.java.ejs', {fields: fieldsToIncludeInPartialPatchTest, saveMethod, callBlock, callListBlock}); -%> + } + + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void fullUpdate<%= entityClass %>WithPatch() throws Exception { + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + + <% const fieldsToIncludeInFullPatchTest = fieldsToTest.map(field => prepareFieldForPatchTest(field, () => true)); %> +<%- include('/_global_partials_entity_/it_patch_update.partial.java.ejs', {fields: fieldsToIncludeInFullPatchTest, saveMethod, callBlock, callListBlock}); -%> + } + + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void patchNonExisting<%= entityClass %>() throws Exception { + int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(primaryKey.type) %>); + + <%_ if (dtoMapstruct) { _%> + // Create the <%= entityClass %> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + + <%_ } _%> + // If the entity doesn't have an ID, it will throw BadRequestAlertException + <%_ if (reactive) { _%> + webTestClient.patch().uri(ENTITY_API_URL_ID, <%= restInstance %>.get<%= primaryKey.nameCapitalized %>()) + .contentType(MediaType.valueOf("application/merge-patch+json")) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isBadRequest(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(patch(ENTITY_API_URL_ID, <%= restInstance %>.get<%= primaryKey.nameCapitalized %>())<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isBadRequest()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeUpdate); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + } + + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void patchWithIdMismatch<%= entityClass %>() throws Exception { + int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(primaryKey.type) %>); + + <%_ if (dtoMapstruct) { _%> + // Create the <%= entityClass %> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + + <%_ } _%> + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + <%_ if (reactive) { _%> + webTestClient.patch().uri(ENTITY_API_URL_ID, <%- this.getJavaValueGeneratorForType(primaryKey.type) %>) + .contentType(MediaType.valueOf("application/merge-patch+json")) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isBadRequest(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(patch(ENTITY_API_URL_ID, <%- this.getJavaValueGeneratorForType(primaryKey.type) %>)<% if (authenticationUsesCsrf) { %>.with(csrf())<% } %> + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isBadRequest()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeUpdate); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + } + + <%= putTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= putDefaultRole %>"}) + void patchWithMissingIdPathParam<%= entityClass %>() throws Exception { + int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + <%_ } _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(primaryKey.type) %>); + + <%_ if (dtoMapstruct) { _%> + // Create the <%= entityClass %> + <%= dtoClass %> <%= dtoInstance %> = <%= entityInstance %>Mapper.toDto(<%= persistInstance %>); + + <%_ } _%> + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + <%_ if (reactive) { _%> + webTestClient.patch().uri(ENTITY_API_URL) + .contentType(MediaType.valueOf("application/merge-patch+json")) + .bodyValue(TestUtil.convertObjectToJsonBytes(<%= restInstance %>)) + .exchange() + .expectStatus().isEqualTo(405); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(patch(ENTITY_API_URL)<% if (authenticationUsesCsrf) { %>.with(csrf())<% } %> + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(<%= restInstance %>))) + .andExpect(status().isMethodNotAllowed()); + <%_ } _%> + + // Validate the <%= entityClass %> in the database + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeUpdate); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + + } + + void delete<%= entityClass %>IsAllowed() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ if (searchEngineElasticsearch) { _%> + <%= entityInstance %>Repository.save(<%= persistInstance %>)<%= callBlock %>; + <%= entityInstance %>SearchRepository.save(<%= persistInstance %>)<%= callBlock %>; + <%_ } _%> + + int databaseSizeBeforeDelete = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeBefore).isEqualTo(databaseSizeBeforeDelete); + <%_ } _%> + + // Delete the <%= entityInstance %> + <%_ if (reactive) { _%> + webTestClient.delete().uri(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isNoContent(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(delete(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()<% if (primaryKey.typeUUID && databaseTypeSql) { %>.toString()<% } %>)<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + <%_ } _%> + + // Validate the database contains one less item + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeDelete - 1); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore-1); + <%_ } _%> + + } + <%_ allowedDeleteRoles.forEach(secRole => { _%> + + <%= deleteTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void delete<%= entityClass %>_with_<%= secRole %>() throws Exception { + delete<%= entityClass %>IsAllowed(); + } + <%_ }); _%> + + void delete<%= entityClass %>IsForbidden() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= primaryKey.nameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ if (searchEngineElasticsearch) { _%> + <%= entityInstance %>Repository.save(<%= persistInstance %>)<%= callBlock %>; + <%= entityInstance %>SearchRepository.save(<%= persistInstance %>)<%= callBlock %>; + <%_ } _%> + + int databaseSizeBeforeDelete = <%= entityInstance %>Repository.findAll()<%= callListBlock %>.size(); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeBefore = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeBefore).isEqualTo(databaseSizeBeforeDelete); + <%_ } _%> + + // Delete the <%= entityInstance %> + <%_ if (reactive) { _%> + webTestClient.delete().uri(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()) + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isForbidden(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(delete(ENTITY_API_URL_ID, <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()<% if (primaryKey.typeUUID && databaseTypeSql) { %>.toString()<% } %>)<% if (authenticationUsesCsrf) { %>.with(csrf())<% }%> + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + <%_ } _%> + + // Validate the database contains one less item + <%_ if (databaseTypeCouchbase) { _%> + SecurityContextHolder.setContext(TestSecurityContextHolder.getContext()); + <%_ } _%> + List<<%= persistClass %>> <%= entityInstance %>List = <%= entityInstance %>Repository.findAll()<%= callListBlock %>; + assertThat(<%= entityInstance %>List).hasSize(databaseSizeBeforeDelete); + <%_ if (searchEngineElasticsearch) { _%> + int searchDatabaseSizeAfter = IterableUtil.sizeOf(<%= entityInstance %>SearchRepository.findAll()<%= callListBlock %>); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + <%_ } _%> + + } + <%_ security.forbiddenRoles.delete.forEach(secRole => { _%> + + @Test<%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void delete<%= entityClass %>_with_<%= secRole %>() throws Exception { + delete<%= entityClass %>IsForbidden(); + } + <%_ }); _%> +<%_ } _%> +<%_ if (searchEngineAny) { _%> + + void search<%= entityClass %>IsAllowed() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= persistInstance %> = <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ if (searchEngineElasticsearch) { _%> + <%= entityInstance %>SearchRepository.save(<%= persistInstance %>)<%= callBlock %>; + <%_ } else if (searchEngineCouchbase) { _%> + // Wait for the <%= entityInstance %> to be indexed + TestUtil.retryUntilNotEmpty(() -> <%= entityInstance %>Repository.search("id:" + <%= entityInstance %>.get<%= primaryKey.nameCapitalized %>())<% if (reactive) { %>.collectList().block()<% } %>); + <%_ } _%> + + // Search the <%= entityInstance %> + <%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_SEARCH_API_URL + "?query=id:" + <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()) + .exchange() + .expectStatus().isOk() + .expectHeader().contentType(MediaType.APPLICATION_JSON) + .expectBody() + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_SEARCH_API_URL + "?query=id:" + <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + <%_ } _%> + <%_ if (databaseTypeSql || databaseTypeMongodb || databaseTypeCouchbase || databaseTypeCassandra) { _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.[*].<%= primaryKey.name %>").value(hasItem(<%= idValue %>))<%= !reactive ? ')' : '' %><%_ } _%><% for (field of fieldsToTest) { %> + <%_ if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.[*].<%= field.fieldName %>ContentType").value(hasItem(<%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %>_CONTENT_TYPE))<%= !reactive ? ')' : '' %> + <%_ } _%> + <%= !reactive ? '.andExpect(' : '.' %>jsonPath("$.[*].<%= field.fieldName %>").value(hasItem(<% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %>Base64Utils.encodeToString(<% } else + if (field.fieldTypeZonedDateTime) { %>sameInstant(<% } else + if (field.fieldTypeBigDecimal) { %>sameNumber(<% } %><%= 'DEFAULT_' + field.fieldNameUnderscored.toUpperCase() %><% + if (field.fieldTypeBinary && !field.blobContentTypeText) { %><% + if (databaseTypeCassandra) { %>.array()<% } %>)<% } else + if (field.fieldTypeInteger) { %><% } else + if (field.fieldTypeLong) { %>.intValue()<% } else + if (field.fieldTypeFloat || field.fieldTypeDouble) { %>.doubleValue()<% } else + if (field.fieldTypeBigDecimal) { %>)<% } else + if (field.fieldTypeBoolean) { %>.booleanValue()<% } else + if (field.fieldTypeZonedDateTime) { %>)<% } else + if (!field.fieldTypeString) { %>.toString()<% } %>))<%= !reactive ? ')' : '' %><%_ } _%>; + } + <%_ allowedGetRoles.forEach(secRole => { _%> + + <%_ if (searchEngineCouchbase) { _%> + @Timeout(value = 15, unit = TimeUnit.MINUTES) + <%_ } _%> + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void search<%= entityClass %>_with_<%= secRole %>() throws Exception { + search<%= entityClass %>IsAllowed(); + } + <%_ }); _%> + + void search<%= entityClass %>IsForbidden() <% if (!reactive) { %>throws Exception <% } %>{ + // Initialize the database + <%_ if (!primaryKey.derived) { _%> + <%_ for (field of primaryKey.fields.filter(f => !f.autoGenerateByRepository)) { _%> + <%= persistInstance %>.set<%= field.fieldNameCapitalized %>(<%- this.getJavaValueGeneratorForType(field.fieldType) %>); + <%_ } _%> + <%_ } _%> + <%= persistInstance %> = <%= entityInstance %>Repository.<%= saveMethod %>(<%= persistInstance %>)<%= callBlock %>; + <%_ if (searchEngineElasticsearch) { _%> + <%= entityInstance %>SearchRepository.save(<%= persistInstance %>)<%= callBlock %>; + <%_ } else if (searchEngineCouchbase) { _%> + // Wait for the <%= entityInstance %> to be indexed + TestUtil.retryUntilNotEmpty(() -> <%= entityInstance %>Repository.search("id:" + <%= entityInstance %>.get<%= primaryKey.nameCapitalized %>())<% if (reactive) { %>.collectList().block()<% } %>); + <%_ } _%> + + // Search the <%= entityInstance %> + <%_ if (reactive) { _%> + webTestClient.get().uri(ENTITY_SEARCH_API_URL + "?query=id:" + <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>()) + .exchange() + .expectStatus().isForbidden(); + <%_ } else { _%> + rest<%= entityClass %>MockMvc.perform(get(ENTITY_SEARCH_API_URL + "?query=id:" + <%= persistInstance %>.get<%= primaryKey.nameCapitalized %>())) + .andExpect(status().isForbidden()); + <%_ } _%> + } + // #end search<%= entityClass %>IsForbidden + <%_ security.forbiddenRoles.get.forEach(secRole => { _%> + + <%_ if (searchEngineCouchbase) { _%> + @Timeout(value = 15, unit = TimeUnit.MINUTES) + <%_ } _%> + <%= getTestPrefix%><%= transactionalAnnotation %> + @WithMockUser(username = "user", authorities = {"<%= secRole %>"}) + void search<%= entityClass %>_with_<%= secRole %>() throws Exception { + search<%= entityClass %>IsForbidden(); + } + <%_ }); _%> + +<%_ } _%> +} diff --git a/jdl/__test-files__/security1.jdl b/jdl/__test-files__/security1.jdl new file mode 100644 index 000000000000..f222d6b3be7d --- /dev/null +++ b/jdl/__test-files__/security1.jdl @@ -0,0 +1,6 @@ +entity A +entity B +entity C +secure all with roles { + ROLE_ADMIN allows (get, post, put, delete) +} diff --git a/jdl/__test-files__/security2.jdl b/jdl/__test-files__/security2.jdl new file mode 100644 index 000000000000..47d758f7127a --- /dev/null +++ b/jdl/__test-files__/security2.jdl @@ -0,0 +1,6 @@ +entity A +entity B +entity C +secure all except B with roles { + ROLE_ADMIN allows (get, post, put, delete) +} diff --git a/jdl/__test-files__/security3.jdl b/jdl/__test-files__/security3.jdl new file mode 100644 index 000000000000..e48268cd20dc --- /dev/null +++ b/jdl/__test-files__/security3.jdl @@ -0,0 +1,7 @@ +entity A +entity B +entity C + +secure A,B,C with roles { + ROLE_ADMIN allows (get, post, put, delete) +} diff --git a/jdl/converters/jdl-to-json/jdl-to-json-secure-converter.ts b/jdl/converters/jdl-to-json/jdl-to-json-secure-converter.ts new file mode 100644 index 000000000000..152959f526da --- /dev/null +++ b/jdl/converters/jdl-to-json/jdl-to-json-secure-converter.ts @@ -0,0 +1,60 @@ +/** + * Copyright 2013-2023 the original author or authors from the JHipster project. + * + * This file is part of the JHipster project, see https://www.jhipster.tech/ + * for more information. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { IJSONSecure, JSONSecure } from '../../jhipster/json-secure.js'; +import { JDLEntity } from '../../models/index.js'; +import { JDLSecurityType } from '../../models/jdl-security-type.js'; + +export default { + convert, +}; + +/** + * Converts secure clause to JSON content, + * @param jdlEntities - the JDL object containing entities with secure property. + * @returns {Map} a map having for keys an entity's name and for values its JSON secure clause. + */ +function convert(jdlEntities: JDLEntity[]): Map { + if (!jdlEntities) { + throw new Error('A JDL entities must be passed to convert JDL security to JSON.'); + } + const convertedSecureClauses = new Map(); + + jdlEntities.forEach(jdlEntity => { + const convertedSecure = getConvertedSecureForEntity(jdlEntity); + convertedSecureClauses.set(jdlEntity.name, convertedSecure); + }); + + return convertedSecureClauses; +} + +/** + * Converts a JDLEntity object to an IJSONSecure object with security configurations. + * + * @param {JDLEntity} jdlEntity - The JDLEntity object to be converted. + * @returns {IJSONSecure} - The converted IJSONSecure object with security configurations. + */ +function getConvertedSecureForEntity(jdlEntity: JDLEntity): IJSONSecure { + const jsonSecure: JSONSecure = new JSONSecure({ securityType: JDLSecurityType.None }); + + if (jdlEntity.secure) { + jsonSecure.addConfigFromJDLSecure(jdlEntity.secure); + } + + return jsonSecure; +} diff --git a/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.spec.ts b/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.spec.ts index d41db21be6fc..48900890f236 100644 --- a/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.spec.ts +++ b/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.spec.ts @@ -18,7 +18,7 @@ */ /* eslint-disable no-new, no-unused-expressions */ -import { before, it, describe, after, expect, expect as jestExpect } from 'esmocha'; +import { before, it, describe, after, expect as jestExpect } from 'esmocha'; import chai, { expect } from 'chai'; import sinon from 'sinon'; import sinonChai from 'sinon-chai'; @@ -46,6 +46,7 @@ import JDLBinaryOption from '../../models/jdl-binary-option.js'; import logger from '../../utils/objects/logger.js'; import { convert } from './jdl-with-applications-to-json-converter.js'; +import { JDLSecurityType, RoleActionType } from '../../models/jdl-security-type.js'; const { Validations: { REQUIRED, UNIQUE, MIN, MAX, MINLENGTH, MAXLENGTH, PATTERN, MINBYTES, MAXBYTES }, @@ -106,7 +107,7 @@ describe('jdl - JDLWithApplicationsToJSONConverter', () => { }); }); - it('should return a map with no entiy', () => { + it('should return a map with no entity', () => { result.forEach(entities => { expect(entities.length).to.equal(0); }); @@ -1925,5 +1926,36 @@ JSONEntity { }); }); }); + context('with entity security options', () => { + let convertedEntity; + + before(() => { + const jdlObject = new JDLObject(); + const application = createJDLApplication({ applicationType: MONOLITH, baseName: 'toto' }); + const entityA = new JDLEntity({ + name: 'A', + tableName: 'entity_a', + comment: 'The best entity', + secure: { + securityType: JDLSecurityType.Roles, + roles: [{ role: 'Test', actionList: [RoleActionType.Post, RoleActionType.Get] }], + }, + }); + application.addEntityName('A'); + jdlObject.addApplication(application); + jdlObject.addEntity(entityA); + const returnedMap: any = convert({ + jdlObject, + }); + convertedEntity = returnedMap.get('toto')[0]; + }); + + it('should convert them', () => { + expect(convertedEntity.secure).to.deep.equal({ + securityType: JDLSecurityType.Roles, + roles: [{ role: 'Test', actionList: [RoleActionType.Post, RoleActionType.Get] }], + }); + }); + }); }); }); diff --git a/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.ts b/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.ts index de29e145a1ec..5786ba986afd 100644 --- a/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.ts +++ b/jdl/converters/jdl-to-json/jdl-with-applications-to-json-converter.ts @@ -21,6 +21,8 @@ import BasicEntityConverter from './jdl-to-json-basic-entity-converter.js'; import FieldConverter from './jdl-to-json-field-converter.js'; import RelationshipConverter from './jdl-to-json-relationship-converter.js'; import OptionConverter from './jdl-to-json-option-converter.js'; +import SecureClauseConverter from './jdl-to-json-secure-converter.js'; +import { JDLSecurityType } from '../../models/jdl-security-type.js'; let entities; let jdlObject; @@ -47,6 +49,7 @@ export function convert(args: any = {}) { setBasicEntityInformation(); setFields(); setRelationships(); + setSecureClause(); setApplicationToEntities(); const entitiesForEachApplication = getEntitiesForEachApplicationMap(); setOptions(entitiesForEachApplication); @@ -94,6 +97,15 @@ function setFields() { }); } +function setSecureClause() { + const convertedSecure = SecureClauseConverter.convert(jdlObject.getEntities()); + convertedSecure.forEach((entitySecure, entityName) => { + if (entitySecure && entitySecure.securityType !== JDLSecurityType.None) { + entities[entityName].secure = entitySecure; + } + }); +} + function setRelationships() { const convertedRelationships = RelationshipConverter.convert(jdlObject.getRelationships(), jdlObject.getEntityNames()); convertedRelationships.forEach((entityRelationships, entityName) => { diff --git a/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.spec.ts b/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.spec.ts index 57eaf29cfdea..6bb746887564 100644 --- a/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.spec.ts +++ b/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.spec.ts @@ -46,6 +46,7 @@ import JDLRelationship from '../../models/jdl-relationship.js'; import JDLUnaryOption from '../../models/jdl-unary-option.js'; import JDLBinaryOption from '../../models/jdl-binary-option.js'; import logger from '../../utils/objects/logger.js'; +import { JDLSecurityType, RoleActionType } from '../../models/jdl-security-type.js'; const { Validations: { REQUIRED, UNIQUE, MIN, MAX, MINLENGTH, MAXLENGTH, PATTERN, MINBYTES, MAXBYTES }, @@ -1611,5 +1612,36 @@ JSONEntity { }); }); }); + context('with entity security options', () => { + let convertedEntity; + + before(() => { + const jdlObject = new JDLObject(); + const entityA = new JDLEntity({ + name: 'A', + tableName: 'entity_a', + comment: 'The best entity', + secure: { + securityType: JDLSecurityType.Roles, + roles: [{ role: 'Test', actionList: [RoleActionType.Post, RoleActionType.Get] }], + }, + }); + jdlObject.addEntity(entityA); + const returnedMap: any = convert({ + jdlObject, + applicationName: 'toto', + applicationType: MONOLITH, + databaseType: SQL, + }); + convertedEntity = returnedMap.get('toto')[0]; + }); + + it('should convert them', () => { + expect(convertedEntity.secure).to.deep.equal({ + securityType: JDLSecurityType.Roles, + roles: [{ role: 'Test', actionList: [RoleActionType.Post, RoleActionType.Get] }], + }); + }); + }); }); }); diff --git a/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.ts b/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.ts index 922a3840878b..d6b6131fab8b 100644 --- a/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.ts +++ b/jdl/converters/jdl-to-json/jdl-without-application-to-json-converter.ts @@ -22,6 +22,8 @@ import FieldConverter from './jdl-to-json-field-converter.js'; import RelationshipConverter from './jdl-to-json-relationship-converter.js'; import OptionConverter from './jdl-to-json-option-converter.js'; import JDLObject from '../../models/jdl-object.js'; +import SecureClauseConverter from './jdl-to-json-secure-converter.js'; +import { JDLSecurityType } from '../../models/jdl-security-type.js'; let entities; let jdlObject: JDLObject | null; @@ -48,6 +50,7 @@ export function convert(args: any = {}) { setOptions(); setFields(); setRelationships(); + setSecureClause(); setApplicationToEntities(); return new Map([[args.applicationName, Object.values(entities)]]); } @@ -86,6 +89,17 @@ function setFields() { }); } +function setSecureClause() { + if (jdlObject) { + const convertedSecure = SecureClauseConverter.convert(jdlObject.getEntities()); + convertedSecure.forEach((entitySecure, entityName) => { + if (entitySecure && entitySecure.securityType !== JDLSecurityType.None) { + entities[entityName].secure = entitySecure; + } + }); + } +} + function setRelationships() { const convertedRelationships = RelationshipConverter.convert(jdlObject!.getRelationships(), jdlObject!.getEntityNames()); convertedRelationships.forEach((entityRelationships, entityName) => { diff --git a/jdl/converters/parsed-jdl-to-jdl-object/__snapshots__/option-converter.spec.ts.snap b/jdl/converters/parsed-jdl-to-jdl-object/__snapshots__/option-converter.spec.ts.snap index 5895fa65f348..a0a3459c609c 100644 --- a/jdl/converters/parsed-jdl-to-jdl-object/__snapshots__/option-converter.spec.ts.snap +++ b/jdl/converters/parsed-jdl-to-jdl-object/__snapshots__/option-converter.spec.ts.snap @@ -146,6 +146,20 @@ exports[`jdl - OptionConverter convertOptions when passing options such as searc ] `; +exports[`jdl - OptionConverter convertOptions when passing options such as secure should convert it 1`] = ` +[ + JDLUnaryOption { + "entityNames": Set { + "A", + }, + "excludedNames": Set { + "B", + }, + "name": "secure", + }, +] +`; + exports[`jdl - OptionConverter convertOptions when passing options such as service should convert it 1`] = ` [ JDLBinaryOption { diff --git a/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.spec.ts b/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.spec.ts index c5532b389faf..1f9146b7c524 100644 --- a/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.spec.ts +++ b/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.spec.ts @@ -65,6 +65,89 @@ describe('jdl - EntityConverter', () => { "tableName": "b_table", }, ] +`); + }); + }); + + context('when passing entities with security', () => { + let convertedEntities; + + before(() => { + convertedEntities = convertEntities( + [ + { + name: 'A', + documentation: '/** No comment */', + secure: { + securityType: 'roles', + roles: [ + { + role: 'ROLE_ADMIN', + actionList: ['GET', 'POST'], + }, + ], + }, + }, + { + name: 'B', + tableName: 'b_table', + secure: { + securityType: 'roles', + roles: [ + { + role: 'ROLE_ADMIN', + actionList: ['GET', 'POST'], + }, + ], + }, + }, + ], + () => [], + ); + }); + + it('should convert them', () => { + expect(convertedEntities).toMatchInlineSnapshot(` +[ + JDLEntity { + "annotations": {}, + "comment": "/** No comment */", + "fields": {}, + "name": "A", + "secure": JDLSecure { + "roles": [ + { + "actionList": [ + "GET", + "POST", + ], + "role": "ROLE_ADMIN", + }, + ], + "securityType": "roles", + }, + "tableName": "A", + }, + JDLEntity { + "annotations": {}, + "comment": undefined, + "fields": {}, + "name": "B", + "secure": JDLSecure { + "roles": [ + { + "actionList": [ + "GET", + "POST", + ], + "role": "ROLE_ADMIN", + }, + ], + "securityType": "roles", + }, + "tableName": "b_table", + }, +] `); }); }); diff --git a/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.ts b/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.ts index 8c8740469dbd..6e4b44c7c4c2 100644 --- a/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.ts +++ b/jdl/converters/parsed-jdl-to-jdl-object/entity-converter.ts @@ -18,8 +18,9 @@ */ import { lowerFirst } from 'lodash-es'; -import { JDLEntity } from '../../models/index.js'; +import { JDLEntity, JDLSecure } from '../../models/index.js'; import { formatComment } from '../../utils/format-utils.js'; +import { JDLSecurityType } from '../../models/jdl-security-type.js'; export default { convertEntities }; @@ -47,6 +48,12 @@ export function convertEntities(parsedEntities, jdlFieldGetterFunction): JDLEnti }); const jdlFields = jdlFieldGetterFunction.call(undefined, parsedEntity); jdlEntity.addFields(jdlFields); + if (parsedEntity.secure && parsedEntity.secure.securityType !== JDLSecurityType.None) { + // jdlEntity.secure = jdlSecureGetterFunction.call(undefined, parsedEntity); + jdlEntity.secure = new JDLSecure(parsedEntity.secure); + } else { + delete jdlEntity.secure; + } return jdlEntity; }); } diff --git a/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.spec.ts b/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.spec.ts index e9c29c0f3400..bf4cd7f35b37 100644 --- a/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.spec.ts +++ b/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.spec.ts @@ -1011,6 +1011,61 @@ skipClient D }); }); }); + context('when parsing a JDL with secure entity definition', () => { + context('secure all entities', () => { + let jdlObject; + + before(() => { + const input = JDLReader.parseFromFiles([path.join(__dirname, '..', '..', '__test-files__', 'security1.jdl')]); + jdlObject = ParsedJDLToJDLObjectConverter.parseFromConfigurationObject({ + parsedContent: input, + applicationType: MONOLITH, + }); + }); + + it('should set it', () => { + expect(jdlObject.entities.A.secure.securityType).to.equal('roles'); + expect(jdlObject.entities.B.secure.securityType).to.equal('roles'); + expect(jdlObject.entities.C.secure.securityType).to.equal('roles'); + }); + }); + + context('secure all except C entities', () => { + let jdlObject; + + before(() => { + const input = JDLReader.parseFromFiles([path.join(__dirname, '..', '..', '__test-files__', 'security2.jdl')]); + jdlObject = ParsedJDLToJDLObjectConverter.parseFromConfigurationObject({ + parsedContent: input, + applicationType: MONOLITH, + }); + }); + + it('should set it', () => { + expect(jdlObject.entities.A.secure.securityType).to.equal('roles'); + expect(jdlObject.entities.C.secure.securityType).to.equal('roles'); + expect(jdlObject.entities.B.secure).to.be.undefined; + }); + }); + + context('secure list of entities', () => { + let jdlObject; + + before(() => { + const input = JDLReader.parseFromFiles([path.join(__dirname, '..', '..', '__test-files__', 'security3.jdl')]); + jdlObject = ParsedJDLToJDLObjectConverter.parseFromConfigurationObject({ + parsedContent: input, + applicationType: MONOLITH, + }); + }); + + it('should set it', () => { + expect(jdlObject.entities.A.secure.securityType).to.equal('roles'); + expect(jdlObject.entities.B.secure.securityType).to.equal('roles'); + expect(jdlObject.entities.C.secure.securityType).to.equal('roles'); + }); + }); + }); }); }); }); diff --git a/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.ts b/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.ts index 4ebabcc91cbb..df3396882dd6 100644 --- a/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.ts +++ b/jdl/converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.ts @@ -29,6 +29,7 @@ import { convertValidations } from './validation-converter.js'; import { convertOptions } from './option-converter.js'; import { convertRelationships } from './relationship-converter.js'; import { convertDeployments } from './deployment-converter.js'; +import { JDLEntity, JDLSecure } from '../../models/index.js'; let parsedContent; let configuration; @@ -58,6 +59,7 @@ export function parseFromConfigurationObject(configurationObject): JDLObject { fillClassesAndFields(); fillAssociations(); fillOptions(); + fillSecurity(); return jdlObject; } @@ -167,6 +169,31 @@ function fillOptions() { fillUnaryAndBinaryOptions(); } +function fillSecurity() { + const jdlSecureStmts = parsedContent.secure; + + for (let i = 0; i < jdlSecureStmts.length; i++) { + const jdlSecureStmt = jdlSecureStmts[i]; + // check if entityNames[0] === '*' + if (jdlSecureStmt.entityNames && jdlSecureStmt.entityNames.length === 1 && jdlSecureStmt.entityNames[0] === '*') { + jdlSecureStmt.entityNames = []; + parsedContent.entities.forEach(entity => { + if (!jdlSecureStmt.excludedNames.includes(entity.name)) { + jdlSecureStmt.entityNames.push(entity.name); + } + }); + } + + for (let j = 0; j < jdlSecureStmt.entityNames.length; j++) { + const entityName = jdlSecureStmt.entityNames[j]; + const entity: JDLEntity = jdlObject.getEntity(entityName); + if (entity) { + entity.secure = new JDLSecure(jdlSecureStmt); + } + } + } +} + // TODO: move it to another file? it may not be the parser's responsibility to do it function globallyAddMicroserviceOption(applicationName) { jdlObject.addOption( diff --git a/jdl/jdl-importer.spec.ts b/jdl/jdl-importer.spec.ts index 3f2dde20b658..61c512cd2258 100644 --- a/jdl/jdl-importer.spec.ts +++ b/jdl/jdl-importer.spec.ts @@ -20,7 +20,7 @@ /* eslint-disable no-new, no-unused-expressions */ import path, { dirname } from 'path'; import { fileURLToPath } from 'url'; -import { before, it, describe, after, expect, expect as jestExpect } from 'esmocha'; +import { before, it, describe, after, expect as jestExpect } from 'esmocha'; import fse from 'fs-extra'; import { expect } from 'chai'; @@ -673,5 +673,103 @@ relationship OneToOne { jestExpect(importState.exportedApplications[0]['generator-jhipster'].clientFramework).toBe(NO_CLIENT_FRAMEWORK); }); }); + context('when parsing entity with a secure definition', () => { + it('import entity with role security definition', () => { + const importState = createImporterFromContent( + ` +entity A + +secure A with roles { + ROLE_A allows (GET, Put, Post, delete) + ROLE_B allows (get) +} +`, + { applicationName: 'MyApp', databaseType: databaseTypes.SQL }, + ).import(); + jestExpect(importState.exportedEntities[0].secure.securityType).toBe('roles'); + jestExpect(importState.exportedEntities[0].secure.roles.length).toBe(2); + jestExpect(importState.exportedEntities[0].secure.roles[0].role).toBe('ROLE_A'); + jestExpect(importState.exportedEntities[0].secure.roles[0].actionList.length).toBe(4); + jestExpect(importState.exportedEntities[0].secure.roles[1].role).toBe('ROLE_B'); + jestExpect(importState.exportedEntities[0].secure.roles[1].actionList.length).toBe(1); + jestExpect(importState.exportedEntities[0].secure.roles[1].actionList[0]).toBe('GET'); + }); + + it('import entity with organizational security definition', () => { + const importState = createImporterFromContent( + ` +entity A + +secure A with organizationalSecurity { + resourceName RESNAME +} +`, + { applicationName: 'MyApp', databaseType: databaseTypes.SQL }, + ).import(); + jestExpect(importState.exportedEntities[0].secure.securityType).toBe('organizationalSecurity'); + jestExpect(importState.exportedEntities[0].secure.organizationalSecurity.resource).toBe('RESNAME'); + }); + + it('import entity with parent privilege security definition', () => { + const importState = createImporterFromContent( + ` +entity A +entity B +relationship ManyToOne { B {a required} to A} + +secure B with parentPrivileges { + parent A + field a +} +`, + { applicationName: 'MyApp', databaseType: databaseTypes.SQL }, + ).import(); + jestExpect(importState.exportedEntities[1].secure.securityType).toBe('parentPrivileges'); + jestExpect(importState.exportedEntities[1].secure.parentPrivileges.parent).toBe('A'); + jestExpect(importState.exportedEntities[1].secure.parentPrivileges.field).toBe('a'); + }); + + it('import entity with relation privileges security definition', () => { + const importState = createImporterFromContent( + ` +entity A +entity B +relationship ManyToOne { B {a required} to A} +relationship ManyToOne { B {c required} to C} +entity C +secure B with relPrivileges { + fromEntity A field a + toEntity C field c +} +`, + { applicationName: 'MyApp', databaseType: databaseTypes.SQL }, + ).import(); + jestExpect(importState.exportedEntities[1].secure.securityType).toBe('relPrivileges'); + jestExpect(importState.exportedEntities[1].secure.relPrivileges.fromEntity).toBe('A'); + jestExpect(importState.exportedEntities[1].secure.relPrivileges.fromField).toBe('a'); + jestExpect(importState.exportedEntities[1].secure.relPrivileges.toEntity).toBe('C'); + jestExpect(importState.exportedEntities[1].secure.relPrivileges.toField).toBe('c'); + }); + + it('import entity with privileges security definition', () => { + const importState = createImporterFromContent( + ` +entity A + +secure A with privileges { + Read requirePrivs (A_ALL_R, A_OWN_R, A_ORG_R, A_USR_R) + Write requirePrivs (A_ALL_W) +} +`, + { applicationName: 'MyApp', databaseType: databaseTypes.SQL }, + ).import(); + jestExpect(importState.exportedEntities[0].secure.securityType).toBe('privileges'); + jestExpect(importState.exportedEntities[0].secure.privileges.length).toBe(2); + jestExpect(importState.exportedEntities[0].secure.privileges[0].action).toBe('read'); + jestExpect(importState.exportedEntities[0].secure.privileges[0].privList.length).toBe(4); + jestExpect(importState.exportedEntities[0].secure.privileges[1].action).toBe('write'); + jestExpect(importState.exportedEntities[0].secure.privileges[1].privList.length).toBe(1); + }); + }); }); }); diff --git a/jdl/jhipster/index.ts b/jdl/jhipster/index.ts index 01f3a83c6226..5b2bf9f4e9d5 100644 --- a/jdl/jhipster/index.ts +++ b/jdl/jhipster/index.ts @@ -29,6 +29,7 @@ export { default as validations } from './validations.js'; export * from './validations.js'; export { default as websocketTypes } from './websocket-types.js'; export { default as checkAndReturnRelationshipOnValue } from './relationship-on-handler-options.js'; +export { default as jsonSecure } from './json-secure.js'; export { IngressTypes as ingressTypes } from './kubernetes-platform-types.js'; export { defaultApplicationOptions }; diff --git a/jdl/jhipster/json-entity.spec.ts b/jdl/jhipster/json-entity.spec.ts index 912ec2cc56af..ebd2d5d0aaa4 100644 --- a/jdl/jhipster/json-entity.spec.ts +++ b/jdl/jhipster/json-entity.spec.ts @@ -18,9 +18,10 @@ */ /* eslint-disable no-new,no-unused-expressions */ -import { before, it, describe, expect, expect as jestExpect } from 'esmocha'; +import { before, it, describe, expect as jestExpect } from 'esmocha'; import { expect } from 'chai'; import { jsonEntity as JSONEntity } from '../jhipster/index.js'; +import { JDLSecurityType, PrivilegeActionType, RoleActionType } from '../models/jdl-security-type.js'; describe('jdl - JSONEntity', () => { describe('new', () => { @@ -123,6 +124,37 @@ JSONEntity { "skipClient": true, "skipServer": true, } +`); + }); + + it('should use them with secure option', () => { + jestExpect(entity).toMatchInlineSnapshot(` +JSONEntity { + "angularJSSuffix": "yes", + "annotations": {}, + "applications": [], + "clientRootFolder": "oh", + "documentation": "", + "dto": "mapstruct", + "embedded": true, + "entityTableName": "titi", + "fields": [ + 42, + ], + "fluentMethods": true, + "jpaMetamodelFiltering": true, + "microserviceName": "nope", + "name": "Titi", + "pagination": "pagination", + "readOnly": true, + "relationships": [ + 42, + 43, + ], + "service": "serviceClass", + "skipClient": true, + "skipServer": true, +} `); }); }); @@ -294,4 +326,86 @@ JSONEntity { }); }); }); + describe('setSecure', () => { + context('when adding secure on entity', () => { + let entity; + + before(() => { + entity = new JSONEntity({ + entityName: 'seco', + }); + }); + + it('should add none securityType', () => { + entity.setSecure({ securityType: JDLSecurityType.None }); + expect(entity.secure).to.exist; + expect(entity.secure.securityType).to.equal(JDLSecurityType.None); + }); + + it('should add roles securityType', () => { + const roleDef1 = { + role: 'test_role', + actionList: [RoleActionType.Put, RoleActionType.Post, RoleActionType.Get, RoleActionType.Delete], + }; + + entity.setSecure({ securityType: JDLSecurityType.Roles, roles: [roleDef1], comment: 'comment' }); + expect(entity.secure.securityType).equal(JDLSecurityType.Roles); + expect(entity.secure.comment).equal('comment'); + expect(entity.secure.roles).to.deep.equal([roleDef1]); + }); + + it('should add privileges securityType', () => { + const privDef1 = { + action: PrivilegeActionType.Read, + privList: ['PRIV1_R', 'PRIV2_R'], + }; + + entity.setSecure({ securityType: JDLSecurityType.Privileges, privileges: [privDef1], comment: 'comment' }); + expect(entity.secure.securityType).equal(JDLSecurityType.Privileges); + expect(entity.secure.comment).equal('comment'); + expect(entity.secure.privileges).to.deep.equal([privDef1]); + }); + + it('should add organizationalSecurity securityType', () => { + const organizationalSecurityDef1 = { + resource: 'resourceName', + }; + + entity.setSecure({ + securityType: JDLSecurityType.OrganizationalSecurity, + organizationalSecurity: organizationalSecurityDef1, + comment: 'comment', + }); + expect(entity.secure.securityType).equal(JDLSecurityType.OrganizationalSecurity); + expect(entity.secure.comment).equal('comment'); + expect(entity.secure.organizationalSecurity).to.deep.equal(organizationalSecurityDef1); + }); + + it('should add parent privileges securityType', () => { + const parentPrivilegesDef1 = { + parent: 'Parent', + field: 'parentId', + }; + + entity.setSecure({ securityType: JDLSecurityType.ParentPrivileges, parentPrivileges: parentPrivilegesDef1, comment: 'comment' }); + expect(entity.secure.securityType).equal(JDLSecurityType.ParentPrivileges); + expect(entity.secure.comment).equal('comment'); + expect(entity.secure.parentPrivileges).to.deep.equal(parentPrivilegesDef1); + }); + + it('should add relational privileges securityType', () => { + const relPrivilegesDef1 = { + fromEntity: 'FromEntity', + fromField: 'FromField', + toEntity: 'ToEntity', + toField: 'toField', + }; + + entity.setSecure({ securityType: JDLSecurityType.RelPrivileges, relPrivileges: relPrivilegesDef1, comment: 'comment' }); + expect(entity.secure.securityType).equal(JDLSecurityType.RelPrivileges); + expect(entity.secure.comment).equal('comment'); + expect(entity.secure.relPrivileges).to.deep.equal(relPrivilegesDef1); + }); + }); + }); }); diff --git a/jdl/jhipster/json-entity.ts b/jdl/jhipster/json-entity.ts index 8e98ccff29d3..44d040e6675f 100644 --- a/jdl/jhipster/json-entity.ts +++ b/jdl/jhipster/json-entity.ts @@ -19,6 +19,8 @@ import { merge } from '../utils/object-utils.js'; import { upperFirst } from '../utils/string-utils.js'; +import JSONSecure from './json-secure.js'; +import { JDLSecurityType } from '../models/jdl-security-type.js'; /** * The JSONEntity class represents a read-to-be exported to JSON entity. @@ -77,6 +79,9 @@ class JSONEntity { if (merged.skipClient) { this.skipClient = merged.skipClient; } + if (args.secure && args.secure.securityType !== JDLSecurityType.None) { + this.secure = new JSONSecure(args.secure); + } this.applications = []; } @@ -115,6 +120,14 @@ class JSONEntity { setAnnotations(annotations = {}) { Object.assign(this.annotations, annotations); } + + setSecure(secure: any) { + if (secure && secure.securityType) { + this.secure = new JSONSecure(secure); + } else { + this.secure = new JSONSecure({ securityType: JDLSecurityType.None }); + } + } } export default JSONEntity; diff --git a/jdl/jhipster/json-secure.spec.ts b/jdl/jhipster/json-secure.spec.ts new file mode 100644 index 000000000000..bb587a31f85b --- /dev/null +++ b/jdl/jhipster/json-secure.spec.ts @@ -0,0 +1,145 @@ +import { expect } from 'chai'; +import { jsonSecure as JSONSecure } from '../jhipster/index.js'; +import { JDLSecurityType, PrivilegeActionType, RoleActionType } from '../models/jdl-security-type.js'; +import { JDLSecure } from '../models/index.js'; + +describe('JSONSecure', () => { + it('should initialize with securityType None and no additional properties', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.None }); + expect(obj.securityType).to.equal(JDLSecurityType.None); + expect(obj.roles).to.be.undefined; + expect(obj.privileges).to.be.undefined; + expect(obj.organizationalSecurity).to.be.undefined; + expect(obj.parentPrivileges).to.be.undefined; + expect(obj.relPrivileges).to.be.undefined; + expect(obj.comment).to.be.undefined; + }); + + it('should initialize with the provided securityType and comment', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.Roles, comment: 'Test' }); + expect(obj.securityType).to.equal(JDLSecurityType.Roles); + expect(obj.comment).to.equal('Test'); + }); + + it('should initialize roles if securityType is Roles', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.Roles, roles: [{ role: 'Test', actionList: [] }] }); + expect(obj.roles).to.deep.equal([{ role: 'Test', actionList: [] }]); + }); + + it('should initialize roles if securityType is Roles with actions Get and Post', () => { + const obj = new JSONSecure({ + securityType: JDLSecurityType.Roles, + roles: [{ role: 'Test', actionList: [RoleActionType.Get, RoleActionType.Post] }], + }); + expect(obj.roles).to.deep.equal([{ role: 'Test', actionList: [RoleActionType.Get, RoleActionType.Post] }]); + }); + + it('should initialize organizationalSecurity if securityType is OrganizationalSecurity', () => { + const obj = new JSONSecure({ + securityType: JDLSecurityType.OrganizationalSecurity, + organizationalSecurity: { resource: 'TestResource' }, + }); + expect(obj.securityType).to.equal(JDLSecurityType.OrganizationalSecurity); + expect(obj.organizationalSecurity).to.deep.equal({ resource: 'TestResource' }); + }); + + it('should initialize parentPrivileges if securityType is ParentPrivileges', () => { + const obj = new JSONSecure({ + securityType: JDLSecurityType.ParentPrivileges, + parentPrivileges: { parent: 'TestParent', field: 'TestField' }, + }); + expect(obj.securityType).to.equal(JDLSecurityType.ParentPrivileges); + expect(obj.parentPrivileges).to.deep.equal({ parent: 'TestParent', field: 'TestField' }); + }); + + it('should initialize privileges if securityType is Privileges', () => { + const obj = new JSONSecure({ + securityType: JDLSecurityType.Privileges, + privileges: [{ action: PrivilegeActionType.Read, privList: ['Admin'] }], + }); + expect(obj.securityType).to.equal(JDLSecurityType.Privileges); + expect(obj.privileges).to.deep.equal([{ action: PrivilegeActionType.Read, privList: ['Admin'] }]); + }); + + it('should initialize relational privileges securityType', () => { + const relPrivilegesDef1 = { + fromEntity: 'FromEntity', + fromField: 'FromField', + toEntity: 'ToEntity', + toField: 'toField', + }; + + const obj = new JSONSecure({ + securityType: JDLSecurityType.RelPrivileges, + relPrivileges: relPrivilegesDef1, + comment: 'comment', + }); + + expect(obj.securityType).equal(JDLSecurityType.RelPrivileges); + expect(obj.comment).equal('comment'); + expect(obj.relPrivileges).to.deep.equal(relPrivilegesDef1); + }); + + it('should initialize roles from JDLSecure if securityType is Roles with actions Get and Post', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.None }); + const jdl = new JDLSecure({ + securityType: JDLSecurityType.Roles, + roles: [{ role: 'Test', actionList: [RoleActionType.Get, RoleActionType.Post] }], + }); + obj.addConfigFromJDLSecure(jdl); + expect(obj.roles).to.deep.equal([{ role: 'Test', actionList: [RoleActionType.Get, RoleActionType.Post] }]); + }); + + it('should initialize organizationalSecurity from JDLSecure if securityType is OrganizationalSecurity', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.None }); + const jdl = new JDLSecure({ + securityType: JDLSecurityType.OrganizationalSecurity, + organizationalSecurity: { resource: 'TestResource' }, + }); + obj.addConfigFromJDLSecure(jdl); + expect(obj.securityType).to.equal(JDLSecurityType.OrganizationalSecurity); + expect(obj.organizationalSecurity).to.deep.equal({ resource: 'TestResource' }); + }); + + it('should initialize parentPrivileges if securityType is ParentPrivileges', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.None }); + const jdl = new JDLSecure({ + securityType: JDLSecurityType.ParentPrivileges, + parentPrivileges: { parent: 'TestParent', field: 'TestField' }, + }); + obj.addConfigFromJDLSecure(jdl); + expect(obj.securityType).to.equal(JDLSecurityType.ParentPrivileges); + expect(obj.parentPrivileges).to.deep.equal({ parent: 'TestParent', field: 'TestField' }); + }); + + it('should initialize privileges if securityType is Privileges', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.None }); + const jdl = new JDLSecure({ + securityType: JDLSecurityType.Privileges, + privileges: [{ action: PrivilegeActionType.Read, privList: ['Admin'] }], + }); + obj.addConfigFromJDLSecure(jdl); + expect(obj.securityType).to.equal(JDLSecurityType.Privileges); + expect(obj.privileges).to.deep.equal([{ action: PrivilegeActionType.Read, privList: ['Admin'] }]); + }); + + it('should initialize relational privileges securityType', () => { + const obj = new JSONSecure({ securityType: JDLSecurityType.None }); + const relPrivilegesDef1 = { + fromEntity: 'FromEntity', + fromField: 'FromField', + toEntity: 'ToEntity', + toField: 'toField', + }; + + const jdl = new JSONSecure({ + securityType: JDLSecurityType.RelPrivileges, + relPrivileges: relPrivilegesDef1, + comment: 'comment', + }); + obj.addConfigFromJDLSecure(jdl); + expect(obj.securityType).equal(JDLSecurityType.RelPrivileges); + expect(obj.comment).equal('comment'); + expect(obj.relPrivileges).to.deep.equal(relPrivilegesDef1); + }); +}); diff --git a/jdl/jhipster/json-secure.ts b/jdl/jhipster/json-secure.ts new file mode 100644 index 000000000000..58b309e518ae --- /dev/null +++ b/jdl/jhipster/json-secure.ts @@ -0,0 +1,132 @@ +import { JDLSecurityType, PrivilegeActionType, RoleActionType } from '../models/jdl-security-type.js'; +import { IJDLSecure } from '../models/jdl-secure.js'; + +interface IJSONRoleSecurity { + role: string; + actionList: RoleActionType[]; +} + +interface IJSONPrivilegeSecurity { + action: PrivilegeActionType; + privList: string[]; +} + +interface IJSONOrganizationalSecurity { + resource: string; +} + +interface IJSONParentPrivileges { + parent: string; + field: string; +} + +interface IJSONRelPrivileges { + fromEntity: string; + fromField: string; + toEntity: string; + toField: string; +} + +export interface IJSONSecure { + securityType: JDLSecurityType; + roles?: IJSONRoleSecurity[]; + privileges?: IJSONPrivilegeSecurity[]; + organizationalSecurity?: IJSONOrganizationalSecurity; + parentPrivileges?: IJSONParentPrivileges; + relPrivileges?: IJSONRelPrivileges; + comment?: string; +} +export class JSONSecure implements IJSONSecure { + securityType: JDLSecurityType = JDLSecurityType.None; + roles?: IJSONRoleSecurity[]; + privileges?: IJSONPrivilegeSecurity[]; + organizationalSecurity?: IJSONOrganizationalSecurity; + parentPrivileges?: IJSONParentPrivileges; + relPrivileges?: IJSONRelPrivileges; + comment?: string; + + /** + * Constructor for creating a secure definition. + * Throws an error if no arguments are provided or if the security type is missing. + * + * @param {IJSONSecure} args - The arguments containing the security type. + * @throws {Error} If no arguments are provided or if the security type is missing. + */ + constructor(args: IJSONSecure) { + if (!args) { + throw new Error('No arguments provided.'); + } + if (!args.securityType) { + throw new Error('The security type is mandatory to create a secure definition.'); + } + this.addConfig(args); + } + + /** + * Update the configuration with the provided JSON Secure object. + * + * @param {IJSONSecure} config - The JSON Secure object containing the configuration to be added. + * @returns {void} + */ + public addConfig(config: IJSONSecure): void { + this.securityType = config.securityType; + this.comment = config.comment; + switch (config.securityType) { + case JDLSecurityType.Roles: + this.roles = config.roles ? [...config.roles] : []; + break; + case JDLSecurityType.Privileges: + this.privileges = config.privileges ? [...config.privileges] : []; + break; + case JDLSecurityType.OrganizationalSecurity: + this.organizationalSecurity = config.organizationalSecurity; + break; + case JDLSecurityType.ParentPrivileges: + this.parentPrivileges = config.parentPrivileges; + break; + case JDLSecurityType.RelPrivileges: + this.relPrivileges = config.relPrivileges; + break; + default: + this.securityType = JDLSecurityType.None; + } + this.removeUndefinedproperties(); + } + + /** + * Adds configuration from a JDL Secure object. + * + * @param {IJDLSecure} config - The JDL Secure object containing the configuration to add. + * @return {void} + */ + public addConfigFromJDLSecure(config: IJDLSecure): void { + this.securityType = config.securityType; + this.comment = config.comment; + switch (config.securityType) { + case JDLSecurityType.Roles: + this.roles = config.roles ? [...config.roles] : []; + break; + case JDLSecurityType.Privileges: + this.privileges = config.privileges ? [...config.privileges] : []; + break; + case JDLSecurityType.OrganizationalSecurity: + this.organizationalSecurity = config.organizationalSecurity; + break; + case JDLSecurityType.ParentPrivileges: + this.parentPrivileges = config.parentPrivileges; + break; + case JDLSecurityType.RelPrivileges: + this.relPrivileges = config.relPrivileges; + break; + default: + this.securityType = JDLSecurityType.None; + } + this.removeUndefinedproperties(); + } + + public removeUndefinedproperties(): void { + Object.keys(this).forEach(key => this[key] === undefined && delete this[key]); + } +} + +export default JSONSecure; diff --git a/jdl/jhipster/reserved-keywords/jhipster.ts b/jdl/jhipster/reserved-keywords/jhipster.ts index 30a28fdffd2c..bf3d994eafb4 100644 --- a/jdl/jhipster/reserved-keywords/jhipster.ts +++ b/jdl/jhipster/reserved-keywords/jhipster.ts @@ -36,4 +36,5 @@ export default [ 'PRINCIPAL', 'ENTITY', 'RESULT', + 'SECURE', ]; diff --git a/jdl/jhipster/unary-options.spec.ts b/jdl/jhipster/unary-options.spec.ts index ff6a4d3fe08f..cfc5a6460ccf 100644 --- a/jdl/jhipster/unary-options.spec.ts +++ b/jdl/jhipster/unary-options.spec.ts @@ -18,7 +18,7 @@ */ /* eslint-disable no-new, no-unused-expressions */ -import { before, it, describe, expect, expect as jestExpect } from 'esmocha'; +import { before, it, describe, expect as jestExpect } from 'esmocha'; import { expect } from 'chai'; import { unaryOptions } from '../jhipster/index.js'; @@ -58,6 +58,7 @@ describe('jdl - UnaryOptions', () => { "readOnly", "filter", "embedded", + "secure", ] `); }); diff --git a/jdl/jhipster/unary-options.ts b/jdl/jhipster/unary-options.ts index 07ca27882b81..0c83ad7eb0bb 100644 --- a/jdl/jhipster/unary-options.ts +++ b/jdl/jhipster/unary-options.ts @@ -25,6 +25,7 @@ const Options: any = { READ_ONLY: 'readOnly', FILTER: 'filter', EMBEDDED: 'embedded', + SECURE: 'secure', }; const optionNames = Object.values(Options); diff --git a/jdl/models/index.ts b/jdl/models/index.ts index e987edd16be4..ab3a7f71ea98 100644 --- a/jdl/models/index.ts +++ b/jdl/models/index.ts @@ -19,3 +19,4 @@ // eslint-disable-next-line import/prefer-default-export export { default as JDLEntity } from './jdl-entity.js'; export { default as JDLEnum } from './jdl-enum.js'; +export { default as JDLSecure } from './jdl-secure.js'; diff --git a/jdl/models/jdl-entity.ts b/jdl/models/jdl-entity.ts index 61fda34ed943..b3dd46bbea37 100644 --- a/jdl/models/jdl-entity.ts +++ b/jdl/models/jdl-entity.ts @@ -21,11 +21,14 @@ import { upperFirst } from 'lodash-es'; import { merge } from '../utils/object-utils.js'; import getTableNameFromEntityName from '../jhipster/entity-table-name-creator.js'; import JDLField from './jdl-field.js'; +import { JDLSecure } from './index.js'; +import { IJDLSecure } from './jdl-secure.js'; export default class JDLEntity { name: any; tableName: any; fields: Record; + secure?: IJDLSecure; comment: any; annotations: Record; @@ -39,6 +42,11 @@ export default class JDLEntity { this.fields = merged.fields; this.comment = merged.comment; this.annotations = merged.annotations ?? {}; + if (merged.secure) { + this.secure = merged.secure; + } else { + delete this.secure; + } } /** @@ -56,6 +64,10 @@ export default class JDLEntity { this.fields[field.name] = field; } + addSecure(secure: IJDLSecure) { + this.secure = new JDLSecure(secure); + } + forEachField(functionToApply: (field: JDLField, index: number, array: JDLField[]) => void) { if (!functionToApply) { throw new Error('A function must be passed to iterate over fields'); diff --git a/jdl/models/jdl-secure.spec.ts b/jdl/models/jdl-secure.spec.ts new file mode 100644 index 000000000000..c92d5af4e131 --- /dev/null +++ b/jdl/models/jdl-secure.spec.ts @@ -0,0 +1,145 @@ +/** + * Copyright 2013-2023 the original author or authors from the JHipster project. + * + * This file is part of the JHipster project, see https://www.jhipster.tech/ + * for more information. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { JDLSecurityType, PrivilegeActionType, RoleActionType } from './jdl-security-type.js'; +import { JDLSecure } from './index.js'; +import { IJDLSecure, IOrganizationalSecurity, IParentPrivileges, IPrivilegeSecurity, IRelPrivileges, IRoleSecurity } from './jdl-secure.js'; + +describe('JDLSecure', () => { + it('should initialize with securityType None and no additional properties', () => { + const obj = new JDLSecure({ securityType: JDLSecurityType.None }); + expect(obj.securityType).to.equal(JDLSecurityType.None); + expect(obj.roles).to.be.undefined; + expect(obj.privileges).to.be.undefined; + expect(obj.organizationalSecurity).to.be.undefined; + expect(obj.parentPrivileges).to.be.undefined; + expect(obj.relPrivileges).to.be.undefined; + expect(obj.comment).to.be.undefined; + }); + + it('should initialize with the provided securityType and comment', () => { + const obj = new JDLSecure({ securityType: JDLSecurityType.Roles, comment: 'Test' }); + obj.addConfig({ securityType: JDLSecurityType.Roles, comment: 'Test' }); + expect(obj.securityType).to.equal(JDLSecurityType.Roles); + expect(obj.comment).to.equal('Test'); + }); + + it('should initialize roles if securityType is Roles', () => { + const obj = new JDLSecure({ securityType: JDLSecurityType.Roles }); + obj.addConfig({ securityType: JDLSecurityType.Roles, roles: [{ role: 'Test', actionList: [] }] }); + expect(obj.roles).to.deep.equal([{ role: 'Test', actionList: [] }]); + }); + + it('should initialize roles if securityType is Roles with actions Get and Post', () => { + const obj = new JDLSecure({ + securityType: JDLSecurityType.Roles, + roles: [{ role: 'Test', actionList: [RoleActionType.Get, RoleActionType.Post] }], + }); + expect(obj.roles).to.deep.equal([{ role: 'Test', actionList: [RoleActionType.Get, RoleActionType.Post] }]); + }); + + it('should initialize organizationalSecurity if securityType is OrganizationalSecurity', () => { + const obj = new JDLSecure({ + securityType: JDLSecurityType.OrganizationalSecurity, + organizationalSecurity: { resource: 'TestResource' }, + }); + expect(obj.securityType).to.equal(JDLSecurityType.OrganizationalSecurity); + expect(obj.organizationalSecurity).to.deep.equal({ resource: 'TestResource' }); + }); + + it('should initialize parentPrivileges if securityType is ParentPrivileges', () => { + const obj = new JDLSecure({ securityType: JDLSecurityType.ParentPrivileges }); + obj.addConfig({ + securityType: JDLSecurityType.ParentPrivileges, + parentPrivileges: { parent: 'TestParent', field: 'TestField' }, + }); + expect(obj.securityType).to.equal(JDLSecurityType.ParentPrivileges); + expect(obj.parentPrivileges).to.deep.equal({ parent: 'TestParent', field: 'TestField' }); + }); + + it('should initialize privileges if securityType is Privileges', () => { + const obj = new JDLSecure({ securityType: JDLSecurityType.Privileges }); + obj.addConfig({ + securityType: JDLSecurityType.Privileges, + privileges: [{ action: PrivilegeActionType.Read, privList: ['Admin'] }], + }); + expect(obj.securityType).to.equal(JDLSecurityType.Privileges); + expect(obj.privileges).to.deep.equal([{ action: PrivilegeActionType.Read, privList: ['Admin'] }]); + }); + + it('should initialize relational privileges securityType', () => { + const relPrivilegesDef1 = { + fromEntity: 'FromEntity', + fromField: 'FromField', + toEntity: 'ToEntity', + toField: 'toField', + }; + + const obj = new JDLSecure({ + securityType: JDLSecurityType.RelPrivileges, + relPrivileges: relPrivilegesDef1, + comment: 'comment', + }); + + expect(obj.securityType).equal(JDLSecurityType.RelPrivileges); + expect(obj.comment).equal('comment'); + expect(obj.relPrivileges).to.deep.equal(relPrivilegesDef1); + }); + + it('should correctly convert a configuration with JDLSecurityType.Roles to string', function () { + const roleSec: IRoleSecurity[] = [{ role: 'ADMIN', actionList: [RoleActionType.Post, RoleActionType.Put] }]; + const config: IJDLSecure = { securityType: JDLSecurityType.Roles, roles: roleSec }; + const jdlSec = new JDLSecure(config); + + expect(jdlSec.toString()).to.equal('roles ADMIN ( POST PUT)'); + }); + + it('should correctly convert a configuration with JDLSecurityType.Privileges to string', function () { + const privSec: IPrivilegeSecurity[] = [{ action: PrivilegeActionType.Read, privList: ['privilege1', 'privilege2'] }]; + const config: IJDLSecure = { securityType: JDLSecurityType.Privileges, privileges: privSec }; + const jdlSec = new JDLSecure(config); + + expect(jdlSec.toString()).to.equal('privileges read ( privilege1 privilege2)'); + }); + + it('should correctly convert a configuration with JDLSecurityType.OrganizationalSecurity to string', function () { + const orgSec: IOrganizationalSecurity = { resource: 'resource1' }; + const config: IJDLSecure = { securityType: JDLSecurityType.OrganizationalSecurity, organizationalSecurity: orgSec }; + const jdlSec = new JDLSecure(config); + + expect(jdlSec.toString()).to.equal('organizationalSecurity resource resource1'); + }); + + it('should correctly convert a configuration with JDLSecurityType.ParentPrivileges to string', function () { + const ppSec: IParentPrivileges = { parent: 'parent1', field: 'field1' }; + const config: IJDLSecure = { securityType: JDLSecurityType.ParentPrivileges, parentPrivileges: ppSec }; + const jdlSec = new JDLSecure(config); + + expect(jdlSec.toString()).to.equal('parentPrivileges parent1 field1'); + }); + + it('should correctly convert a configuration with JDLSecurityType.RelPrivileges to string', function () { + const rpSec: IRelPrivileges = { fromEntity: 'entity1', fromField: 'field1', toEntity: 'entity2', toField: 'field2' }; + const config: IJDLSecure = { securityType: JDLSecurityType.RelPrivileges, relPrivileges: rpSec }; + const jdlSec = new JDLSecure(config); + + expect(jdlSec.toString()).to.equal('relPrivileges entity1 field1 entity2 field2'); + }); +}); diff --git a/jdl/models/jdl-secure.ts b/jdl/models/jdl-secure.ts new file mode 100644 index 000000000000..3714808690c6 --- /dev/null +++ b/jdl/models/jdl-secure.ts @@ -0,0 +1,139 @@ +/** + * Copyright 2013-2023 the original author or authors from the JHipster project. + * + * This file is part of the JHipster project, see https://www.jhipster.tech/ + * for more information. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JDLSecurityType, PrivilegeActionType, RoleActionType } from './jdl-security-type.js'; + +export interface IRoleSecurity { + role: string; + actionList: RoleActionType[]; +} + +export interface IPrivilegeSecurity { + action: PrivilegeActionType; + privList: string[]; +} + +export interface IOrganizationalSecurity { + resource: string; +} + +export interface IParentPrivileges { + parent: string; + field: string; +} + +export interface IRelPrivileges { + fromEntity: string; + fromField: string; + toEntity: string; + toField: string; +} + +export interface IJDLSecure { + securityType: JDLSecurityType; + roles?: IRoleSecurity[]; + privileges?: IPrivilegeSecurity[]; + organizationalSecurity?: IOrganizationalSecurity; + parentPrivileges?: IParentPrivileges; + relPrivileges?: IRelPrivileges; + comment?: string; +} +export default class JDLSecure implements IJDLSecure { + securityType: JDLSecurityType = JDLSecurityType.None; + roles?: IRoleSecurity[]; + privileges?: IPrivilegeSecurity[]; + organizationalSecurity?: IOrganizationalSecurity; + parentPrivileges?: IParentPrivileges; + relPrivileges?: IRelPrivileges; + comment?: string; + + constructor(args: IJDLSecure) { + if (!args) { + throw new Error('A secure definition must at least contain a security type.'); + } + if (!args.securityType) { + throw new Error('The security type is mandatory to create a secure definition.'); + } + this.addConfig(args); + } + + public addConfig(config: IJDLSecure) { + this.securityType = config.securityType; + this.comment = config.comment; + switch (config.securityType) { + case JDLSecurityType.Roles: + this.roles = config.roles ? [...config.roles] : []; + break; + case JDLSecurityType.Privileges: + this.privileges = config.privileges ? [...config.privileges] : []; + break; + case JDLSecurityType.OrganizationalSecurity: + this.organizationalSecurity = config.organizationalSecurity; + break; + case JDLSecurityType.ParentPrivileges: + this.parentPrivileges = config.parentPrivileges; + break; + case JDLSecurityType.RelPrivileges: + this.relPrivileges = config.relPrivileges; + break; + default: + this.securityType = JDLSecurityType.None; + } + this.removeUndefinedproperties(); + } + + public toString(): string { + let string = ''; + if (this.comment) { + string += `/**\n${this.comment + .split('\n') + .map(line => ` * ${line}\n`) + .join('')} */\n`; + } + string += `${this.securityType}`; + if (this.securityType === JDLSecurityType.Roles) { + this.roles?.forEach(item => { + string += ` ${item.role} (`; + item.actionList.forEach(action => { + string += ` ${action}`; + }); + string += ')'; + }); + } else if (this.securityType === JDLSecurityType.Privileges) { + this.privileges?.forEach(item => { + string += ` ${item.action} (`; + item.privList.forEach(priv => { + string += ` ${priv}`; + }); + string += ')'; + }); + } else if (this.securityType === JDLSecurityType.OrganizationalSecurity && this.organizationalSecurity) { + string += ` resource ${this.organizationalSecurity.resource}`; + } else if (this.securityType === JDLSecurityType.ParentPrivileges && this.parentPrivileges) { + string += ` ${this.parentPrivileges.parent} ${this.parentPrivileges.field}`; + } else if (this.securityType === JDLSecurityType.RelPrivileges && this.relPrivileges) { + string += ` ${this.relPrivileges.fromEntity} ${this.relPrivileges.fromField} ${this.relPrivileges.toEntity} ${this.relPrivileges.toField}`; + } + return string; + } + + public removeUndefinedproperties(): void { + Object.keys(this).forEach(key => this[key] === undefined && delete this[key]); + } +} diff --git a/jdl/models/jdl-security-type.ts b/jdl/models/jdl-security-type.ts new file mode 100644 index 000000000000..a26de5e31320 --- /dev/null +++ b/jdl/models/jdl-security-type.ts @@ -0,0 +1,22 @@ +export enum JDLSecurityType { + None = 'none', + Roles = 'roles', + Privileges = 'privileges', + OrganizationalSecurity = 'organizationalSecurity', + ParentPrivileges = 'parentPrivileges', + RelPrivileges = 'relPrivileges', + CustomSecurity = 'customSecurity', +} + +export enum PrivilegeActionType { + Read = 'read', + Write = 'write', + Execute = 'execute', +} + +export enum RoleActionType { + Put = 'PUT', + Post = 'POST', + Get = 'GET', + Delete = 'DELETE', +} diff --git a/jdl/parsing/dsl-api.spec.ts b/jdl/parsing/dsl-api.spec.ts index 2ea85a11c1e5..299400af63d6 100644 --- a/jdl/parsing/dsl-api.spec.ts +++ b/jdl/parsing/dsl-api.spec.ts @@ -123,7 +123,7 @@ describe('jdl - JDL DSL API', () => { }); it('should provide suggestions', () => { - expect(result).to.have.lengthOf(11); + expect(result).to.have.lengthOf(12); expect(result).to.have.members([ tokens.AT, tokens.APPLICATION, @@ -132,6 +132,7 @@ describe('jdl - JDL DSL API', () => { tokens.ENTITY, tokens.RELATIONSHIP, tokens.ENUM, + tokens.SECURE, tokens.JAVADOC, tokens.UNARY_OPTION, tokens.BINARY_OPTION, diff --git a/jdl/parsing/grammar.spec.ts b/jdl/parsing/grammar.spec.ts index 6d4d3247ed32..968081bbfc55 100644 --- a/jdl/parsing/grammar.spec.ts +++ b/jdl/parsing/grammar.spec.ts @@ -19,7 +19,7 @@ /* eslint-disable no-unused-expressions */ -import { before, it, describe, expect, expect as jestExpect } from 'esmocha'; +import { before, it, describe, expect as jestExpect } from 'esmocha'; import { expect } from 'chai'; import { parseFromContent } from '../readers/jdl-reader.js'; import { relationshipTypes, validations, unaryOptions, binaryOptions } from '../jhipster/index.js'; @@ -2040,4 +2040,34 @@ entity A { }); }); }); + context('when parsing secure option', () => { + context('define entities', () => { + it('secure using the * keyword', () => { + const content = parseFromContent('secure * with roles { ROLE_ADMIN allows (get)}'); + const parsedSecure = content.secure[0]; + expect(parsedSecure.entityNames).to.deep.equal(['*']); + }); + it('secure using the all keyword', () => { + const content = parseFromContent('secure * with roles { ROLE_ADMIN allows (get)}'); + const parsedSecure = content.secure[0]; + expect(parsedSecure.entityNames).to.deep.equal(['*']); + }); + it('secure using several entities', () => { + const content = parseFromContent('secure A,B,C with roles { ROLE_ADMIN allows (get)}'); + const parsedSecure = content.secure[0]; + expect(parsedSecure.entityNames).to.deep.equal(['A', 'B', 'C']); + }); + it('secure using the except keyword', () => { + const content = parseFromContent('secure * except A with roles { ROLE_ADMIN allows (get)}'); + const parsedSecure = content.secure[0]; + expect(parsedSecure.entityNames).to.deep.equal(['*']); + expect(parsedSecure.excludedNames).to.deep.equal(['A']); + }); + it('secure entity with role that is empty', () => { + const content = parseFromContent('secure A with roles { ROLE_ADMIN allows ()}'); + const parsedSecure = content.secure[0]; + expect(parsedSecure.entityNames).to.deep.equal(['A']); + }); + }); + }); }); diff --git a/jdl/parsing/jdl-ast-builder-visitor.ts b/jdl/parsing/jdl-ast-builder-visitor.ts index 91df0dedf107..99990b84f80a 100644 --- a/jdl/parsing/jdl-ast-builder-visitor.ts +++ b/jdl/parsing/jdl-ast-builder-visitor.ts @@ -52,6 +52,7 @@ export default class JDLAstBuilderVisitor extends BaseJDLCSTVisitor { enums: [], options: {}, useOptions: [], + secure: [], }; if (context.constantDeclaration) { @@ -80,6 +81,9 @@ export default class JDLAstBuilderVisitor extends BaseJDLCSTVisitor { if (context.enumDeclaration) { ast.enums = context.enumDeclaration.map(this.visit, this); } + if (context.secureDeclaration) { + ast.secure = context.secureDeclaration.map(this.visit, this); + } if (context.unaryOptionDeclaration) { context.unaryOptionDeclaration.map(this.visit, this).forEach(option => { @@ -163,6 +167,150 @@ export default class JDLAstBuilderVisitor extends BaseJDLCSTVisitor { }; } + secureDeclaration(context) { + // extract list of entities + const contextName = 'secureEntityList'; + + if (!context[contextName]) { + return {}; + } + + const { entityNames, excludedNames } = this.visit(context.secureEntityList); + let securityType = ''; + let roles = []; + let privileges = []; + let organizationalSecurity = { resource: null }; + let parentPrivileges = { parent: null, field: null }; + let relPrivileges = { fromEntity: null, fromField: null, toEntity: null, toField: null }; + + if (context.rolesSecurity) { + securityType = 'roles'; + roles = this.visit(context.rolesSecurity); + } + + if (context.privilegesSecurity) { + securityType = 'privileges'; + privileges = this.visit(context.privilegesSecurity); + } + + if (context.organizationalSecurity) { + securityType = 'organizationalSecurity'; + organizationalSecurity = this.visit(context.organizationalSecurity); + } + + if (context.parentPrivilegesSecurity) { + securityType = 'parentPrivileges'; + parentPrivileges = this.visit(context.parentPrivilegesSecurity); + } + + if (context.relPrivilegesSecurity) { + securityType = 'relPrivileges'; + relPrivileges = this.visit(context.relPrivilegesSecurity); + } + + return { + entityNames, + excludedNames, + securityType, + roles, + privileges, + parentPrivileges, + relPrivileges, + organizationalSecurity, + }; + } + + rolesSecurity(context) { + // return _.map(context.roleDefinition, element => this.visit(element)); + return context.roleDefinition.map(element => this.visit(element)); + } + + privilegesSecurity(context) { + // return _.map(context.privDefinition, element => this.visit(element)); + return context.privDefinition.map(element => this.visit(element)); + } + + organizationalSecurity(context) { + const res = context.NAME[0].image; + return { resource: res }; + } + + resourceDefinition(context) { + const res = context.NAME[0].image; + return { resource: res }; + } + + parentPrivilegesSecurity(context) { + // return _.map(context.parentPriveleges, element => this.visit(element)); + + const ent1 = context.parentPrivileges[0].children.NAME[0].image; + const fld = context.parentPrivileges[0].children.NAME[1].image; + + return { parent: ent1, field: fld }; + } + + relPrivilegesSecurity(context) { + return this.relPrivileges(context.relPrivileges[0].children); + } + + roleDefinition(context) { + const role = context.NAME[0].image; + let actionList = []; + if (context.rolePropList) { + // actionList = context.enumPropList[0].children.NAME.map(nameToken => nameToken.image); + if (context.rolePropList[0].children.NAME) { + actionList = context.rolePropList[0].children.NAME.map(nameToken => nameToken.image.toUpperCase()); + } + } + + return { role, actionList }; + } + + privDefinition(context) { + const action = context.NAME[0].image.toLowerCase(); + let privList = []; + if (context.rolePropList) { + // privList = context.enumPropList[0].children.NAME.map(nameToken => nameToken.image); + privList = context.rolePropList[0].children.NAME.map(nameToken => nameToken.image); + } + + return { action, privList }; + } + + parentPrivileges(context) { + const ent1 = context.NAME[0].image; + const fld = context.NAME[1].image; + + return { parent: ent1, field: fld }; + } + + relPrivileges(context) { + const ent1 = context.NAME[0].image; + const fld1 = context.NAME[1].image; + const ent2 = context.NAME[2].image; + const fld2 = context.NAME[3].image; + + return { fromEntity: ent1, fromField: fld1, toEntity: ent2, toField: fld2 }; + } + + secureEntityList(context) { + let entityList: any[] = []; + if (context.NAME) { + entityList = context.NAME.map(nameToken => nameToken.image); + } + + const entityOnlyListContainsAll = entityList.length === 1 && entityList[0] === 'all'; + // if (context.ALL || context.STAR) { + if (context.STAR || entityOnlyListContainsAll) { + entityList = ['*']; + } + let exclusionList: any[] = []; + if (context.exclusion) { + exclusionList = context.exclusion[0].children.NAME.map(nameToken => nameToken.image); + } + return { entityNames: deduplicate(entityList), excludedNames: deduplicate(exclusionList) }; + } + annotationDeclaration(context) { const optionName = context.option[0].image; if (!context.value) { @@ -389,6 +537,23 @@ export default class JDLAstBuilderVisitor extends BaseJDLCSTVisitor { return prop; } + rolePropList(context) { + return context.roleProp.map(this.visit, this); + } + + roleProp(context) { + const prop: any = { + key: context.rolePropKey[0].image, + }; + if (context.rolePropValue) { + prop.value = context.rolePropValue[0].image; + } + if (context.enumPropValueWithQuotes) { + prop.value = context.rolePropValueWithQuotes[0].image.replace(/"/g, ''); + } + return prop; + } + entityList(context) { let entityList: any[] = []; if (context.NAME) { diff --git a/jdl/parsing/jdl-parser.ts b/jdl/parsing/jdl-parser.ts index ccc613fe1461..0e2de64747d4 100644 --- a/jdl/parsing/jdl-parser.ts +++ b/jdl/parsing/jdl-parser.ts @@ -56,6 +56,19 @@ export default class JDLParser extends CstParser { this.enumDeclaration(); this.enumPropList(); this.enumProp(); + this.rolePropList(); + this.roleProp(); + this.secureDeclaration(); + this.rolesSecurity(); + this.privilegesSecurity(); + this.organizationalSecurity(); + this.parentPrivilegesSecurity(); + this.relPrivilegesSecurity(); + this.roleDefinition(); + this.privDefinition(); + this.parentPrivileges(); + this.relPrivileges(); + this.secureEntityList(); this.entityList(); this.exclusion(); this.useOptionDeclaration(); @@ -91,6 +104,7 @@ export default class JDLParser extends CstParser { { ALT: () => this.SUBRULE(this.entityDeclaration) }, { ALT: () => this.SUBRULE(this.relationDeclaration) }, { ALT: () => this.SUBRULE(this.enumDeclaration) }, + { ALT: () => this.SUBRULE(this.secureDeclaration) }, { ALT: () => this.CONSUME(LexerTokens.JAVADOC) }, { ALT: () => this.SUBRULE(this.useOptionDeclaration) }, { ALT: () => this.SUBRULE(this.unaryOptionDeclaration) }, @@ -380,6 +394,171 @@ export default class JDLParser extends CstParser { }); } + secureDeclaration(): any { + this.RULE('secureDeclaration', () => { + this.CONSUME(LexerTokens.SECURE); + this.SUBRULE(this.secureEntityList); + this.OR([ + { ALT: () => this.SUBRULE(this.rolesSecurity) }, + { ALT: () => this.SUBRULE(this.privilegesSecurity) }, + { ALT: () => this.SUBRULE(this.organizationalSecurity) }, + { ALT: () => this.SUBRULE(this.parentPrivilegesSecurity) }, + { ALT: () => this.SUBRULE(this.relPrivilegesSecurity) }, + ]); + }); + } + + rolesSecurity(): any { + this.RULE('rolesSecurity', () => { + this.CONSUME(LexerTokens.ROLES); + this.CONSUME(LexerTokens.LCURLY); + this.MANY(() => { + this.SUBRULE(this.roleDefinition); + this.OPTION(() => { + this.CONSUME(LexerTokens.COMMA); + }); + }); + this.CONSUME(LexerTokens.RCURLY); + }); + } + + privilegesSecurity(): any { + this.RULE('privilegesSecurity', () => { + this.CONSUME(LexerTokens.PRIVILEGES); + this.CONSUME(LexerTokens.LCURLY); + this.MANY(() => { + this.SUBRULE(this.privDefinition); + this.OPTION(() => { + this.CONSUME(LexerTokens.COMMA); + }); + }); + this.CONSUME(LexerTokens.RCURLY); + }); + } + + organizationalSecurity(): any { + this.RULE('organizationalSecurity', () => { + this.CONSUME(LexerTokens.ORGANIZATIONAL_SECURITY); + this.CONSUME(LexerTokens.LCURLY); + + this.OPTION(() => { + this.SUBRULE(this.comment); + }); + this.CONSUME(LexerTokens.RESOURCE_NAME); + this.CONSUME(LexerTokens.NAME); + this.CONSUME(LexerTokens.RCURLY); + }); + } + + parentPrivilegesSecurity(): any { + this.RULE('parentPrivilegesSecurity', () => { + this.CONSUME(LexerTokens.PARENT_PRIVILEGES); + this.CONSUME(LexerTokens.LCURLY); + this.SUBRULE(this.parentPrivileges); + this.CONSUME(LexerTokens.RCURLY); + }); + } + + relPrivilegesSecurity(): any { + this.RULE('relPrivilegesSecurity', () => { + this.CONSUME(LexerTokens.REL_PRIVILEGES); + this.CONSUME(LexerTokens.LCURLY); + this.SUBRULE(this.relPrivileges); + this.CONSUME(LexerTokens.RCURLY); + }); + } + + rolePropList(): any { + this.RULE('rolePropList', () => { + this.CONSUME(LexerTokens.LPAREN); + this.MANY_SEP({ + SEP: LexerTokens.COMMA, + DEF: () => { + this.CONSUME(LexerTokens.NAME); + }, + }); + this.CONSUME(LexerTokens.RPAREN); + }); + } + + roleProp(): any { + this.RULE('roleProp', () => { + this.OPTION(() => { + this.OR([ + { ALT: () => this.CONSUME2(LexerTokens.STRING, { LABEL: 'rolePropValueWithQuotes' }) }, + { ALT: () => this.CONSUME3(LexerTokens.NAME, { LABEL: 'rolePropValue' }) }, + ]); + }); + }); + } + + roleDefinition(): any { + this.RULE('roleDefinition', () => { + this.OPTION(() => { + this.SUBRULE(this.comment); + }); + this.CONSUME(LexerTokens.NAME); + this.CONSUME(LexerTokens.ALLOWS); + this.SUBRULE(this.rolePropList); + }); + } + + privDefinition(): any { + this.RULE('privDefinition', () => { + this.OPTION(() => { + this.SUBRULE(this.comment); + }); + this.CONSUME(LexerTokens.NAME); + this.CONSUME(LexerTokens.REQUIRE_PRIVS); + this.SUBRULE(this.rolePropList); + }); + } + + parentPrivileges(): any { + this.RULE('parentPrivileges', () => { + this.OPTION(() => { + this.SUBRULE(this.comment); + }); + this.CONSUME(LexerTokens.PARENT); + this.CONSUME(LexerTokens.NAME); + this.CONSUME(LexerTokens.FIELD); + this.CONSUME2(LexerTokens.NAME); + }); + } + + relPrivileges(): any { + this.RULE('relPrivileges', () => { + this.OPTION(() => { + this.SUBRULE(this.comment); + }); + this.CONSUME(LexerTokens.FROM_ENTITY); + this.CONSUME(LexerTokens.NAME); + this.CONSUME(LexerTokens.FIELD); + this.CONSUME2(LexerTokens.NAME); + this.CONSUME(LexerTokens.TO_ENTITY); + this.CONSUME3(LexerTokens.NAME); + this.CONSUME2(LexerTokens.FIELD); + this.CONSUME4(LexerTokens.NAME); + }); + } + + secureEntityList(): any { + this.RULE('secureEntityList', () => { + this.MANY({ + GATE: () => this.LA(2).tokenType === LexerTokens.COMMA, + DEF: () => { + this.CONSUME(LexerTokens.NAME); + this.CONSUME(LexerTokens.COMMA); + }, + }); + this.OR([{ ALT: () => this.CONSUME(LexerTokens.STAR) }, { ALT: () => this.CONSUME1(LexerTokens.NAME) }]); + this.OPTION(() => { + this.SUBRULE(this.exclusion); + }); + this.CONSUME(LexerTokens.WITH); + }); + } + entityList(): any { this.RULE('entityList', () => { this.commonEntityList(); diff --git a/jdl/parsing/lexer/lexer.ts b/jdl/parsing/lexer/lexer.ts index 80ac714d1fa5..cb302955abbd 100644 --- a/jdl/parsing/lexer/lexer.ts +++ b/jdl/parsing/lexer/lexer.ts @@ -106,6 +106,21 @@ createTokenFromConfig({ name: 'ENUM', pattern: 'enum' }); // Relationship-related createTokenFromConfig({ name: 'RELATIONSHIP', pattern: 'relationship' }); createTokenFromConfig({ name: 'BUILT_IN_ENTITY', pattern: BUILT_IN_ENTITY }); +// Security +createTokenFromConfig({ name: 'SECURE', pattern: 'secure' }); +createTokenFromConfig({ name: 'ROLES', pattern: 'roles' }); +createTokenFromConfig({ name: 'CUSTOM_SECURITY', pattern: 'privilegesSecurity' }); +createTokenFromConfig({ name: 'ALLOWS', pattern: 'allows' }); +createTokenFromConfig({ name: 'REQUIRE_PRIVS', pattern: 'requirePrivs' }); +createTokenFromConfig({ name: 'PRIVILEGES', pattern: 'privileges' }); +createTokenFromConfig({ name: 'PARENT_PRIVILEGES', pattern: 'parentPrivileges' }); +createTokenFromConfig({ name: 'REL_PRIVILEGES', pattern: 'relPrivileges' }); +createTokenFromConfig({ name: 'ORGANIZATIONAL_SECURITY', pattern: 'organizationalSecurity' }); +createTokenFromConfig({ name: 'RESOURCE_NAME', pattern: 'resourceName' }); +createTokenFromConfig({ name: 'PARENT', pattern: 'parent' }); +createTokenFromConfig({ name: 'FROM_ENTITY', pattern: 'fromEntity' }); +createTokenFromConfig({ name: 'TO_ENTITY', pattern: 'toEntity' }); +createTokenFromConfig({ name: 'FIELD', pattern: 'field' }); // Category For the relationship type key names RelationshipTypeTokens.tokens.forEach(token => { diff --git a/jdl/parsing/validator.ts b/jdl/parsing/validator.ts index 2540fd074ea8..f0d188d576dc 100644 --- a/jdl/parsing/validator.ts +++ b/jdl/parsing/validator.ts @@ -521,6 +521,18 @@ class JDLSyntaxValidatorVisitor extends BaseJDLCSTVisitorWithDefaults { }); } + rolePropList(context) { + super.rolePropList(context); + if (context.NAME && context.NAME.length > 0) { + context.NAME.forEach(nameToken => { + const propValue = nameToken.image; + if (propValue) { + this.checkNameSyntax(propValue, ENUM_PROP_VALUE_PATTERN, 'role property value'); + } + }); + } + } + entityList(context) { super.entityList(context); if (context.NAME) { diff --git a/jdl/utils/object-utils.ts b/jdl/utils/object-utils.ts index 8b04a9975cbe..0bf87a6ce7b1 100644 --- a/jdl/utils/object-utils.ts +++ b/jdl/utils/object-utils.ts @@ -48,6 +48,20 @@ function removeEntriesWithUndefinedValue(entity: any) { }); } +function checkIfSecurityDiffers(firstEntity, secondEntity) { + if (firstEntity.security) { + if (!secondEntity.security) { + return true; + } + } + if (secondEntity.security) { + if (!firstEntity.security) { + return true; + } + } + return false; +} + export function areEntitiesEqual(firstEntity: any, secondEntity: any) { removeEntriesWithUndefinedValue(firstEntity); removeEntriesWithUndefinedValue(secondEntity); @@ -56,17 +70,60 @@ export function areEntitiesEqual(firstEntity: any, secondEntity: any) { firstEntity.relationships.length !== secondEntity.relationships.length || firstEntity.documentation !== secondEntity.documentation || firstEntity.entityTableName !== secondEntity.entityTableName || - Object.keys(firstEntity).length !== Object.keys(secondEntity).length + Object.keys(firstEntity).length !== Object.keys(secondEntity).length || + checkIfSecurityDiffers(firstEntity, secondEntity) ) { return false; } return ( areFieldsEqual(firstEntity.fields, secondEntity.fields) && areRelationshipsEqual(firstEntity.relationships, secondEntity.relationships) && - areOptionsTheSame(firstEntity, secondEntity) + areOptionsTheSame(firstEntity, secondEntity) && + areSecurityOptionsAreTheSame(firstEntity, secondEntity) ); } +function areSecurityOptionsAreTheSame(firstEntity, secondEntity) { + let result = true; + if (firstEntity.secure && secondEntity.secure) { + result = firstEntity.secure.securityType === secondEntity.secure.securityType; + if (result) { + if (firstEntity.secure.securityType === 'roles') { + if (firstEntity.secure.roles && secondEntity.secure.roles) { + result = firstEntity.secure.roles.length === secondEntity.secure.roles.length; + if (result) { + for (let i = 0; i < firstEntity.secure.roles.length; i++) { + const role1 = firstEntity.secure.roles[i]; + const role2 = secondEntity.secure.roles[i]; + result = result && role1.role === role2.role; + if (result && role1.actionList && role2.actionList && role1.actionList.length === role2.actionList.length) + for (let j = 0; j < role1.actionList.length; j++) { + result = result && role1.actionList[j] === role2.actionList[j]; + } + } + } + } + } else if (firstEntity.secure.customSecurity && secondEntity.secure.customSecurity) { + result = firstEntity.secure.customSecurity.length === secondEntity.secure.customSecurity.length; + if (result) { + for (let i = 0; i < firstEntity.secure.customSecurity.length; i++) { + const item1 = firstEntity.secure.customSecurity[i]; + const item2 = secondEntity.secure.customSecurity[i]; + result = result && item1.action === item2.action; + if (result && item1.privList && item2.privList && item1.privList.length === item2.privList.length) + for (let j = 0; j < item1.privList.length; j++) { + result = result && item1.privList[j] === item2.privList[j]; + } + } + } + } + } + } else { + return !firstEntity.secure && !secondEntity.secure; + } + return result; +} + function areFieldsEqual(firstFields: any[], secondFields: any[]) { return firstFields.every((field, index) => { if (Object.keys(field).length !== Object.keys(secondFields[index]).length) { diff --git a/test-integration/samples/jdl-entities/app-roles-security.jdl b/test-integration/samples/jdl-entities/app-roles-security.jdl new file mode 100644 index 000000000000..0836a3901d3e --- /dev/null +++ b/test-integration/samples/jdl-entities/app-roles-security.jdl @@ -0,0 +1,191 @@ +application { + config { + applicationType monolith, + baseName jhipsterSampleApplication, + packageName tech.jhipster.sample, + authenticationType jwt, + prodDatabaseType postgresql, + buildTool maven, + searchEngine no, + testFrameworks [gatling, cypress], + clientFramework angular, + enableTranslation true, + nativeLanguage en, + languages [ en, fr ] + } + entities * +} + +entity Bank { + bankNumber Integer +} +entity BankAccount { + name String required + bankNumber Integer + agencyNumber Long + lastOperationDuration Float + meanOperationDuration Double + balance BigDecimal required + openingDay LocalDate + lastOperationDate Instant + active Boolean + accountType BankAccountType + attachment AnyBlob + description TextBlob +} +entity TheLabel { + labelName String required minlength(3) +} +entity Operation { + date Instant required + description String + amount BigDecimal required +} + +enum BankAccountType { + CHECKING (checking_account), + SAVINGS (savings_account), + LOAN (loan_account) +} + +entity Department { + name String required, + description TextBlob, + advertisement Blob, + logo ImageBlob +} + +/** + * JobHistory comment. + */ +entity JobHistory { + startDate ZonedDateTime, + endDate ZonedDateTime, + language Language +} + +enum Language { + FRENCH, ENGLISH, SPANISH +} + +enum JobType { + BOSS, SLAVE +} + +entity Job { + title String minlength(5) maxlength(25), + type JobType, + minSalary Long, + maxSalary Long +} + +/** + * The Employee entity. + * Second line in javadoc. + */ +entity Employee { + /** + * The firstname attribute. + */ + firstName String, + lastName String, + email String, + phoneNumber String, + hireDate ZonedDateTime, + salary Long, + commissionPct Long +} + +entity Location { + streetAddress String, + postalCode String, + city String, + stateProvince String +} + +entity Task { + title String, + description String +} + +entity GoldenBadge { + name String +} + +entity SilverBadge { + name String +} + +entity Identifier { + name String required unique +} + +entity Country { + name String +} + +entity Region { + name String +} + +relationship OneToOne { + Department{location} to Location, + Employee{user(login)} to @Id User with builtInEntity +} + +relationship OneToMany { + BankAccount{operation} to Operation{bankAccount(name)} +} +relationship ManyToOne { + BankAccount{user(login)} to User with builtInEntity +} +relationship ManyToMany { + Operation{theLabel(labelName)} to TheLabel{operation} +} + +relationship OneToMany { + /** + * A relationship + */ + Department{employee} to + /** + * Another side of the same relationship, + */ + Employee{department}, + Employee{job} to Job{emp(lastName)}, + Location{country} to Country, + Country{area(name)} to Region +} + +relationship ManyToOne { + Employee{manager(lastName)} to Employee, + Employee{sibag(name) required} to SilverBadge, + Employee{gobag(name) required} to GoldenBadge, + SilverBadge{iden(name) required} to Identifier, + GoldenBadge{iden(name) required} to Identifier +} + +relationship ManyToMany { + JobHistory{department} to Department{history}, + JobHistory{job} to Job{history}, + JobHistory{emp(firstName)} to Employee{history}, + Job{chore(title)} to Task{linkedJob(title)}, + Bank{account} to BankAccount{bank} +} + +dto BankAccount, Employee, Department, Location, Country, Region, SilverBadge, GoldenBadge, Identifier with mapstruct + +angularSuffix BankAccount with mySuffix +filter BankAccount, Employee +clientRootFolder BankAccount, TheLabel, Operation with test-root + +paginate TheLabel, Job with pagination +paginate Operation, JobHistory, Employee with infinite-scroll + +service TheLabel, Employee, Department, Region with serviceClass +service BankAccount, Location, Country with serviceImpl +secure all with roles { + ROLE_ADMIN allows (get, put, post, delete) + ROLE_USER allows (get, put, post, delete) +} +