Merge pull request 'patches v1.3.8' (#80) from develop into main

Reviewed-on: #80
This commit is contained in:
Julian Krauser 2025-03-28 21:29:31 +00:00
commit f9485fc31d
14 changed files with 154 additions and 23 deletions

View file

@ -9,7 +9,7 @@ export interface SynchronizeNewsletterCommand {
newsletterTitle: string;
newsletterText: string;
newsletterSignatur: string;
recipientsByQueryId?: number;
recipientsByQueryId?: string;
}
export interface SendNewsletterCommand {

View file

@ -6,10 +6,10 @@ export interface CreateQueryStoreCommand {
}
export interface UpdateQueryStoreCommand {
id: number;
id: string;
query: string | DynamicQueryStructure;
}
export interface DeleteQueryStoreCommand {
id: number;
id: string;
}

View file

@ -328,7 +328,7 @@ export async function synchronizeNewsletterById(req: Request, res: Response): Pr
let newsletterTitle = req.body.newsletterTitle;
let newsletterText = req.body.newsletterText;
let newsletterSignatur = req.body.newsletterSignatur;
let recipientsByQueryId = req.body.recipientsByQueryId ?? null;
let recipientsByQueryId = req.body.recipientsByQueryId || null;
let syncNewsletter: SynchronizeNewsletterCommand = {
id,

View file

@ -27,7 +27,7 @@ export async function getAllQueryStores(req: Request, res: Response): Promise<an
* @returns {Promise<*>}
*/
export async function getQueryStoreById(req: Request, res: Response): Promise<any> {
const id = parseInt(req.params.id);
const id = req.params.id;
let queryStore = await QueryStoreService.getById(id);
@ -61,7 +61,7 @@ export async function createQueryStore(req: Request, res: Response): Promise<any
* @returns {Promise<*>}
*/
export async function updateQueryStore(req: Request, res: Response): Promise<any> {
const id = parseInt(req.params.id);
const id = req.params.id;
const query = req.body.query;
let updateQueryStore: UpdateQueryStoreCommand = {
@ -81,7 +81,7 @@ export async function updateQueryStore(req: Request, res: Response): Promise<any
* @returns {Promise<*>}
*/
export async function deleteQueryStore(req: Request, res: Response): Promise<any> {
const id = parseInt(req.params.id);
const id = req.params.id;
let deleteQueryStore: DeleteQueryStoreCommand = {
id: id,

View file

@ -48,6 +48,7 @@ import { salutation } from "./entity/configuration/salutation";
import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200-BackupAndResetDatabase";
import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSchema";
import { TemplatesAndProtocolSort1742549956787 } from "./migrations/1742549956787-templatesAndProtocolSort";
import { QueryToUUID1742922178643 } from "./migrations/1742922178643-queryToUUID";
const dataSource = new DataSource({
type: DB_TYPE as any,
@ -101,7 +102,12 @@ const dataSource = new DataSource({
webapi,
webapiPermission,
],
migrations: [BackupAndResetDatabase1738166124200, CreateSchema1738166167472, TemplatesAndProtocolSort1742549956787],
migrations: [
BackupAndResetDatabase1738166124200,
CreateSchema1738166167472,
TemplatesAndProtocolSort1742549956787,
QueryToUUID1742922178643,
],
migrationsRun: true,
migrationsTransactionMode: "each",
subscribers: [],

View file

@ -1,6 +1,5 @@
import { Column, Entity, ManyToOne, OneToMany, PrimaryColumn } from "typeorm";
import { newsletterDates } from "./newsletterDates";
import { member } from "../member/member";
import { newsletterRecipients } from "./newsletterRecipients";
import { query } from "../../configuration/query";
@ -27,8 +26,8 @@ export class newsletter {
@Column({ type: "boolean", default: false })
isSent: boolean;
@Column({ type: "int", nullable: true })
recipientsByQueryId?: number;
@Column({ nullable: true })
recipientsByQueryId?: string;
@OneToMany(() => newsletterDates, (dates) => dates.newsletter, { cascade: ["insert"] })
dates: newsletterDates[];

View file

@ -2,8 +2,8 @@ import { Column, Entity, PrimaryColumn } from "typeorm";
@Entity()
export class query {
@PrimaryColumn({ generated: "increment", type: "int" })
id: number;
@PrimaryColumn({ generated: "uuid", type: "varchar" })
id: string;
@Column({ type: "varchar", length: 255, unique: true })
title: string;

View file

@ -6,6 +6,7 @@ import InternalException from "../exceptions/internalException";
import UserService from "../service/management/userService";
import { BACKUP_COPIES, BACKUP_INTERVAL } from "../env.defaults";
import DatabaseActionException from "../exceptions/databaseActionException";
import { availableTemplates } from "../type/templateTypes";
export type BackupSection =
| "member"
@ -212,7 +213,7 @@ export default abstract class BackupHelper {
case "calendar":
return await this.getCalendar();
case "query":
return await this.getQueryStore();
return await this.getQueryStore(collectIds);
case "template":
return await this.getTemplate();
case "user":
@ -348,7 +349,7 @@ export default abstract class BackupHelper {
"member.birthdate",
"member.internalId",
])
.addSelect(["recipientsByQuery.title", "recipientsByQuery.query"])
.addSelect([...(collectIds ? ["query.id"] : []), "recipientsByQuery.title", "recipientsByQuery.query"])
.getMany()
.then((res: any) =>
res.map((n: any) => ({
@ -393,8 +394,8 @@ export default abstract class BackupHelper {
.getMany(),
};
}
private static async getQueryStore(): Promise<Array<any>> {
return await dataSource.getRepository("query").find({ select: { title: true, query: true } });
private static async getQueryStore(collectIds: boolean): Promise<Array<any>> {
return await dataSource.getRepository("query").find({ select: { id: collectIds, title: true, query: true } });
}
private static async getTemplate(): Promise<{ [key: string]: Array<any> }> {
return {
@ -743,11 +744,30 @@ export default abstract class BackupHelper {
.values(data?.["template"] ?? [])
.orIgnore()
.execute();
let templates = await this.transactionManager.getRepository("template").find();
let dataWithMappedId = (data?.["template_usage"] ?? [])
.filter((d) => availableTemplates.includes(d.scope))
.map((d) => ({
...d,
headerHeightId: templates.find((template) => template.template == d.headerHeight.template)?.id ?? null,
footerHeightId: templates.find((template) => template.template == d.footerHeight.template)?.id ?? null,
headerId: templates.find((template) => template.template == d.header.template)?.id ?? null,
bodyId: templates.find((template) => template.template == d.body.template)?.id ?? null,
footerId: templates.find((template) => template.template == d.footer.template)?.id ?? null,
}));
availableTemplates.forEach((at) => {
if (!dataWithMappedId.some((d) => d.scope == at)) {
dataWithMappedId.push({
scope: at,
});
}
});
await this.transactionManager
.createQueryBuilder()
.insert()
.into("template_usage")
.values(data?.["template_usage"] ?? [])
.values(dataWithMappedId)
.orIgnore()
.execute();
}

View file

@ -3,6 +3,7 @@ import { PermissionModule } from "../type/permissionTypes";
import TemplateUsageService from "../service/configuration/templateUsageService";
import Handlebars, { template } from "handlebars";
import { FileSystemHelper } from "./fileSystemHelper";
import { TemplateFormat } from "../type/templateTypes";
export abstract class TemplateHelper {
static getTemplateFromFile(template: string) {
@ -40,7 +41,7 @@ export abstract class TemplateHelper {
bodyData = {},
footerData = {},
}: {
module: `${PermissionModule}` | `${PermissionModule}.${string}`;
module: TemplateFormat;
title?: string;
headerData?: any;
bodyData?: any;
@ -88,7 +89,7 @@ export abstract class TemplateHelper {
footerData = {},
customTemplate,
}: {
module: `${PermissionModule}` | `${PermissionModule}.${string}`;
module: TemplateFormat;
title?: string;
headerData?: any;
bodyData?: any;

View file

@ -0,0 +1,94 @@
import { MigrationInterface, QueryRunner, TableColumn, TableForeignKey } from "typeorm";
import { getTypeByORM, isIncrementPrimary, isUUIDPrimary } from "./ormHelper";
import { query } from "../entity/configuration/query";
export class QueryToUUID1742922178643 implements MigrationInterface {
name = "QueryToUUID1742922178643";
public async up(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable("newsletter");
const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("recipientsByQueryId") !== -1);
await queryRunner.dropForeignKey("newsletter", foreignKey);
const entries = await queryRunner.manager.getRepository(query).find({ select: { title: true, query: true } });
await queryRunner.clearTable("query");
await queryRunner.dropColumn("newsletter", "recipientsByQueryId");
await queryRunner.dropColumn("query", "id");
await queryRunner.addColumn(
"query",
new TableColumn({
name: "id",
...getTypeByORM("uuid"),
...isUUIDPrimary,
})
);
await queryRunner.addColumn(
"newsletter",
new TableColumn({
name: "recipientsByQueryId",
...getTypeByORM("uuid", true),
})
);
await queryRunner.manager.createQueryBuilder().insert().into("query").values(entries).execute();
await queryRunner.createForeignKey(
"newsletter",
new TableForeignKey({
columnNames: ["recipientsByQueryId"],
referencedColumnNames: ["id"],
referencedTableName: "query",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable("newsletter");
const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("recipientsByQueryId") !== -1);
await queryRunner.dropForeignKey("newsletter", foreignKey);
const entries = await queryRunner.manager.getRepository(query).find({ select: { title: true, query: true } });
await queryRunner.clearTable("query");
await queryRunner.dropColumn("newsletter", "recipientsByQueryId");
await queryRunner.dropColumn("query", "id");
await queryRunner.addColumn(
"query",
new TableColumn({
name: "id",
...getTypeByORM("int"),
...isIncrementPrimary,
})
);
await queryRunner.addColumn(
"newsletter",
new TableColumn({
name: "recipientsByQueryId",
...getTypeByORM("int", true),
})
);
await queryRunner.manager
.createQueryBuilder()
.insert()
.into("query")
.values(entries.map((e, i) => ({ ...e, id: i + 1 })))
.execute();
await queryRunner.createForeignKey(
"newsletter",
new TableForeignKey({
columnNames: ["recipientsByQueryId"],
referencedColumnNames: ["id"],
referencedTableName: "query",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
}
}

View file

@ -26,7 +26,7 @@ export default abstract class QueryStoreService {
* @description get queryStore by id
* @returns {Promise<query>}
*/
static async getById(id: number): Promise<query> {
static async getById(id: string): Promise<query> {
return await dataSource
.getRepository(query)
.createQueryBuilder("queryStore")

11
src/type/templateTypes.ts Normal file
View file

@ -0,0 +1,11 @@
import { PermissionModule } from "./permissionTypes";
export type TemplateFormat = `${PermissionModule}` | `${PermissionModule}.${string}`;
export const availableTemplates: Array<TemplateFormat> = [
"member",
"listprint",
"listprint.member",
"newsletter",
"protocol",
];

View file

@ -8,6 +8,6 @@ export interface NewsletterViewModel {
newsletterText: string;
newsletterSignatur: string;
isSent: boolean;
recipientsByQueryId?: number;
recipientsByQueryId?: string;
recipientsByQuery?: QueryStoreViewModel;
}

View file

@ -1,7 +1,7 @@
import { DynamicQueryStructure } from "../../../type/dynamicQueries";
export interface QueryStoreViewModel {
id: number;
id: string;
title: string;
query: string | DynamicQueryStructure;
}