Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/migrate-mailer-openapi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/meteor': patch
'@rocket.chat/rest-typings': patch
---

Migrated `mailer` and `mailer.unsubscribe` REST API endpoints from deprecated `addRoute` pattern to new chainable `.post()` pattern with inline AJV response schemas, enabling automatic OpenAPI 3.0.3 documentation generation.
116 changes: 93 additions & 23 deletions apps/meteor/app/api/server/v1/mailer.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,111 @@
import { isMailerProps, isMailerUnsubscribeProps } from '@rocket.chat/rest-typings';
import {
ajv,
validateBadRequestErrorResponse,
validateUnauthorizedErrorResponse,
validateForbiddenErrorResponse,
} from '@rocket.chat/rest-typings';

import { sendMail } from '../../../mail-messages/server/functions/sendMail';
import { Mailer } from '../../../mail-messages/server/lib/Mailer';
import type { ExtractRoutesFromAPI } from '../ApiClass';
import { API } from '../api';

API.v1.addRoute(
'mailer',
{
authRequired: true,
validateParams: isMailerProps,
permissionsRequired: ['send-mail'],
type MailerProps = {
from: string;
subject: string;
body: string;
dryrun?: boolean;
query?: string;
};

const isMailerProps = ajv.compile<MailerProps>({
type: 'object',
properties: {
from: { type: 'string' },
subject: { type: 'string' },
body: { type: 'string' },
dryrun: { type: 'boolean', nullable: true },
query: { type: 'string', nullable: true },
Comment thread
amitb0ra marked this conversation as resolved.
},
required: ['from', 'subject', 'body'],
additionalProperties: false,
});

type MailerUnsubscribeProps = {
_id: string;
createdAt: string;
};

const isMailerUnsubscribeProps = ajv.compile<MailerUnsubscribeProps>({
type: 'object',
properties: {
_id: { type: 'string' },
createdAt: { type: 'string' },
},
{
async post() {
required: ['_id', 'createdAt'],
additionalProperties: false,
});

const mailerEndpoints = API.v1
.post(
'mailer',
{
authRequired: true,
body: isMailerProps,
permissionsRequired: ['send-mail'],
response: {
200: ajv.compile<void>({
type: 'object',
properties: {
success: { type: 'boolean', enum: [true] },
},
required: ['success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { from, subject, body, dryrun, query } = this.bodyParams;

const result = await sendMail({ from, subject, body, dryrun: Boolean(dryrun), query });

return API.v1.success(result);
},
},
);

API.v1.addRoute(
'mailer.unsubscribe',
{
authRequired: true,
validateParams: isMailerUnsubscribeProps,
rateLimiterOptions: { intervalTimeInMS: 60000, numRequestsAllowed: 1 },
},
{
async post() {
)
.post(
'mailer.unsubscribe',
{
authRequired: true,
body: isMailerUnsubscribeProps,
rateLimiterOptions: { intervalTimeInMS: 60000, numRequestsAllowed: 1 },
response: {
200: ajv.compile<void>({
type: 'object',
properties: {
success: { type: 'boolean', enum: [true] },
},
required: ['success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { _id, createdAt } = this.bodyParams;

await Mailer.unsubscribe(_id, createdAt);

return API.v1.success();
},
},
);
);

export type MailerEndpoints = ExtractRoutesFromAPI<typeof mailerEndpoints>;

declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
interface Endpoints extends MailerEndpoints {}
}
5 changes: 0 additions & 5 deletions packages/rest-typings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import type { IntegrationHooksEndpoints } from './v1/integrations/hooks';
import type { InvitesEndpoints } from './v1/invites';
import type { LDAPEndpoints } from './v1/ldap';
import type { LicensesEndpoints } from './v1/licenses';
import type { MailerEndpoints } from './v1/mailer';
import type { MeEndpoints } from './v1/me';
import type { MiscEndpoints } from './v1/misc';
import type { ModerationEndpoints } from './v1/moderation';
Expand Down Expand Up @@ -81,7 +80,6 @@ export interface Endpoints
E2eEndpoints,
AssetsEndpoints,
EmailInboxEndpoints,
MailerEndpoints,
SubscriptionsEndpoints,
AutoTranslateEndpoints,
ImportEndpoints,
Expand Down Expand Up @@ -213,9 +211,6 @@ export * from './v1/channels';
export * from './v1/customSounds';
export * from './v1/customUserStatus';
export * from './v1/subscriptionsEndpoints';
export * from './v1/mailer';
export * from './v1/mailer/MailerParamsPOST';
export * from './v1/mailer/MailerUnsubscribeParamsPOST';
export * from './v1/misc';
export * from './v1/invites';
export * from './v1/dm';
Expand Down
12 changes: 0 additions & 12 deletions packages/rest-typings/src/v1/mailer.ts

This file was deleted.

36 changes: 0 additions & 36 deletions packages/rest-typings/src/v1/mailer/MailerParamsPOST.ts

This file was deleted.

This file was deleted.