Skip to content

Commit

Permalink
Brevo service integrated
Browse files Browse the repository at this point in the history
  • Loading branch information
kaihaase committed Dec 18, 2023
1 parent 5a5407b commit c4fca34
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 16 deletions.
32 changes: 22 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lenne.tech/nest-server",
"version": "10.2.6",
"version": "10.2.7",
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
"keywords": [
"node",
Expand Down Expand Up @@ -63,6 +63,7 @@
},
"dependencies": {
"@apollo/gateway": "2.5.7",
"@getbrevo/brevo": "1.0.1",
"@lenne.tech/mongoose-gridfs": "1.4.2",
"@lenne.tech/multer-gridfs-storage": "5.0.6",
"@nestjs/apollo": "12.0.11",
Expand Down
2 changes: 1 addition & 1 deletion spectaql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ servers:
info:
title: lT Nest Server
description: Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).
version: 10.2.6
version: 10.2.7
contact:
name: lenne.Tech GmbH
url: https://lenne.tech
Expand Down
35 changes: 35 additions & 0 deletions src/core/common/helpers/table.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export function htmlTable(
header: string[],
rows: string[][],
options?: {
tableStyle?: string;
theadStyle?: string;
trHeadStyle?: string;
thStyle?: string;
tbodyStyle?: string;
trStyle?: string;
tdStyle?: string;
},
): string {
const config = {
tableStyle: 'width: 100%; border: 1px solid #000; border-collapse: collapse;',
trHeadStyle: 'background-color: #f0f0f0;',
thStyle: 'border: 1px solid #000; padding: 10px;',
tcStyle: 'border: 1px solid #000; padding: 10px;',
...options,
};
let table = `<table style="${config.tableStyle}"><thead style="${config.theadStyle}"><tr style="${config.trHeadStyle}">`;
for (const head of header) {
table += `<th style="${config.thStyle}">${head}</th>`;
}
table += '</tr></thead><tbody style="${config.tbodyStyle}">';
for (const row of rows) {
table += `<tr style="${config.trStyle}">`;
for (const cell of row) {
table += `<td style="${config.tdStyle}">${cell}</td>`;
}
table += '</tr>';
}
table += '</tbody></table>';
return table;
}
17 changes: 17 additions & 0 deletions src/core/common/interfaces/server-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ export interface IServerOptions {
*/
automaticObjectIdFiltering?: boolean;

/**
* Configuration for Brevo
* See: https://developers.brevo.com/
*/
brevo?: {
/**
* API key for Brevo
*/
apiKey?: string;

/**
* Regular expression for excluding (test) users
* e.g. /@testuser.com$/i
*/
exclude?: RegExp;
};

/**
* Whether to use the compression middleware package to enable gzip compression.
* See: https://docs.nestjs.com/techniques/compression
Expand Down
48 changes: 48 additions & 0 deletions src/core/common/services/brevo.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Injectable } from '@nestjs/common';
import brevo = require('@getbrevo/brevo');
import { ConfigService } from './config.service';

@Injectable()
export class BrevoService {
constructor(protected configService: ConfigService) {
const defaultClient = brevo.ApiClient.instance;
const apiKey = defaultClient.authentications['api-key'];
apiKey.apiKey = configService.configFastButReadOnly.brevo?.apiKey;
if (!apiKey.apiKey) {
console.warn('Brevo API key not set!');
}
}

/**
* Send a transactional email via Brevo
*/
async sendMail(to: string, templateId: number, params?: object): Promise<unknown> {

// Check input
if (!to || !templateId) {
return false;
}

// Exclude (test) users
if (this.configService.configFastButReadOnly.brevo?.exclude?.test?.(to)) {
return 'TEST_USER!';
}

// Prepare data
const apiInstance = new brevo.TransactionalEmailsApi();
const sendSmtpEmail = new brevo.SendSmtpEmail();
sendSmtpEmail.templateId = templateId;
sendSmtpEmail.to = [{ email: to }];
sendSmtpEmail.params = params;

// Send email
try {
return await apiInstance.sendTransacEmail(sendSmtpEmail);
} catch (error) {
console.error(error);
}

// Return null if error
return null;
}
}
47 changes: 46 additions & 1 deletion src/core/common/services/crud.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NotFoundException } from '@nestjs/common';
import { Document, FilterQuery, PipelineStage, Query, QueryOptions } from 'mongoose';
import { AggregateOptions, Document, FilterQuery, PipelineStage, Query, QueryOptions } from 'mongoose';
import { FilterArgs } from '../args/filter.args';
import { getStringIds } from '../helpers/db.helper';
import { convertFilterArgsToQuery } from '../helpers/filter.helper';
Expand All @@ -15,6 +15,51 @@ export abstract class CrudService<
CreateInput = any,
UpdateInput = any,
> extends ModuleService<Model> {

/**
* Aggregate
* @param serviceOptions.aggregateOptions Aggregate options, see https://www.mongodb.com/docs/manual/core/aggregation-pipeline/
* @param serviceOptions.collation Collation, see https://www.mongodb.com/docs/manual/reference/collation/
* @param serviceOptions.outputPath Output path of items which should be prepared, e.g. 'items'
*/
async aggregate<T = any>(
pipeline: PipelineStage[],
serviceOptions?: ServiceOptions & { aggregateOptions?: AggregateOptions },
): Promise<T> {
return this.process(
async () => {
const aggregateOptions = serviceOptions?.aggregateOptions || {};
const collation = serviceOptions?.collation || ConfigService.get('mongoose.collation');
if (collation && !aggregateOptions.collation) {
aggregateOptions.collation = collation;
}
return this.mainDbModel.aggregate(pipeline, aggregateOptions).exec();
},
{ serviceOptions },
);
}

/**
* Aggregate without checks or restrictions
* Warning: Disables the handling of rights and restrictions!
*/
async aggregateForce<T = any>(pipeline: PipelineStage[], serviceOptions: ServiceOptions = {}): Promise<T> {
serviceOptions = serviceOptions || {};
serviceOptions.force = true;
return this.aggregate(pipeline, serviceOptions);
}

/**
* Aggregate without checks, restrictions or preparations
* Warning: Disables the handling of rights and restrictions! The raw data may contain secrets (such as passwords).
*/
async aggregateRaw<T = any>(pipeline: PipelineStage[], serviceOptions: ServiceOptions = {}): Promise<T> {
serviceOptions = serviceOptions || {};
serviceOptions.prepareInput = null;
serviceOptions.prepareOutput = null;
return this.aggregateForce(pipeline, serviceOptions);
}

/**
* Create item
*/
Expand Down
18 changes: 15 additions & 3 deletions src/core/common/services/module.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,14 @@ export abstract class ModuleService<T extends CoreModel = any> {

// Pop and map main model
if (config.processFieldSelection && config.fieldSelection && this.processFieldSelection) {
const field = config.outputPath ? _.get(result, config.outputPath) : result;
await this.processFieldSelection(field, config.fieldSelection, config.processFieldSelection);
let temps = result;
if (!Array.isArray(result)) {
temps = [result];
}
for (const temp of temps) {
const field = config.outputPath ? _.get(temp, config.outputPath) : temp;
await this.processFieldSelection(field, config.fieldSelection, config.processFieldSelection);
}
}

// Prepare output
Expand All @@ -191,7 +197,13 @@ export abstract class ModuleService<T extends CoreModel = any> {
opts.targetModel = config.outputType;
}
if (config.outputPath) {
_.set(result, config.outputPath, await this.prepareOutput(_.get(result, config.outputPath), opts));
let temps = result;
if (!Array.isArray(result)) {
temps = [result];
}
for (const temp of temps) {
_.set(temp, config.outputPath, await this.prepareOutput(_.get(temp, config.outputPath), opts));
}
} else {
result = await this.prepareOutput(result, config);
}
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export * from './core/common/helpers/graphql.helper';
export * from './core/common/helpers/input.helper';
export * from './core/common/helpers/model.helper';
export * from './core/common/helpers/service.helper';
export * from './core/common/helpers/table.helper';
export * from './core/common/inputs/combined-filter.input';
export * from './core/common/inputs/core-input.input';
export * from './core/common/inputs/filter.input';
Expand Down Expand Up @@ -61,6 +62,7 @@ export * from './core/common/scalars/any.scalar';
export * from './core/common/scalars/date.scalar';
export * from './core/common/scalars/date-timestamp.scalar';
export * from './core/common/scalars/json.scalar';
export * from './core/common/services/brevo.service';
export * from './core/common/services/config.service';
export * from './core/common/services/core-cron-jobs.service';
export * from './core/common/services/crud.service';
Expand Down

0 comments on commit c4fca34

Please sign in to comment.