@mshima/generator-jhipster-nodejs 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +204 -0
- package/README.md +243 -2
- package/build-info.json +3 -0
- package/cli/cli-customizations.cjs +9 -0
- package/cli/cli.cjs +40 -0
- package/cli/logo.js +31 -0
- package/cli/version.cjs +12 -0
- package/generators/ci-cd/USAGE +5 -0
- package/generators/ci-cd/command.js +5 -0
- package/generators/ci-cd/generator.js +158 -0
- package/generators/ci-cd/index.js +2 -0
- package/generators/ci-cd/templates/.gitlab-ci.yml.ejs +137 -0
- package/generators/ci-cd/templates/azure-pipelines.yml.ejs +68 -0
- package/generators/ci-cd/templates/circle.yml.ejs +59 -0
- package/generators/ci-cd/templates/docker-registry.yml.ejs +58 -0
- package/generators/ci-cd/templates/github-ci.yml.ejs +90 -0
- package/generators/ci-cd/templates/jenkins/Jenkinsfile.ejs +104 -0
- package/generators/ci-cd/templates/jenkins/idea.gdsl +90 -0
- package/generators/ci-cd/templates/jenkins/jenkins.yml.ejs +33 -0
- package/generators/ci-cd/templates/travis.yml.ejs +75 -0
- package/generators/client/command.js +5 -0
- package/generators/client/generator.js +21 -0
- package/generators/client/generators/bootstrap/command.js +5 -0
- package/generators/client/generators/bootstrap/generator.js +28 -0
- package/generators/client/generators/bootstrap/index.js +2 -0
- package/generators/client/index.js +2 -0
- package/generators/common/generators/bootstrap/command.js +5 -0
- package/generators/common/generators/bootstrap/generator.js +37 -0
- package/generators/common/generators/bootstrap/index.js +2 -0
- package/generators/docker/command.js +5 -0
- package/generators/docker/generator.js +16 -0
- package/generators/docker/generators/bootstrap/command.js +5 -0
- package/generators/docker/generators/bootstrap/generator.js +56 -0
- package/generators/docker/generators/bootstrap/index.js +2 -0
- package/generators/docker/index.js +2 -0
- package/generators/docker/templates/docker/app.yml.ejs +25 -0
- package/generators/generator-nodejs-constants.js +32 -0
- package/generators/languages/command.js +5 -0
- package/generators/languages/generator.js +22 -0
- package/generators/languages/index.js +2 -0
- package/generators/languages/templates/src/main/webapp/i18n/en/home.json +19 -0
- package/generators/languages/templates/src/main/webapp/i18n/es/home.json +19 -0
- package/generators/languages/templates/src/main/webapp/i18n/it/home.json +19 -0
- package/generators/node-server/command.js +96 -0
- package/generators/node-server/entity-files.js +26 -0
- package/generators/node-server/files.js +254 -0
- package/generators/node-server/generator.js +705 -0
- package/generators/node-server/generators/bootstrap/command.js +5 -0
- package/generators/node-server/generators/bootstrap/generator.js +84 -0
- package/generators/node-server/generators/bootstrap/index.js +2 -0
- package/generators/node-server/generators/migrations/command.js +5 -0
- package/generators/node-server/generators/migrations/files.js +42 -0
- package/generators/node-server/generators/migrations/generator.js +108 -0
- package/generators/node-server/generators/migrations/index.js +2 -0
- package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityConstraintsMigration_.ts.ejs +40 -0
- package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityMigration_.ts.ejs +26 -0
- package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityRemovedMigration_.ts.ejs +15 -0
- package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityUpdatedConstraintsMigration_.ts.ejs +60 -0
- package/generators/node-server/generators/migrations/templates/server/src/migrations/_entityUpdatedMigration_.ts.ejs +28 -0
- package/generators/node-server/index.js +2 -0
- package/generators/node-server/resources/package.json +76 -0
- package/generators/node-server/support/api-fake-data.js +54 -0
- package/generators/node-server/support/columns.js +73 -0
- package/generators/node-server/support/database.js +24 -0
- package/generators/node-server/support/entity-authorities.js +32 -0
- package/generators/node-server/support/entity-paths.js +13 -0
- package/generators/node-server/support/enums.js +11 -0
- package/generators/node-server/support/index.js +9 -0
- package/generators/node-server/support/mail-templates.js +82 -0
- package/generators/node-server/support/migrations.js +376 -0
- package/generators/node-server/support/validators.js +25 -0
- package/generators/node-server/templates/.dockerignore.ejs +16 -0
- package/generators/node-server/templates/.gitignore.jhi.nodejs.ejs +24 -0
- package/generators/node-server/templates/Dockerfile.ejs +54 -0
- package/generators/node-server/templates/README.md.jhi.nodejs.ejs +220 -0
- package/generators/node-server/templates/package.json.ejs +82 -0
- package/generators/node-server/templates/server/README.md.ejs +277 -0
- package/generators/node-server/templates/server/e2e/_entityFileName_.database.e2e-spec.ts.ejs +192 -0
- package/generators/node-server/templates/server/e2e/_entityFileName_.e2e-spec.ts.ejs +402 -0
- package/generators/node-server/templates/server/e2e/account.e2e-spec.ts.ejs +349 -0
- package/generators/node-server/templates/server/e2e/app.e2e-spec.ts.ejs +188 -0
- package/generators/node-server/templates/server/e2e/authority.e2e-spec.ts.ejs +50 -0
- package/generators/node-server/templates/server/e2e/cors.e2e-spec.ts.ejs +110 -0
- package/generators/node-server/templates/server/e2e/management.e2e-spec.ts.ejs +180 -0
- package/generators/node-server/templates/server/e2e/oauth2-bearer.e2e-spec.ts.ejs +71 -0
- package/generators/node-server/templates/server/e2e/session.e2e-spec.ts.ejs +234 -0
- package/generators/node-server/templates/server/e2e/setup.test.js.ejs +1 -0
- package/generators/node-server/templates/server/e2e/user.e2e-spec.ts.ejs +318 -0
- package/generators/node-server/templates/server/env.ejs +1 -0
- package/generators/node-server/templates/server/eslint.config.mjs.ejs +48 -0
- package/generators/node-server/templates/server/nest-cli.json.ejs +11 -0
- package/generators/node-server/templates/server/ormconfig.ts.ejs +35 -0
- package/generators/node-server/templates/server/package.json.ejs +143 -0
- package/generators/node-server/templates/server/sonar-project.properties.ejs +10 -0
- package/generators/node-server/templates/server/src/app.config.spec.ts.ejs +13 -0
- package/generators/node-server/templates/server/src/app.config.ts.ejs +179 -0
- package/generators/node-server/templates/server/src/app.module.ts.ejs +80 -0
- package/generators/node-server/templates/server/src/application-config.ts.ejs +153 -0
- package/generators/node-server/templates/server/src/auth/account.controller.ts.ejs +183 -0
- package/generators/node-server/templates/server/src/auth/auth.module.ts.ejs +104 -0
- package/generators/node-server/templates/server/src/auth/auth.service.ts.ejs +294 -0
- package/generators/node-server/templates/server/src/auth/csrf.middleware.spec.ts.ejs +76 -0
- package/generators/node-server/templates/server/src/auth/csrf.middleware.ts.ejs +85 -0
- package/generators/node-server/templates/server/src/auth/decorators/auth-user.decorator.ts.ejs +8 -0
- package/generators/node-server/templates/server/src/auth/decorators/roles.decorator.ts.ejs +4 -0
- package/generators/node-server/templates/server/src/auth/dto/key-and-password.dto.ts.ejs +17 -0
- package/generators/node-server/templates/server/src/auth/dto/password-change.dto.ts.ejs +17 -0
- package/generators/node-server/templates/server/src/auth/dto/user-login.dto.ts.ejs +21 -0
- package/generators/node-server/templates/server/src/auth/guards/auth.guard.ts.ejs +50 -0
- package/generators/node-server/templates/server/src/auth/guards/oauth2-login.guard.ts.ejs +6 -0
- package/generators/node-server/templates/server/src/auth/guards/roles.guard.ts.ejs +25 -0
- package/generators/node-server/templates/server/src/auth/index.ts.ejs +8 -0
- package/generators/node-server/templates/server/src/auth/jwt.config.spec.ts.ejs +22 -0
- package/generators/node-server/templates/server/src/auth/jwt.config.ts.ejs +29 -0
- package/generators/node-server/templates/server/src/auth/oauth2.authorities.spec.ts.ejs +24 -0
- package/generators/node-server/templates/server/src/auth/oauth2.authorities.ts.ejs +33 -0
- package/generators/node-server/templates/server/src/auth/oauth2.config.ts.ejs +51 -0
- package/generators/node-server/templates/server/src/auth/passport.jwt.strategy.ts.ejs +45 -0
- package/generators/node-server/templates/server/src/auth/passport.oauth2-bearer.strategy.ts.ejs +32 -0
- package/generators/node-server/templates/server/src/auth/passport.oauth2.strategy.spec.ts.ejs +27 -0
- package/generators/node-server/templates/server/src/auth/passport.oauth2.strategy.ts.ejs +44 -0
- package/generators/node-server/templates/server/src/auth/password-util.ts.ejs +10 -0
- package/generators/node-server/templates/server/src/auth/password.service.ts.ejs +34 -0
- package/generators/node-server/templates/server/src/auth/payload.interface.ts.ejs +14 -0
- package/generators/node-server/templates/server/src/auth/role-type.ts.ejs +7 -0
- package/generators/node-server/templates/server/src/auth/session-user.middleware.ts.ejs +33 -0
- package/generators/node-server/templates/server/src/auth/session.entity.ts.ejs +36 -0
- package/generators/node-server/templates/server/src/auth/session.store.spec.ts.ejs +99 -0
- package/generators/node-server/templates/server/src/auth/session.store.ts.ejs +116 -0
- package/generators/node-server/templates/server/src/auth/user.jwt.controller.ts.ejs +26 -0
- package/generators/node-server/templates/server/src/auth/user.oauth2.controller.ts.ejs +78 -0
- package/generators/node-server/templates/server/src/auth/user.session.controller.ts.ejs +103 -0
- package/generators/node-server/templates/server/src/common/common.module.ts.ejs +19 -0
- package/generators/node-server/templates/server/src/common/database/base.entity.ts.ejs +13 -0
- package/generators/node-server/templates/server/src/common/database/column-transformers.spec.ts.ejs +46 -0
- package/generators/node-server/templates/server/src/common/database/column-transformers.ts.ejs +69 -0
- package/generators/node-server/templates/server/src/common/database/column-types.spec.ts.ejs +21 -0
- package/generators/node-server/templates/server/src/common/database/column-types.ts.ejs +29 -0
- package/generators/node-server/templates/server/src/common/database/pagination.entity.spec.ts.ejs +66 -0
- package/generators/node-server/templates/server/src/common/database/pagination.entity.ts.ejs +108 -0
- package/generators/node-server/templates/server/src/common/dto/base.dto.ts.ejs +19 -0
- package/generators/node-server/templates/server/src/common/errors/error-constants.ts.ejs +10 -0
- package/generators/node-server/templates/server/src/common/errors/problem-details.filter.ts.ejs +63 -0
- package/generators/node-server/templates/server/src/common/errors/problem.exception.ts.ejs +92 -0
- package/generators/node-server/templates/server/src/common/field_validators.ejs +77 -0
- package/generators/node-server/templates/server/src/common/header-util.ts.ejs +105 -0
- package/generators/node-server/templates/server/src/common/interceptors/logging.interceptor.spec.ts.ejs +93 -0
- package/generators/node-server/templates/server/src/common/interceptors/logging.interceptor.ts.ejs +48 -0
- package/generators/node-server/templates/server/src/common/logging.spec.ts.ejs +83 -0
- package/generators/node-server/templates/server/src/common/logging.ts.ejs +129 -0
- package/generators/node-server/templates/server/src/common/metrics.spec.ts.ejs +43 -0
- package/generators/node-server/templates/server/src/common/metrics.ts.ejs +198 -0
- package/generators/node-server/templates/server/src/common/request.ts.ejs +8 -0
- package/generators/node-server/templates/server/src/common/sortable-properties.ts.ejs +43 -0
- package/generators/node-server/templates/server/src/common/updatable-properties.ts.ejs +14 -0
- package/generators/node-server/templates/server/src/config/application-dev.yml.ejs +118 -0
- package/generators/node-server/templates/server/src/config/application-prod.yml.ejs +97 -0
- package/generators/node-server/templates/server/src/config/application-secret-samples.yml.ejs +42 -0
- package/generators/node-server/templates/server/src/config/application-test.yml.ejs +68 -0
- package/generators/node-server/templates/server/src/config/application-tls-samples.yml.ejs +15 -0
- package/generators/node-server/templates/server/src/config/application-tls.yml.ejs +15 -0
- package/generators/node-server/templates/server/src/config/application.yml.ejs +149 -0
- package/generators/node-server/templates/server/src/config.spec.ts.ejs +81 -0
- package/generators/node-server/templates/server/src/config.ts.ejs +255 -0
- package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.controller.ts.ejs +178 -0
- package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.entity.ts.ejs +110 -0
- package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.mapper.ts.ejs +37 -0
- package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.module.ts.ejs +14 -0
- package/generators/node-server/templates/server/src/entities/_entityFileName_/_entityFileName_.service.ts.ejs +123 -0
- package/generators/node-server/templates/server/src/entities/_entityFileName_/dto/_entityFileName_.dto.ts.ejs +99 -0
- package/generators/node-server/templates/server/src/mail/mail.module.ts.ejs +49 -0
- package/generators/node-server/templates/server/src/mail/mail.service.spec.ts.ejs +105 -0
- package/generators/node-server/templates/server/src/mail/mail.service.ts.ejs +120 -0
- package/generators/node-server/templates/server/src/main.ts.ejs +114 -0
- package/generators/node-server/templates/server/src/management/api-docs.controller.ts.ejs +27 -0
- package/generators/node-server/templates/server/src/management/api-docs.service.ts.ejs +8 -0
- package/generators/node-server/templates/server/src/management/configuration.controller.ts.ejs +80 -0
- package/generators/node-server/templates/server/src/management/health.controller.spec.ts.ejs +79 -0
- package/generators/node-server/templates/server/src/management/health.controller.ts.ejs +87 -0
- package/generators/node-server/templates/server/src/management/health.module.ts.ejs +9 -0
- package/generators/node-server/templates/server/src/management/loggers.controller.ts.ejs +40 -0
- package/generators/node-server/templates/server/src/management/management.controller.spec.ts.ejs +26 -0
- package/generators/node-server/templates/server/src/management/management.controller.ts.ejs +34 -0
- package/generators/node-server/templates/server/src/management/management.module.ts.ejs +21 -0
- package/generators/node-server/templates/server/src/management/metrics.controller.ts.ejs +85 -0
- package/generators/node-server/templates/server/src/migrations/1570200270081-CreateTables.ts.ejs +64 -0
- package/generators/node-server/templates/server/src/migrations/1570200270082-CreateSessions.ts.ejs +26 -0
- package/generators/node-server/templates/server/src/migrations/1570200490072-SeedUsersRoles.ts.ejs +74 -0
- package/generators/node-server/templates/server/src/migrations/support/schema.ts.ejs +254 -0
- package/generators/node-server/templates/server/src/orm.config.ts.ejs +147 -0
- package/generators/node-server/templates/server/src/registry/consul.service.ts.ejs +70 -0
- package/generators/node-server/templates/server/src/registry/eureka.service.ts.ejs +129 -0
- package/generators/node-server/templates/server/src/swagger.ts.ejs +24 -0
- package/generators/node-server/templates/server/src/tls.ts.ejs +27 -0
- package/generators/node-server/templates/server/src/users/authority.controller.ts.ejs +85 -0
- package/generators/node-server/templates/server/src/users/authority.entity.ts.ejs +22 -0
- package/generators/node-server/templates/server/src/users/dto/authority.dto.ts.ejs +10 -0
- package/generators/node-server/templates/server/src/users/dto/user.dto.ts.ejs +116 -0
- package/generators/node-server/templates/server/src/users/public.user.controller.ts.ejs +51 -0
- package/generators/node-server/templates/server/src/users/user.controller.ts.ejs +191 -0
- package/generators/node-server/templates/server/src/users/user.entity.ts.ejs +63 -0
- package/generators/node-server/templates/server/src/users/user.mapper.ts.ejs +26 -0
- package/generators/node-server/templates/server/src/users/user.module.ts.ejs +19 -0
- package/generators/node-server/templates/server/src/users/user.service.ts.ejs +156 -0
- package/generators/node-server/templates/server/tsconfig.build.json.ejs +9 -0
- package/generators/node-server/templates/server/tsconfig.json.ejs +23 -0
- package/generators/node-server/templates/server/typeorm-migration.ts.ejs +18 -0
- package/generators/node-server/templates/server/vitest.config.e2e.ts.ejs +16 -0
- package/generators/node-server/templates/server/vitest.config.ts.ejs +16 -0
- package/generators/server/command.js +7 -0
- package/generators/server/generator.js +20 -0
- package/generators/server/generators/bootstrap/command.js +5 -0
- package/generators/server/generators/bootstrap/generator.js +15 -0
- package/generators/server/generators/bootstrap/index.js +2 -0
- package/generators/server/index.js +2 -0
- package/package.json +75 -4
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default port of each production database type of JHipster, the port of its docker service: the database of the
|
|
3
|
+
* application-dev.yml and application-prod.yml files.
|
|
4
|
+
*/
|
|
5
|
+
export const databasePorts = {
|
|
6
|
+
postgresql: 5432,
|
|
7
|
+
mysql: 3306,
|
|
8
|
+
mariadb: 3306,
|
|
9
|
+
mssql: 1433,
|
|
10
|
+
oracle: 1521,
|
|
11
|
+
mongodb: 27017,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** The default port of the production database of an application. */
|
|
15
|
+
export const getDatabasePort = ({ databaseType, prodDatabaseType = databaseType }) => databasePorts[prodDatabaseType];
|
|
16
|
+
|
|
17
|
+
/** The embedded development databases of the NodeJS server: SQLite, on disk or in memory. */
|
|
18
|
+
const embeddedDevDatabaseTypes = ['sqlite', 'sqliteMemory'];
|
|
19
|
+
|
|
20
|
+
/** The TypeORM type of the development database: better-sqlite3 for an embedded one, else the server type. */
|
|
21
|
+
export const getNodeDevDatabaseType = ({ databaseType, devDatabaseType = databaseType }) => {
|
|
22
|
+
if (embeddedDevDatabaseTypes.includes(devDatabaseType)) return 'better-sqlite3';
|
|
23
|
+
return devDatabaseType === 'postgresql' ? 'postgres' : devDatabaseType;
|
|
24
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The roles of the entities, like the @PreAuthorize of the resources of JHipster Spring Boot (entitySpringPreAuthorize and
|
|
3
|
+
* entitySpringReadPreAuthorize of its spring-boot generator): the roles of entityAuthority (@EntityAuthority of the JDL,
|
|
4
|
+
* ROLE_ADMIN for an admin entity, like the built-in User and Authority) write and read, the roles of entityReadAuthority
|
|
5
|
+
* (@EntityReadAuthority) only read. Without them, any authenticated user.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** The roles of a comma separated authority of JHipster. */
|
|
9
|
+
const rolesOf = authority =>
|
|
10
|
+
String(authority ?? '')
|
|
11
|
+
.split(',')
|
|
12
|
+
.map(role => role.trim())
|
|
13
|
+
.filter(Boolean);
|
|
14
|
+
|
|
15
|
+
/** The TypeScript of the arguments of a @Roles decorator, undefined without roles. */
|
|
16
|
+
const rolesLiteral = roles => (roles.length > 0 ? roles.map(role => `'${role}'`).join(', ') : undefined);
|
|
17
|
+
|
|
18
|
+
/** The mutations of an entity (entityDefaults), delayed until the entity is prepared. */
|
|
19
|
+
export const mutateEntityAuthorities = {
|
|
20
|
+
__override__: false,
|
|
21
|
+
// The roles of the creations, the updates and the deletions.
|
|
22
|
+
nodejsWriteRoles: ({ entityAuthority }, { delayMarker }) => delayMarker ?? rolesLiteral(rolesOf(entityAuthority)),
|
|
23
|
+
// The roles of the reads: the ones of the writes and the ones of entityReadAuthority.
|
|
24
|
+
nodejsReadRoles: ({ entityAuthority, entityReadAuthority }, { delayMarker }) =>
|
|
25
|
+
delayMarker ?? rolesLiteral([...new Set([...rolesOf(entityAuthority), ...rolesOf(entityReadAuthority)])]),
|
|
26
|
+
// The roles that only read, which the tests check refused on the writes.
|
|
27
|
+
nodejsReadOnlyRoles: ({ entityAuthority, entityReadAuthority }, { delayMarker }) => {
|
|
28
|
+
if (delayMarker) return delayMarker;
|
|
29
|
+
const writeRoles = rolesOf(entityAuthority);
|
|
30
|
+
return rolesLiteral(rolesOf(entityReadAuthority).filter(role => !writeRoles.includes(role)));
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files of an entity, from the src folder of the server, in its feature module: an entity of the JDL in its own folder
|
|
3
|
+
* of src/entities, the built-in User and Authority in src/users, with the users module.
|
|
4
|
+
*/
|
|
5
|
+
const entityFolder = ({ builtIn, entityFileName }) => (builtIn ? 'users' : `entities/${entityFileName}`);
|
|
6
|
+
|
|
7
|
+
/** The mutations of an entity (entityDefaults): the paths its imports, its needles and its cleanups use. */
|
|
8
|
+
export const mutateEntityPaths = {
|
|
9
|
+
__override__: false,
|
|
10
|
+
nodejsEntityPath: entity => `${entityFolder(entity)}/${entity.entityFileName}.entity.ts`,
|
|
11
|
+
nodejsDtoPath: entity => `${entityFolder(entity)}/dto/${entity.entityFileName}.dto.ts`,
|
|
12
|
+
nodejsModulePath: entity => `${entityFolder(entity)}/${entity.entityFileName}.module.ts`,
|
|
13
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getEnumInfo } from 'generator-jhipster/generators/base-application/support';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The enum of a field as the server exchanges and stores it, like JHipster Spring Boot: a Java enum is written in JSON
|
|
5
|
+
* and stored (`@Enumerated(EnumType.STRING)`) by its name, its custom value (`LOW (low_level)`) being only a label of the
|
|
6
|
+
* client. Each value of the TypeScript enum is its name.
|
|
7
|
+
*/
|
|
8
|
+
export const serverEnumInfo = (field, clientRootFolder) => {
|
|
9
|
+
const enumInfo = getEnumInfo(field, clientRootFolder);
|
|
10
|
+
return { ...enumInfo, enumValues: enumInfo.enumValues.map(enumValue => ({ ...enumValue, value: enumValue.name })) };
|
|
11
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './database.js';
|
|
2
|
+
export * from './mail-templates.js';
|
|
3
|
+
export * from './columns.js';
|
|
4
|
+
export * from './api-fake-data.js';
|
|
5
|
+
export * from './migrations.js';
|
|
6
|
+
export * from './entity-paths.js';
|
|
7
|
+
export * from './entity-authorities.js';
|
|
8
|
+
export * from './enums.js';
|
|
9
|
+
export * from './validators.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Thymeleaf expression of the mail templates of JHipster Spring Boot as Handlebars: a message (#{key(${a.b})}) as the
|
|
3
|
+
* message helper of the server, a variable (${a.b}), a link with literal substitutions (@{|${baseUrl}/path|}), the
|
|
4
|
+
* language of the mail (${#locale.language}). The variables of a th:with give their converted value.
|
|
5
|
+
*/
|
|
6
|
+
const toHandlebarsValue = (expression, variables) => {
|
|
7
|
+
const value = expression.trim();
|
|
8
|
+
if (value === '${#locale.language}') return '{{langKey}}';
|
|
9
|
+
let match = /^\$\{([\w.]+)\}$/.exec(value);
|
|
10
|
+
if (match) {
|
|
11
|
+
return variables[match[1]] ?? `{{${match[1]}}}`;
|
|
12
|
+
}
|
|
13
|
+
match = /^#\{([\w.]+)(?:\((.*)\))?\}$/.exec(value);
|
|
14
|
+
if (match) {
|
|
15
|
+
const parameters = (match[2] ?? '')
|
|
16
|
+
.split(',')
|
|
17
|
+
.map(parameter => parameter.trim())
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.map(parameter => {
|
|
20
|
+
const variable = /^\$\{([\w.]+)\}$/.exec(parameter);
|
|
21
|
+
if (!variable) throw new Error(`Unsupported message parameter ${parameter}`);
|
|
22
|
+
return ` ${variable[1]}`;
|
|
23
|
+
});
|
|
24
|
+
return `{{message '${match[1]}'${parameters.join('')}}}`;
|
|
25
|
+
}
|
|
26
|
+
match = /^\(?@\{\|(.*)\|\}\)?$/.exec(value);
|
|
27
|
+
if (match) {
|
|
28
|
+
return match[1].replace(/\$\{([\w.]+)\}/g, (_, variable) => variables[variable] ?? `{{${variable}}}`);
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`Unsupported Thymeleaf expression ${value}`);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** The attributes of a tag with their th: attributes converted, and the converted th:text if any. */
|
|
34
|
+
const convertAttributes = attributesText => {
|
|
35
|
+
const attributes = [...attributesText.matchAll(/([\w:-]+)="([^"]*)"/g)].map(([, name, value]) => ({ name, value }));
|
|
36
|
+
const variables = {};
|
|
37
|
+
for (const { value } of attributes.filter(attribute => attribute.name === 'th:with')) {
|
|
38
|
+
for (const definition of value.split(/,(?![^(]*\))/)) {
|
|
39
|
+
const [, variable, expression] = /^\s*(\w+)\s*=\s*(.*)$/.exec(definition) ?? [];
|
|
40
|
+
if (!variable) throw new Error(`Unsupported th:with ${value}`);
|
|
41
|
+
variables[variable] = toHandlebarsValue(expression, variables);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let text;
|
|
45
|
+
const kept = [];
|
|
46
|
+
for (const { name, value } of attributes) {
|
|
47
|
+
if (name === 'th:with' || name.startsWith('xmlns:th')) continue;
|
|
48
|
+
if (name === 'th:text') {
|
|
49
|
+
text = toHandlebarsValue(value, variables);
|
|
50
|
+
} else if (name.startsWith('th:')) {
|
|
51
|
+
kept.push({ name: name.substring(3), value: toHandlebarsValue(value, variables) });
|
|
52
|
+
} else if (!attributes.some(attribute => attribute.name === `th:${name}`)) {
|
|
53
|
+
kept.push({ name, value });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { attributes: kept.map(({ name, value }) => ` ${name}="${value}"`).join(''), text };
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Converts a mail template of JHipster Spring Boot (Thymeleaf) to a Handlebars template of the mail module, for the
|
|
61
|
+
* subset its account mails use: th:text on the elements without children, the other th: attributes (th:href, th:lang)
|
|
62
|
+
* and the th:with variables. The output only interpolates values, escaped, and calls the message helper: it runs no
|
|
63
|
+
* code. An expression or an attribute out of that subset fails the generation instead of giving a broken template.
|
|
64
|
+
*/
|
|
65
|
+
export const thymeleafMailToHandlebars = content => {
|
|
66
|
+
const result = content
|
|
67
|
+
// The leaf elements with a th:text, their content replaced by its value.
|
|
68
|
+
.replace(/<(\w+)((?:\s+[\w:-]+="[^"]*")*)\s*>[^<]*<\/\1>/g, (element, tag, attributesText) => {
|
|
69
|
+
if (!attributesText.includes('th:text=')) return element;
|
|
70
|
+
const { attributes, text } = convertAttributes(attributesText);
|
|
71
|
+
return `<${tag}${attributes}>${text}</${tag}>`;
|
|
72
|
+
})
|
|
73
|
+
// The other tags with th: attributes.
|
|
74
|
+
.replace(/<(\w+)((?:\s+[\w:-]+="[^"]*")*)\s*(\/?)>/g, (tag, name, attributesText, selfClosing) => {
|
|
75
|
+
if (!attributesText.includes('th:')) return tag;
|
|
76
|
+
const { attributes, text } = convertAttributes(attributesText);
|
|
77
|
+
if (text !== undefined) throw new Error(`Unsupported th:text on the element ${name} with children`);
|
|
78
|
+
return `<${name}${attributes}${selfClosing ? ' /' : ''}>`;
|
|
79
|
+
});
|
|
80
|
+
if (/\sth:|xmlns:th/.test(result)) throw new Error('Unsupported Thymeleaf attribute left in the mail template');
|
|
81
|
+
return result;
|
|
82
|
+
};
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { bigDecimalPrecision } from './columns.js';
|
|
2
|
+
import { serverEnumInfo } from './enums.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The migrations of the SQL databases, like the Liquibase changelogs of JHipster Spring Boot: the tables of the built-in
|
|
6
|
+
* users (CreateTables), then for each entity the migration adding its table and the one adding its constraints (its
|
|
7
|
+
* foreign keys and its join tables). The tables and the columns are the ones TypeORM maps the entities to, with their
|
|
8
|
+
* names, so a database created by a former version of the application (its schema synchronized from the entities) keeps
|
|
9
|
+
* its schema.
|
|
10
|
+
*
|
|
11
|
+
* TypeORM runs the migrations in the order of the timestamp of the last 13 digits of their class name: the sections of
|
|
12
|
+
* the master.xml of Liquibase are ranges of timestamps. The changelogs of the tables are the migrations of the tables at
|
|
13
|
+
* the timestamp of the changelogDate of their entity, the changelogs of the constraints the ones of the constraints at
|
|
14
|
+
* that timestamp plus CONSTRAINTS_TIMESTAMP_OFFSET, and the changelogs of the developers, at the end of master.xml, the
|
|
15
|
+
* migrations the typeorm:migration scripts create at their time plus MIGRATION_TIMESTAMP_OFFSET.
|
|
16
|
+
*
|
|
17
|
+
* Like the incremental changelogs of Liquibase, the migrations of an entity are the ones of its changelogs JHipster records
|
|
18
|
+
* (jhipster:base-application:database-changelogs, the changelog objects of the .yo-rc.json): its creation, AddedEntity
|
|
19
|
+
* and AddedEntityConstraints, then each update at the date of its changelog plus MIGRATION_TIMESTAMP_OFFSET (after the
|
|
20
|
+
* constraints, like the incremental changelogs at the end of master.xml): UpdatedEntity adds the columns of the added
|
|
21
|
+
* fields and drops the ones of the removed fields, then UpdatedEntityConstraints, 1 ms after, adds and drops the join
|
|
22
|
+
* columns, their foreign keys and the join tables of the relationships. A migration is written once, by the generation
|
|
23
|
+
* recording its changelog.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The offset of the timestamps of the constraints migrations, after the ones of every table: the timestamp of the
|
|
28
|
+
* constraints of an entity is the one of its table plus 3000000000000 (4702121677000 for 1702121677000), so its name, like
|
|
29
|
+
* the constraints changelog of Liquibase named by the changelogDate of the entity, never changes, whatever the entities it
|
|
30
|
+
* references. The tables of the entities created up to 95 years after the first one stay before every constraints migration.
|
|
31
|
+
*/
|
|
32
|
+
export const CONSTRAINTS_TIMESTAMP_OFFSET = 3 * 10 ** 12;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The offset of the timestamps of the migrations of the developers, created by the typeorm:migration scripts: their time
|
|
36
|
+
* plus 6000000000000, after the constraints migrations of every entity created before, like the changelogs added at the
|
|
37
|
+
* end of the master.xml of Liquibase; within 13 digits until 2096.
|
|
38
|
+
*/
|
|
39
|
+
export const MIGRATION_TIMESTAMP_OFFSET = 6 * 10 ** 12;
|
|
40
|
+
|
|
41
|
+
/** The JavaScript timestamp (milliseconds, UTC) of the changelogDate of an entity, the date JHipster parses it to. */
|
|
42
|
+
const entityTimestamp = entity => entity.changelogDateForRecent.getTime();
|
|
43
|
+
|
|
44
|
+
/** The JavaScript timestamp (milliseconds, UTC) of a changelogDate of JHipster (yyyyMMddHHmmss). */
|
|
45
|
+
export const changelogDateToTimestamp = changelogDate => {
|
|
46
|
+
const [, year, month, day, hours, minutes, seconds] = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/.exec(changelogDate) ?? [];
|
|
47
|
+
if (!year) {
|
|
48
|
+
throw new Error(`The changelogDate ${changelogDate} is not a date of the yyyyMMddHHmmss format`);
|
|
49
|
+
}
|
|
50
|
+
return Date.UTC(Number(year), Number(month) - 1, Number(day), Number(hours), Number(minutes), Number(seconds));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** The class and the file of a migration of an entity, by the kind of the migration and its timestamp. */
|
|
54
|
+
const migrationName = (kind, entityClass, timestamp) => ({
|
|
55
|
+
timestamp,
|
|
56
|
+
className: `${kind}${entityClass}${timestamp}`,
|
|
57
|
+
fileName: `${timestamp}-${kind}${entityClass}`,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/** The TypeScript of the type of a column whose @Column decorator has none: the TypeScript type of its property. */
|
|
61
|
+
const typeOfFieldType = {
|
|
62
|
+
string: 'String',
|
|
63
|
+
number: 'Number',
|
|
64
|
+
boolean: 'Boolean',
|
|
65
|
+
Date: 'Date',
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** A string literal of TypeScript. */
|
|
69
|
+
const literal = value => `'${String(value).replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`;
|
|
70
|
+
|
|
71
|
+
/** The migration column of the primary key of an entity, like its @PrimaryGeneratedColumn. */
|
|
72
|
+
export const primaryKeyColumn = entity => ({
|
|
73
|
+
name: entity.primaryKey.name,
|
|
74
|
+
type: entity.primaryKey.typeUUID ? literal('uuid') : 'Number',
|
|
75
|
+
primary: true,
|
|
76
|
+
generated: entity.primaryKey.typeUUID ? 'uuid' : 'increment',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/** The migration column referencing the primary key of an entity: a join column, or a column of a join table. */
|
|
80
|
+
const referenceColumn = (name, entity) => ({
|
|
81
|
+
name,
|
|
82
|
+
type: entity.primaryKey.typeUUID ? literal('uuid') : 'Number',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/** The migration columns of a field, like the @Column decorators of the entity: its column, and the content type of a blob. */
|
|
86
|
+
export const fieldColumns = field => {
|
|
87
|
+
const { fieldValidationRequired: required, fieldValidationUnique: unique, fieldValidationMaxlength: maxlength } = field;
|
|
88
|
+
const nullable = !required;
|
|
89
|
+
const type = field.nodejsColumnType ? literal(field.nodejsColumnType) : (typeOfFieldType[field.nodejsFieldType] ?? 'String');
|
|
90
|
+
const column = { name: field.fieldNameAsDatabaseColumn, type, nullable, unique };
|
|
91
|
+
if (field.fieldIsEnum) {
|
|
92
|
+
// The values of the TypeScript enum of the field, the ones of the enum column of the databases having one (MySQL).
|
|
93
|
+
column.enum = `[${serverEnumInfo(field)
|
|
94
|
+
.enumValues.map(({ value }) => literal(value))
|
|
95
|
+
.join(', ')}]`;
|
|
96
|
+
} else if (field.fieldType === 'BigDecimal') {
|
|
97
|
+
Object.assign(column, bigDecimalPrecision);
|
|
98
|
+
} else if (!['Instant', 'ZonedDateTime', 'LocalDate'].includes(field.fieldType) && maxlength) {
|
|
99
|
+
column.length = field.fieldValidateRulesMaxlength;
|
|
100
|
+
}
|
|
101
|
+
const columns = [column];
|
|
102
|
+
if (hasContentTypeColumn(field)) {
|
|
103
|
+
columns.push({ name: `${field.fieldNameAsDatabaseColumn}_content_type`, type: 'String', nullable });
|
|
104
|
+
}
|
|
105
|
+
return columns;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** Whether a field is a binary blob, stored with the column of its content type. */
|
|
109
|
+
const hasContentTypeColumn = field => ['byte[]', 'ByteBuffer'].includes(field.fieldType) && field.fieldTypeBlobContent !== 'text';
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The camelCase of the default naming strategy of TypeORM, the names it gives the join columns and the columns of the join
|
|
113
|
+
* tables: unlike the one of lodash, it keeps the case of the letters it does not join (mainURL_id is mainURLId).
|
|
114
|
+
*/
|
|
115
|
+
export const typeormCamelCase = text =>
|
|
116
|
+
text.replaceAll(/^([A-Z])|[\s\-_](\w)/g, (_match, first, next) => (next ? next.toUpperCase() : first.toLowerCase()));
|
|
117
|
+
|
|
118
|
+
/** The join column of a relationship, named like TypeORM names it (the relationship and the primary key of the other entity). */
|
|
119
|
+
const joinColumnName = relationship =>
|
|
120
|
+
typeormCamelCase(`${relationship.relationshipFieldName}_${relationship.otherEntity.primaryKey.name}`);
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The relationships of an entity with a join column, the persistableRelationships of JHipster but the many-to-many ones:
|
|
124
|
+
* the many-to-one ones, and the one-to-one ones on their owner side.
|
|
125
|
+
*/
|
|
126
|
+
const joinColumnRelationships = entity => entity.persistableRelationships.filter(relationship => !relationship.relationshipManyToMany);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The names of the columns of a join table, like TypeORM names the ones of a @JoinTable: the same names, like the ones of a
|
|
130
|
+
* many-to-many relationship of an entity with itself named like its table, suffixed by _1 and _2.
|
|
131
|
+
*/
|
|
132
|
+
const junctionColumnNames = (joinColumnName, inverseJoinColumnName) =>
|
|
133
|
+
joinColumnName === inverseJoinColumnName
|
|
134
|
+
? [`${joinColumnName}_1`, `${inverseJoinColumnName}_2`]
|
|
135
|
+
: [joinColumnName, inverseJoinColumnName];
|
|
136
|
+
|
|
137
|
+
/** The relationships of an entity with a join table, the many-to-many ones on their owner side (shouldWriteJoinTable of JHipster). */
|
|
138
|
+
const joinTableRelationships = entity => entity.relationships.filter(relationship => relationship.shouldWriteJoinTable);
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The join table of a many-to-many relationship on its owner side, the one of its @JoinTable and of its constraints
|
|
142
|
+
* migration: the joinTable of JHipster, its columns named by the table of the entity and the relationship.
|
|
143
|
+
*/
|
|
144
|
+
export const joinTable = (entity, relationship) => {
|
|
145
|
+
const [joinColumnName, inverseJoinColumnName] = junctionColumnNames(`${entity.entityTableName}_id`, `${relationship.columnName}_id`);
|
|
146
|
+
return {
|
|
147
|
+
name: relationship.joinTable.name,
|
|
148
|
+
joinColumn: {
|
|
149
|
+
...referenceColumn(joinColumnName, entity),
|
|
150
|
+
referencedTableName: entity.entityTableName,
|
|
151
|
+
referencedColumnName: entity.primaryKey.name,
|
|
152
|
+
},
|
|
153
|
+
inverseJoinColumn: {
|
|
154
|
+
...referenceColumn(inverseJoinColumnName, relationship.otherEntity),
|
|
155
|
+
referencedTableName: relationship.otherEntity.entityTableName,
|
|
156
|
+
referencedColumnName: relationship.otherEntity.primaryKey.name,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** The migration adding the table of an entity, its name, its class and its columns, at the timestamp of its creation. */
|
|
162
|
+
export const entityMigration = (entity, timestamp = entityTimestamp(entity)) => {
|
|
163
|
+
const relationships = joinColumnRelationships(entity);
|
|
164
|
+
return {
|
|
165
|
+
...migrationName('AddedEntity', entity.entityClass, timestamp),
|
|
166
|
+
tableName: entity.entityTableName,
|
|
167
|
+
columns: [
|
|
168
|
+
primaryKeyColumn(entity),
|
|
169
|
+
...entity.fields.filter(field => !field.id).flatMap(fieldColumns),
|
|
170
|
+
...relationships.map(relationship => ({
|
|
171
|
+
...referenceColumn(joinColumnName(relationship), relationship.otherEntity),
|
|
172
|
+
nullable: true,
|
|
173
|
+
})),
|
|
174
|
+
],
|
|
175
|
+
relationUniques: relationships
|
|
176
|
+
.filter(relationship => relationship.relationshipOneToOne)
|
|
177
|
+
.map(relationship => joinColumnName(relationship)),
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The migration adding the constraints of an entity, its foreign keys and its join tables, when it has some: run after the
|
|
183
|
+
* migrations of every table, like the constraints changelogs of JHipster run after every table.
|
|
184
|
+
*/
|
|
185
|
+
export const constraintsMigration = (entity, timestamp = entityTimestamp(entity)) => {
|
|
186
|
+
const joinColumns = joinColumnRelationships(entity);
|
|
187
|
+
const joinTables = joinTableRelationships(entity);
|
|
188
|
+
if (joinColumns.length === 0 && joinTables.length === 0) return undefined;
|
|
189
|
+
return {
|
|
190
|
+
...migrationName('AddedEntityConstraints', entity.entityClass, timestamp + CONSTRAINTS_TIMESTAMP_OFFSET),
|
|
191
|
+
tableName: entity.entityTableName,
|
|
192
|
+
foreignKeys: joinColumns.map(foreignKey),
|
|
193
|
+
joinTables: joinTables.map(relationship => joinTable(entity, relationship)),
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/** The foreign key of the join column of a relationship. */
|
|
198
|
+
const foreignKey = relationship => ({
|
|
199
|
+
columnName: joinColumnName(relationship),
|
|
200
|
+
referencedTableName: relationship.otherEntity.entityTableName,
|
|
201
|
+
referencedColumnName: relationship.otherEntity.primaryKey.name,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The migrations of a changelog of an entity, of this generation: for its creation the table of the entity at its date
|
|
206
|
+
* (nodejsMigration), then its constraints (nodejsConstraintsMigration); for an update its fields (nodejsUpdatedMigration),
|
|
207
|
+
* then its relationships (nodejsUpdatedConstraintsMigration), after the constraints of every entity; for its removal its
|
|
208
|
+
* join tables and its table (nodejsRemovedMigration), after the updates of the entities whose relationships referenced it.
|
|
209
|
+
* @param {any} changelog the changelog
|
|
210
|
+
* @param {any} entity the entity, prepared: none for a removal
|
|
211
|
+
* @param {any[]} entities the entities of the application, prepared
|
|
212
|
+
*/
|
|
213
|
+
export const changelogMigrations = (changelog, entity, entities) => {
|
|
214
|
+
const timestamp = changelogDateToTimestamp(changelog.changelogDate);
|
|
215
|
+
if (changelog.operation === 'create') {
|
|
216
|
+
return { nodejsMigration: entityMigration(entity, timestamp), nodejsConstraintsMigration: constraintsMigration(entity, timestamp) };
|
|
217
|
+
}
|
|
218
|
+
if (changelog.operation === 'update') {
|
|
219
|
+
const changes = { entity, ...changelogChanges(changelog, entity, entities) };
|
|
220
|
+
return {
|
|
221
|
+
nodejsUpdatedMigration: updatedEntityMigration(changes, timestamp + MIGRATION_TIMESTAMP_OFFSET),
|
|
222
|
+
// The relationships after the fields, whose join columns may reference them.
|
|
223
|
+
nodejsUpdatedConstraintsMigration: updatedConstraintsMigration(changes, timestamp + MIGRATION_TIMESTAMP_OFFSET + 1),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return { nodejsRemovedMigration: removedEntityMigration(changelog, timestamp + MIGRATION_TIMESTAMP_OFFSET) };
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The migration of the removal of an entity, like the dropTable of Liquibase: the join tables it owned, then its table, from
|
|
231
|
+
* the entity before the changelog (its previousEntity). The relationships of the other entities referencing it are removed
|
|
232
|
+
* with it, their join columns and their join tables dropped by their update migrations, dated before.
|
|
233
|
+
* @param {any} changelog the removal changelog of this generation
|
|
234
|
+
* @param {number} timestamp the timestamp of the migration
|
|
235
|
+
*/
|
|
236
|
+
export const removedEntityMigration = ({ entityName, previousEntity }, timestamp) => ({
|
|
237
|
+
...migrationName('RemovedEntity', `${entityName.charAt(0).toUpperCase()}${entityName.slice(1)}`, timestamp),
|
|
238
|
+
entityName,
|
|
239
|
+
tableName: previousEntity.entityTableName,
|
|
240
|
+
joinTables: previousEntity.relationships.filter(hasJoinTable).map(relationship => relationship.joinTable.name),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
/** The class of a migration, by the name of its file (`<timestamp>-<kind><Entity>`, its class `<kind><Entity><timestamp>`). */
|
|
244
|
+
export const migrationOfFile = fileName => ({ fileName, className: fileName.replace(/^(\d+)-(\w+)$/, '$2$1') });
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The fields and the relationships an update changelog of an entity adds and removes: the added ones the entity has,
|
|
248
|
+
* prepared; the removed ones the entity before the changelog has (its previousEntity, of this generation only), by the
|
|
249
|
+
* names of their columns. A relationship changed in its type, its other entity, its side or its join column
|
|
250
|
+
* (recreatedRelationships) is removed and added again; one changed in its options only (onDelete, onUpdate), which the
|
|
251
|
+
* foreign keys of the server do not declare, is left alone. A field changed (its type, its validations) is not migrated.
|
|
252
|
+
* @param {any} changelog the update changelog of this generation
|
|
253
|
+
* @param {any} entity the entity, prepared
|
|
254
|
+
* @param {any[]} entities the entities of the application, prepared
|
|
255
|
+
*/
|
|
256
|
+
export const changelogChanges = (changelog, entity, entities) => {
|
|
257
|
+
const { previousEntity, recreatedRelationships } = changelog;
|
|
258
|
+
const named = (items, names, key) => items.filter(item => names.includes(item[key]));
|
|
259
|
+
return {
|
|
260
|
+
addedFields: named(entity.fields, changelog.addedFields, 'fieldName'),
|
|
261
|
+
removedFields: named(previousEntity.fields, changelog.removedFields, 'fieldName'),
|
|
262
|
+
addedRelationships: named(entity.relationships, [...changelog.addedRelationships, ...recreatedRelationships], 'relationshipName'),
|
|
263
|
+
removedRelationships: named(
|
|
264
|
+
previousEntity.relationships,
|
|
265
|
+
[...changelog.removedRelationships, ...recreatedRelationships],
|
|
266
|
+
'relationshipName',
|
|
267
|
+
).map(relationship => removedRelationship(relationship, entities)),
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* A relationship of the entity before a changelog, as the changelog records it (its type, its side, its other entity and its
|
|
273
|
+
* join table), with the properties of a relationship the migrations name its join column by: its field, and the primary key
|
|
274
|
+
* of its other entity (id when the other entity is no longer).
|
|
275
|
+
*/
|
|
276
|
+
const removedRelationship = (relationship, entities) => {
|
|
277
|
+
const otherEntity = entities.find(entity => entity.name.toLowerCase() === relationship.otherEntityName.toLowerCase());
|
|
278
|
+
return {
|
|
279
|
+
...relationship,
|
|
280
|
+
relationshipFieldName: `${relationship.relationshipName.charAt(0).toLowerCase()}${relationship.relationshipName.slice(1)}`,
|
|
281
|
+
otherEntity: otherEntity ?? { primaryKey: { name: 'id' } },
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/** The names of the columns of a field of the entity before a changelog, by the column name the changelog records. */
|
|
286
|
+
const removedFieldColumnNames = field => [field.columnName, ...(hasContentTypeColumn(field) ? [`${field.columnName}_content_type`] : [])];
|
|
287
|
+
|
|
288
|
+
/** The TypeScript of an array of names, none without a name. */
|
|
289
|
+
const namesLiteral = names => (names.length > 0 ? `[${names.map(literal).join(', ')}]` : undefined);
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The migration of the fields of an entity an update changelog changes, like the updated_entity changelog of Liquibase: the
|
|
293
|
+
* columns of the added fields, and the names of the columns of the removed fields. None without an added or a removed field.
|
|
294
|
+
* @param {{ entity: any, addedFields: any[], removedFields: any[] }} changes the changes of the changelog (changelogChanges)
|
|
295
|
+
* @param {number} timestamp the timestamp of the migration
|
|
296
|
+
*/
|
|
297
|
+
export const updatedEntityMigration = ({ entity, addedFields, removedFields }, timestamp) => {
|
|
298
|
+
if (addedFields.length === 0 && removedFields.length === 0) return undefined;
|
|
299
|
+
const addedColumns = addedFields.flatMap(fieldColumns);
|
|
300
|
+
return {
|
|
301
|
+
...migrationName('UpdatedEntity', entity.entityClass, timestamp),
|
|
302
|
+
tableName: entity.entityTableName,
|
|
303
|
+
addedColumns,
|
|
304
|
+
// The TypeScript of the names of the columns the migration adds and drops, and of the helpers it imports.
|
|
305
|
+
addedColumnNames: namesLiteral(addedColumns.map(column => column.name)),
|
|
306
|
+
removedColumnNames: namesLiteral(removedFields.flatMap(removedFieldColumnNames)),
|
|
307
|
+
schemaImports: addedColumns.length > 0 ? 'addColumns, dropColumns' : 'dropColumns',
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/** Whether a relationship has a join column: a many-to-one one, or a one-to-one one on its owner side. */
|
|
312
|
+
const hasJoinColumn = ({ relationshipType, ownerSide }) =>
|
|
313
|
+
relationshipType === 'many-to-one' || (relationshipType === 'one-to-one' && ownerSide);
|
|
314
|
+
|
|
315
|
+
/** Whether a relationship has a join table: a many-to-many one on its owner side. */
|
|
316
|
+
const hasJoinTable = ({ relationshipType, ownerSide }) => relationshipType === 'many-to-many' && ownerSide;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The migration of the relationships of an entity an update changelog changes, like the updated_entity_constraints changelog
|
|
320
|
+
* of Liquibase: the join columns with their foreign keys and the join tables of the added relationships, and the ones of
|
|
321
|
+
* the removed relationships dropped. None without a join column or a join table added or removed.
|
|
322
|
+
* @param {{ entity: any, addedRelationships: any[], removedRelationships: any[] }} changes the changes of the changelog (changelogChanges)
|
|
323
|
+
* @param {number} timestamp the timestamp of the migration
|
|
324
|
+
*/
|
|
325
|
+
export const updatedConstraintsMigration = ({ entity, addedRelationships, removedRelationships }, timestamp) => {
|
|
326
|
+
const addedJoinColumns = addedRelationships.filter(hasJoinColumn);
|
|
327
|
+
const changes = {
|
|
328
|
+
addedJoinColumns: addedJoinColumns.map(relationship => ({
|
|
329
|
+
...referenceColumn(joinColumnName(relationship), relationship.otherEntity),
|
|
330
|
+
nullable: true,
|
|
331
|
+
})),
|
|
332
|
+
addedForeignKeys: addedJoinColumns.map(foreignKey),
|
|
333
|
+
addedJoinTables: addedRelationships.filter(hasJoinTable).map(relationship => joinTable(entity, relationship)),
|
|
334
|
+
removedJoinColumns: removedRelationships.filter(hasJoinColumn).map(joinColumnName),
|
|
335
|
+
removedJoinTables: removedRelationships.filter(hasJoinTable).map(relationship => relationship.joinTable.name),
|
|
336
|
+
};
|
|
337
|
+
if (Object.values(changes).every(list => list.length === 0)) return undefined;
|
|
338
|
+
return {
|
|
339
|
+
...migrationName('UpdatedEntityConstraints', entity.entityClass, timestamp),
|
|
340
|
+
tableName: entity.entityTableName,
|
|
341
|
+
...changes,
|
|
342
|
+
// The TypeScript of the names of the join columns, of the unique ones of the one-to-one relationships, and of the helpers
|
|
343
|
+
// the migration imports.
|
|
344
|
+
addedJoinColumnNames: namesLiteral(changes.addedJoinColumns.map(column => column.name)),
|
|
345
|
+
relationUniques: namesLiteral(
|
|
346
|
+
addedJoinColumns.filter(relationship => relationship.relationshipType === 'one-to-one').map(joinColumnName),
|
|
347
|
+
),
|
|
348
|
+
removedJoinColumnNames: namesLiteral(changes.removedJoinColumns),
|
|
349
|
+
schemaImports: [
|
|
350
|
+
addedJoinColumns.length > 0 && 'addColumns, createForeignKeys',
|
|
351
|
+
changes.addedJoinTables.length > 0 && 'createJoinTable',
|
|
352
|
+
(addedJoinColumns.length > 0 || changes.removedJoinColumns.length > 0) && 'dropColumns',
|
|
353
|
+
]
|
|
354
|
+
.filter(Boolean)
|
|
355
|
+
.join(', '),
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* The join table of the authorities of the built-in users, named like TypeORM names the junction table of the @JoinTable()
|
|
361
|
+
* of User.authorities: the tables and the property, its columns the tables and their primary keys.
|
|
362
|
+
*/
|
|
363
|
+
export const userAuthorityJoinTable = ({ user, authority }) => ({
|
|
364
|
+
name: `${user.entityTableName}_authorities_${authority.entityTableName}`,
|
|
365
|
+
joinColumn: {
|
|
366
|
+
...referenceColumn(typeormCamelCase(`${user.entityTableName}_${user.primaryKey.name}`), user),
|
|
367
|
+
referencedTableName: user.entityTableName,
|
|
368
|
+
referencedColumnName: user.primaryKey.name,
|
|
369
|
+
},
|
|
370
|
+
inverseJoinColumn: {
|
|
371
|
+
name: typeormCamelCase(`${authority.entityTableName}_name`),
|
|
372
|
+
type: 'String',
|
|
373
|
+
referencedTableName: authority.entityTableName,
|
|
374
|
+
referencedColumnName: 'name',
|
|
375
|
+
},
|
|
376
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** An ISO-8601 duration, like Duration.parse of Java: the pattern of parseDuration (src/common/database/column-transformers.ts). */
|
|
2
|
+
const ISO_DURATION = String.raw`/^([-+]?)P(?:([-+]?\d+)D)?(?:T(?:([-+]?\d+)H)?(?:([-+]?\d+)M)?(?:([-+]?)(\d+)(?:[.,](\d{0,9}))?S)?)?$/i`;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The class-validator decorators of the type of each field type, like the JSON deserialization of JHipster Spring Boot refuses
|
|
6
|
+
* a value of another type (400), instead of the error of the database or of a column transformer (500): a number, a
|
|
7
|
+
* boolean, a UUID, an ISO-8601 instant, a yyyy-MM-dd date, a HH:mm:ss time or an ISO-8601 duration.
|
|
8
|
+
*/
|
|
9
|
+
const typeValidators = {
|
|
10
|
+
Integer: ['@IsInt()'],
|
|
11
|
+
Long: ['@IsInt()'],
|
|
12
|
+
Float: ['@IsNumber()'],
|
|
13
|
+
Double: ['@IsNumber()'],
|
|
14
|
+
BigDecimal: ['@IsNumber()'],
|
|
15
|
+
Boolean: ['@IsBoolean()'],
|
|
16
|
+
UUID: ['@IsUUID()'],
|
|
17
|
+
Instant: ['@IsISO8601()'],
|
|
18
|
+
ZonedDateTime: ['@IsISO8601()'],
|
|
19
|
+
LocalDate: [String.raw`@Matches(/^\d{4}-\d{2}-\d{2}$/)`, '@IsISO8601({ strict: true })'],
|
|
20
|
+
LocalTime: [String.raw`@Matches(/^\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/)`],
|
|
21
|
+
Duration: [`@Matches(${ISO_DURATION})`],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** The class-validator decorators of the type of a field: none for a text, a blob or an enum (@IsEnum). */
|
|
25
|
+
export const fieldTypeValidators = field => (field.fieldIsEnum ? [] : (typeValidators[field.fieldType] ?? []));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.git
|
|
2
|
+
Dockerfile
|
|
3
|
+
.dockerignore
|
|
4
|
+
node_modules
|
|
5
|
+
yarn.lock
|
|
6
|
+
|
|
7
|
+
<%= nodeServerRootDir %>node_modules
|
|
8
|
+
<%= nodeServerRootDir %>dist
|
|
9
|
+
<%= nodeServerRootDir %>.env
|
|
10
|
+
<%= nodeServerRootDir %>yarn.lock
|
|
11
|
+
|
|
12
|
+
<%= clientRootDir %>node_modules
|
|
13
|
+
<%= clientRootDir %>yarn.lock
|
|
14
|
+
|
|
15
|
+
<%= temporaryDir %>
|
|
16
|
+
target
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Copyright 2013-2023 the original author or authors from the JHipster project.
|
|
3
|
+
|
|
4
|
+
This file is part of the JHipster project, see https://www.jhipster.tech/
|
|
5
|
+
for more information.
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
18
|
+
-%>
|
|
19
|
+
<&_ if (!fragment.section) { -&>
|
|
20
|
+
# Generated by jhipster-nodejs:node-server
|
|
21
|
+
<%- nodeServerRootDir %>dist/
|
|
22
|
+
# Local settings of the server, like secrets (the development profile is in .env.dev)
|
|
23
|
+
<%- nodeServerRootDir %>.env
|
|
24
|
+
<&_ } -&>
|