Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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/small-mangos-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/apps-engine": minor
---

Creates a new endpoint that allows agents to send an outbound message from a registered app provider
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
ValidOutboundProvider,
IOutboundMessageProviderService,
IOutboundProviderMetadata,
IOutboundMessage,
} from '@rocket.chat/core-typings';
import { ValidOutboundProviderList } from '@rocket.chat/core-typings';

Expand Down Expand Up @@ -55,13 +56,13 @@ export class OutboundMessageProviderService implements IOutboundMessageProviderS
return manager;
}

public sendMessage(providerId: string, body: any) {
public sendMessage(providerId: string, message: IOutboundMessage) {
const provider = this.provider.findOneByProviderId(providerId);
if (!provider) {
throw new Error('error-invalid-provider');
}

return this.getProviderManager().sendOutboundMessage(provider.appId, provider.type, body);
return this.getProviderManager().sendOutboundMessage(provider.appId, provider.type, message);
}
}

Expand Down
17 changes: 17 additions & 0 deletions apps/meteor/ee/app/livechat-enterprise/server/api/outbound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {
GETOutboundProviderParamsSchema,
GETOutboundProviderBadRequestErrorSchema,
GETOutboundProviderMetadataSchema,
POSTOutboundMessageParams,
POSTOutboundMessageErrorSchema,
POSTOutboundMessageSuccessSchema,
} from '../outboundcomms/rest';
import { outboundMessageProvider } from './lib/outbound';
import type { ExtractRoutesFromAPI } from '../../../../../app/api/server/ApiClass';
Expand Down Expand Up @@ -45,6 +48,20 @@ const outboundCommsEndpoints = API.v1
metadata: providerMetadata,
});
},
)
.post(
'omnichannel/outbound/providers/:id/message',
{
response: { 200: POSTOutboundMessageSuccessSchema, 400: POSTOutboundMessageErrorSchema },
authRequired: true,
body: POSTOutboundMessageParams,
},
async function action() {
const { id } = this.urlParams;

await outboundMessageProvider.sendMessage(id, this.bodyParams);
return API.v1.success();
},
);

export type OutboundCommsEndpoints = ExtractRoutesFromAPI<typeof outboundCommsEndpoints>;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IOutboundMessage, IOutboundProvider, IOutboundProviderMetadata } from '@rocket.chat/core-typings';
import type { IOutboundMessage, IOutboundProvider, IOutboundProviderMetadata, ValidOutboundProvider } from '@rocket.chat/core-typings';
import Ajv from 'ajv';

import type { OutboundCommsEndpoints } from '../api/outbound';
Expand All @@ -12,7 +12,9 @@ declare module '@rocket.chat/rest-typings' {
interface Endpoints extends OutboundCommsEndpoints {}
}

type GETOutboundProviderParams = { type?: string };
type GenericErrorResponse = { success: boolean; message: string };

type GETOutboundProviderParams = { type?: ValidOutboundProvider };
const GETOutboundProviderSchema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -64,18 +66,15 @@ const GETOutboundProviderBadRequestError = {
},
},
};
export const GETOutboundProviderBadRequestErrorSchema = ajv.compile<{ success: boolean; message: string }>(
GETOutboundProviderBadRequestError,
);
export const GETOutboundProviderBadRequestErrorSchema = ajv.compile<GenericErrorResponse>(GETOutboundProviderBadRequestError);

type POSTOutboundMessageParamsType = IOutboundMessage;

type POSTOutboundMessageParams = {
message: IOutboundMessage;
};
const POSTOutboundMessageSchema = {
type: 'object',
required: ['to', 'type'],
properties: {
to: { type: 'string' },
to: { type: 'string', minLength: 1 },
type: { type: 'string' },
templateProviderPhoneNumber: { type: 'string' },
template: {
Expand Down Expand Up @@ -183,7 +182,30 @@ const POSTOutboundMessageSchema = {
additionalProperties: false,
};

export const isPOSTOutboundMessageParams = ajv.compile<POSTOutboundMessageParams>(POSTOutboundMessageSchema);
export const POSTOutboundMessageParams = ajv.compile<POSTOutboundMessageParamsType>(POSTOutboundMessageSchema);

const POSTOutboundMessageError = {
type: 'object',
properties: {
success: {
type: 'boolean',
},
message: {
type: 'string',
},
},
additionalProperties: false,
};

export const POSTOutboundMessageErrorSchema = ajv.compile<GenericErrorResponse>(POSTOutboundMessageError);

const POSTOutboundMessageSuccess = {
type: 'object',
properties: {},
additionalProperties: false,
};

export const POSTOutboundMessageSuccessSchema = ajv.compile<void>(POSTOutboundMessageSuccess);

const OutboundProviderMetadataSchema = {
type: 'object',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ export interface IOutboundMessage {
};
}

type TemplateComponent = {
export type TemplateComponent = {
type: 'header' | 'body' | 'footer' | 'button';
parameters: TemplateParameter[];
};

type TemplateParameter =
export type TemplateParameter =
| {
type: 'text';
text: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface IOutboundProviderTemplate {
partnerId: string;
externalId: string;
updatedExternal: string; // ISO 8601 timestamp
rejectedReason: string | null;
rejectedReason: string | undefined;
}

type Component = IHeaderComponent | IBodyComponent | IFooterComponent;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class AppOutboundProcessError implements Error {
public name = 'OutboundProviderError';

public message: string;

constructor(message: string, where?: string) {
this.message = message;
if (where) {
this.message += ` (${where})`;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { AppAccessorManager } from '.';
import { AppMethod } from '../../definition/metadata';
import type { IOutboundMessageProviders, ProviderMetadata } from '../../definition/outboundComunication';
import type { IOutboundMessage, IOutboundMessageProviders, ProviderMetadata } from '../../definition/outboundComunication';
import type { ProxiedApp } from '../ProxiedApp';
import { AppOutboundProcessError } from '../errors/AppOutboundProcessError';
import type { AppLogStorage } from '../storage';

export class OutboundMessageProvider {
Expand All @@ -18,7 +19,7 @@ export class OutboundMessageProvider {
return this.runTheCode<ProviderMetadata>(AppMethod._OUTBOUND_GET_PROVIDER_METADATA, logStorage, accessors, []);
}

public async runSendOutboundMessage(logStorage: AppLogStorage, accessors: AppAccessorManager, body: any): Promise<void> {
public async runSendOutboundMessage(logStorage: AppLogStorage, accessors: AppAccessorManager, body: IOutboundMessage): Promise<void> {
await this.runTheCode(AppMethod._OUTBOUND_SEND_MESSAGE, logStorage, accessors, [body]);
}

Expand All @@ -41,7 +42,7 @@ export class OutboundMessageProvider {
if (e?.message === 'error-invalid-provider') {
throw new Error('error-provider-not-registered');
}
console.error(e);
throw new AppOutboundProcessError(e.message, method);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
IOutboundEmailMessageProvider,
IOutboundPhoneMessageProvider,
ValidOutboundProvider,
IOutboundMessage,
} from '../../definition/outboundComunication';
import type { AppManager } from '../AppManager';
import type { OutboundMessageBridge } from '../bridges';
Expand Down Expand Up @@ -119,7 +120,7 @@ export class AppOutboundCommunicationProviderManager {
return providerInfo.runGetProviderMetadata(this.manager.getLogStorage(), this.accessors);
}

public sendOutboundMessage(appId: string, providerType: ValidOutboundProvider, body: unknown) {
public sendOutboundMessage(appId: string, providerType: ValidOutboundProvider, body: IOutboundMessage) {
const providerInfo = this.outboundMessageProviders.get(appId)?.get(providerType);
if (!providerInfo) {
throw new Error('provider-not-registered');
Expand Down
6 changes: 3 additions & 3 deletions packages/core-typings/src/omnichannel/outbound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface IOutboundProviderTemplate {
partnerId: string;
externalId: string;
updatedExternal: string; // ISO 8601 timestamp
rejectedReason: string | null;
rejectedReason: string | undefined;
}

type Component = IHeaderComponent | IBodyComponent | IFooterComponent;
Expand Down Expand Up @@ -73,12 +73,12 @@ export interface IOutboundMessage {
};
}

type TemplateComponent = {
export type TemplateComponent = {
type: 'header' | 'body' | 'footer' | 'button';
parameters: TemplateParameter[];
};

type TemplateParameter =
export type TemplateParameter =
| {
type: 'text';
text: string;
Expand Down
Loading