mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-13 19:30:30 +00:00
feat(nestjs): migrate to NestJS
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { castArray } from 'lodash';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { MailTenancy } from '../MailTenancy/MailTenancy.service';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { Customer } from '../Customers/models/Customer';
|
||||
import { CommonMailOptions } from './MailNotification.types';
|
||||
import { formatMessage } from '@/utils/format-message';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class ContactMailNotification {
|
||||
constructor(
|
||||
private readonly mailTenancy: MailTenancy,
|
||||
private readonly tenantContext: TenancyContext,
|
||||
|
||||
@Inject(Customer.name)
|
||||
private readonly customerModel: TenantModelProxy<typeof Customer>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Gets the default mail address of the given contact.
|
||||
* @param {number} invoiceId - Contact id.
|
||||
* @returns {Promise<Pick<CommonMailOptions, 'to' | 'from'>>}
|
||||
*/
|
||||
public async getDefaultMailOptions(
|
||||
customerId: number,
|
||||
): Promise<
|
||||
Pick<CommonMailOptions, 'to' | 'from' | 'toOptions' | 'fromOptions'>
|
||||
> {
|
||||
const customer = await this.customerModel()
|
||||
.query()
|
||||
.findById(customerId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const toOptions = customer.contactAddresses;
|
||||
const fromOptions = await this.mailTenancy.senders();
|
||||
|
||||
const toAddress = toOptions.find((a) => a.primary);
|
||||
const fromAddress = fromOptions.find((a) => a.primary);
|
||||
|
||||
const to = toAddress?.mail ? castArray(toAddress?.mail) : [];
|
||||
const from = fromAddress?.mail ? castArray(fromAddress?.mail) : [];
|
||||
|
||||
return { to, from, toOptions, fromOptions };
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the mail options of the given contact.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @returns {Promise<CommonMailOptions>}
|
||||
*/
|
||||
public async formatMailOptions(
|
||||
mailOptions: CommonMailOptions,
|
||||
formatterArgs?: Record<string, any>,
|
||||
): Promise<CommonMailOptions> {
|
||||
const commonFormatArgs = await this.getCommonFormatArgs();
|
||||
const formatArgs = {
|
||||
...commonFormatArgs,
|
||||
...formatterArgs,
|
||||
};
|
||||
const subjectFormatted = formatMessage(mailOptions?.subject, formatArgs);
|
||||
const messageFormatted = formatMessage(mailOptions?.message, formatArgs);
|
||||
|
||||
return {
|
||||
...mailOptions,
|
||||
subject: subjectFormatted,
|
||||
message: messageFormatted,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the common format args.
|
||||
* @returns {Promise<Record<string, string>>}
|
||||
*/
|
||||
public async getCommonFormatArgs(): Promise<Record<string, string>> {
|
||||
const tenantMetadata = await this.tenantContext.getTenantMetadata();
|
||||
|
||||
return {
|
||||
['Company Name']: tenantMetadata.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ContactMailNotification } from './ContactMailNotification';
|
||||
import { MailTenancyModule } from '../MailTenancy/MailTenancy.module';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
|
||||
@Module({
|
||||
imports: [MailTenancyModule],
|
||||
providers: [ContactMailNotification, TenancyContext],
|
||||
exports: [ContactMailNotification],
|
||||
})
|
||||
export class MailNotificationModule {}
|
||||
@@ -0,0 +1,44 @@
|
||||
export type IMailAttachment = MailAttachmentPath | MailAttachmentContent;
|
||||
|
||||
export interface MailAttachmentPath {
|
||||
filename: string;
|
||||
path: string;
|
||||
cid: string;
|
||||
}
|
||||
export interface MailAttachmentContent {
|
||||
filename: string;
|
||||
content: Buffer;
|
||||
}
|
||||
|
||||
export interface IMailable {
|
||||
constructor(view: string, data?: { [key: string]: string | number });
|
||||
send(): Promise<any>;
|
||||
build(): void;
|
||||
setData(data: { [key: string]: string | number }): IMailable;
|
||||
setTo(to: string): IMailable;
|
||||
setFrom(from: string): IMailable;
|
||||
setSubject(subject: string): IMailable;
|
||||
setView(view: string): IMailable;
|
||||
render(data?: { [key: string]: string | number }): string;
|
||||
getViewContent(): string;
|
||||
}
|
||||
|
||||
export interface AddressItem {
|
||||
label: string;
|
||||
mail: string;
|
||||
primary?: boolean;
|
||||
}
|
||||
|
||||
export interface CommonMailOptions {
|
||||
from: Array<string>;
|
||||
subject: string;
|
||||
message: string;
|
||||
to: Array<string>;
|
||||
cc?: Array<string>;
|
||||
bcc?: Array<string>;
|
||||
formatArgs?: Record<string, any>;
|
||||
toOptions: Array<AddressItem>;
|
||||
fromOptions: Array<AddressItem>;
|
||||
}
|
||||
|
||||
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {}
|
||||
@@ -0,0 +1,6 @@
|
||||
export const ERRORS = {
|
||||
MAIL_FROM_NOT_FOUND: 'Mail from address not found',
|
||||
MAIL_TO_NOT_FOUND: 'Mail to address not found',
|
||||
MAIL_SUBJECT_NOT_FOUND: 'Mail subject not found',
|
||||
MAIL_BODY_NOT_FOUND: 'Mail body not found',
|
||||
};
|
||||
56
packages/server/src/modules/MailNotification/utils.ts
Normal file
56
packages/server/src/modules/MailNotification/utils.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { castArray, isEmpty } from 'lodash';
|
||||
import { ERRORS } from './constants';
|
||||
import { CommonMailOptions } from './MailNotification.types';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
|
||||
/**
|
||||
* Merges the mail options with incoming options.
|
||||
* @param {Partial<SaleInvoiceMailOptions>} mailOptions
|
||||
* @param {Partial<SendInvoiceMailDTO>} overridedOptions
|
||||
*/
|
||||
export function parseMailOptions(
|
||||
mailOptions: CommonMailOptions,
|
||||
overridedOptions: Partial<CommonMailOptions>
|
||||
): CommonMailOptions {
|
||||
const mergedMessageOptions = {
|
||||
...mailOptions,
|
||||
...overridedOptions,
|
||||
};
|
||||
const parsedMessageOptions = {
|
||||
...mergedMessageOptions,
|
||||
from: mergedMessageOptions?.from
|
||||
? castArray(mergedMessageOptions?.from)
|
||||
: [],
|
||||
to: mergedMessageOptions?.to ? castArray(mergedMessageOptions?.to) : [],
|
||||
cc: mergedMessageOptions?.cc ? castArray(mergedMessageOptions?.cc) : [],
|
||||
bcc: mergedMessageOptions?.bcc ? castArray(mergedMessageOptions?.bcc) : [],
|
||||
};
|
||||
return parsedMessageOptions;
|
||||
}
|
||||
|
||||
export function validateRequiredMailOptions(
|
||||
mailOptions: Partial<CommonMailOptions>
|
||||
) {
|
||||
if (isEmpty(mailOptions.from)) {
|
||||
throw new ServiceError(ERRORS.MAIL_FROM_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mailOptions.to)) {
|
||||
throw new ServiceError(ERRORS.MAIL_TO_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mailOptions.subject)) {
|
||||
throw new ServiceError(ERRORS.MAIL_SUBJECT_NOT_FOUND);
|
||||
}
|
||||
if (isEmpty(mailOptions.message)) {
|
||||
throw new ServiceError(ERRORS.MAIL_BODY_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
export const mergeAndValidateMailOptions = (
|
||||
mailOptions: CommonMailOptions,
|
||||
overridedOptions: Partial<CommonMailOptions>
|
||||
): CommonMailOptions => {
|
||||
const parsedMessageOptions = parseMailOptions(mailOptions, overridedOptions);
|
||||
validateRequiredMailOptions(parsedMessageOptions);
|
||||
|
||||
return parsedMessageOptions;
|
||||
};
|
||||
Reference in New Issue
Block a user