idea-aws 4.4.4 → 4.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SES = void 0;
27
+ const client_ses_1 = require("@aws-sdk/client-ses");
28
+ const AWSSES = __importStar(require("@aws-sdk/client-sesv2"));
29
+ const nodemailer_1 = require("nodemailer");
30
+ const dynamoDB_1 = require("./dynamoDB");
31
+ const lambdaLogger_1 = require("./lambdaLogger");
32
+ /**
33
+ * A wrapper for AWS Simple Email Service.
34
+ */
35
+ class SES {
36
+ constructor(options = {}) {
37
+ this.logger = new lambdaLogger_1.LambdaLogger();
38
+ this.ses = new AWSSES.SESv2Client({ region: options.region });
39
+ }
40
+ //
41
+ // CONFIG
42
+ //
43
+ async getTemplate(templateName) {
44
+ const command = new AWSSES.GetEmailTemplateCommand({ TemplateName: templateName });
45
+ const { TemplateContent } = await this.ses.send(command);
46
+ return TemplateContent;
47
+ }
48
+ async setTemplate(templateName, subject, content, isHTML) {
49
+ let isNew = false;
50
+ try {
51
+ const command = new AWSSES.GetEmailTemplateCommand({ TemplateName: templateName });
52
+ await this.ses.send(command);
53
+ }
54
+ catch (notFound) {
55
+ isNew = true;
56
+ }
57
+ const template = {
58
+ TemplateName: templateName,
59
+ TemplateContent: { Subject: subject }
60
+ };
61
+ if (isHTML)
62
+ template.TemplateContent.Html = content;
63
+ else
64
+ template.TemplateContent.Text = content;
65
+ let command;
66
+ if (isNew)
67
+ command = new AWSSES.CreateEmailTemplateCommand(template);
68
+ else
69
+ command = new AWSSES.UpdateEmailTemplateCommand(template);
70
+ await this.ses.send(command);
71
+ }
72
+ async deleteTemplate(templateName) {
73
+ const command = new AWSSES.DeleteEmailTemplateCommand({ TemplateName: templateName });
74
+ await this.ses.send(command);
75
+ }
76
+ async testTemplate(templateName, data) {
77
+ const command = new AWSSES.TestRenderEmailTemplateCommand({
78
+ TemplateName: templateName,
79
+ TemplateData: JSON.stringify(data)
80
+ });
81
+ const { RenderedTemplate } = await this.ses.send(command);
82
+ return RenderedTemplate;
83
+ }
84
+ //
85
+ // SENDING
86
+ //
87
+ /**
88
+ * Send a templated email through AWS Simple Email Service.
89
+ */
90
+ async sendTemplatedEmail(emailData, sesParams) {
91
+ const command = new AWSSES.SendEmailCommand({
92
+ Destination: this.prepareEmailDestination(emailData),
93
+ Content: {
94
+ Template: {
95
+ TemplateName: emailData.template,
96
+ TemplateData: JSON.stringify(emailData.templateData ?? {})
97
+ }
98
+ },
99
+ ConfigurationSetName: emailData.configurationSet,
100
+ ReplyToAddresses: emailData.replyToAddresses,
101
+ FromEmailAddress: sesParams.sourceName ? `${sesParams.sourceName} <${sesParams.source}>` : sesParams.source,
102
+ FromEmailAddressIdentityArn: sesParams.sourceArn
103
+ });
104
+ let ses;
105
+ if (this.ses.config.region === sesParams.region)
106
+ ses = this.ses;
107
+ else
108
+ ses = new AWSSES.SESv2Client({ region: sesParams.region });
109
+ this.logger.trace('SES send templated email');
110
+ return await ses.send(command);
111
+ }
112
+ /**
113
+ * Send an email through AWS Simple Email Service.
114
+ * It supports IDEA's teams custom configuration.
115
+ */
116
+ async sendEmail(emailData, sesParams) {
117
+ // if requested, check whether there is a custom SES configuration to apply for the team
118
+ const customSESConfig = await this.searchForCustomSESConfigByTeamId(sesParams.teamId);
119
+ // if the email includes attachments, send with Nodemailer (to avoid size limitations)
120
+ if (emailData.attachments?.length)
121
+ return await this.sendEmailWithNodemailer(emailData, customSESConfig || sesParams);
122
+ // otherwise, send with SES (more secure)
123
+ else
124
+ return await this.sendEmailWithSES(emailData, customSESConfig || sesParams);
125
+ }
126
+ async searchForCustomSESConfigByTeamId(teamId) {
127
+ if (!teamId)
128
+ return null;
129
+ try {
130
+ return (await new dynamoDB_1.DynamoDB().get({ TableName: 'idea_teamsSES', Key: { teamId } }));
131
+ }
132
+ catch (err) {
133
+ return null;
134
+ }
135
+ }
136
+ async sendEmailWithSES(emailData, sesParams) {
137
+ const command = new AWSSES.SendEmailCommand({
138
+ Destination: this.prepareEmailDestination(emailData),
139
+ Content: { Simple: this.prepareEmailMessage(emailData) },
140
+ ReplyToAddresses: emailData.replyToAddresses,
141
+ FromEmailAddress: sesParams.sourceName ? `${sesParams.sourceName} <${sesParams.source}>` : sesParams.source,
142
+ FromEmailAddressIdentityArn: sesParams.sourceArn
143
+ });
144
+ let ses;
145
+ if (this.ses.config.region === sesParams.region)
146
+ ses = this.ses;
147
+ else
148
+ ses = new AWSSES.SESv2Client({ region: sesParams.region });
149
+ this.logger.trace('SES send email');
150
+ return await ses.send(command);
151
+ }
152
+ async sendEmailWithNodemailer(emailData, sesParams) {
153
+ const mailOptions = {};
154
+ mailOptions.to = emailData.toAddresses.join(',');
155
+ if (emailData.ccAddresses)
156
+ mailOptions.cc = emailData.ccAddresses.join(',');
157
+ if (emailData.bccAddresses)
158
+ mailOptions.bcc = emailData.bccAddresses.join(',');
159
+ mailOptions.from = sesParams.sourceName ? `${sesParams.sourceName} <${sesParams.source}>` : sesParams.source;
160
+ if (emailData.replyToAddresses)
161
+ mailOptions.replyTo = emailData.replyToAddresses.join(',');
162
+ mailOptions.subject = emailData.subject;
163
+ if (emailData.html)
164
+ mailOptions.html = emailData.html;
165
+ if (emailData.text)
166
+ mailOptions.text = emailData.text;
167
+ mailOptions.attachments = emailData.attachments;
168
+ // note: Nodemailer doesn't support SESv2 as of August 2023
169
+ const ses = new client_ses_1.SESClient({ region: sesParams.region });
170
+ this.logger.trace('SES send email (Nodemailer)');
171
+ // note: this is a workaround to make Nodemailer works with AWS SDK 3;
172
+ // see: https://github.com/nodemailer/nodemailer/issues/1430
173
+ return await (0, nodemailer_1.createTransport)({ SES: { ses, aws: { SendRawEmailCommand: client_ses_1.SendRawEmailCommand } } }).sendMail(mailOptions);
174
+ }
175
+ prepareEmailDestination(emailData) {
176
+ return {
177
+ ToAddresses: emailData.toAddresses,
178
+ CcAddresses: emailData.ccAddresses,
179
+ BccAddresses: emailData.bccAddresses
180
+ };
181
+ }
182
+ prepareEmailMessage(emailData) {
183
+ const message = {
184
+ Subject: { Charset: 'UTF-8', Data: emailData.subject },
185
+ Body: {}
186
+ };
187
+ if (emailData.html)
188
+ message.Body.Html = { Charset: 'UTF-8', Data: emailData.html };
189
+ if (emailData.text)
190
+ message.Body.Text = { Charset: 'UTF-8', Data: emailData.text };
191
+ if (!emailData.html && !emailData.text)
192
+ message.Body.Text = { Charset: 'UTF-8', Data: '' };
193
+ return message;
194
+ }
195
+ }
196
+ exports.SES = SES;
@@ -0,0 +1,60 @@
1
+ import * as AWSSNS from '@aws-sdk/client-sns';
2
+ import { PushNotificationsPlatforms } from 'idea-toolbox';
3
+ import { LambdaLogger } from './lambdaLogger';
4
+ /**
5
+ * A wrapper for AWS Simple Notification Service.
6
+ */
7
+ export declare class SNS {
8
+ protected client: AWSSNS.SNSClient;
9
+ protected logger: LambdaLogger;
10
+ constructor(options?: {
11
+ region?: string;
12
+ });
13
+ /**
14
+ * Create a new endpoint in the SNS platform specified.
15
+ * @return platform endpoint ARN
16
+ */
17
+ createPlatormEndpoint(platform: PushNotificationsPlatforms, token: string, options: SNSCreateEndpointParams): Promise<string>;
18
+ /**
19
+ * Publish a message to a SNS endpoint.
20
+ */
21
+ publish(options: SNSPublishParams): Promise<AWSSNS.PublishCommandOutput>;
22
+ }
23
+ /**
24
+ * Options for creating a SNS endpoint.
25
+ */
26
+ export interface SNSCreateEndpointParams {
27
+ /**
28
+ * ARN to production of Apple's (iOS, MacOS) notification services.
29
+ */
30
+ appleArn?: string;
31
+ /**
32
+ * ARN to development of Apple's (iOS, MacOS) notification services.
33
+ */
34
+ appleDevArn?: string;
35
+ /**
36
+ * ARN to Android's notification services.
37
+ */
38
+ androidArn?: string;
39
+ }
40
+ /**
41
+ * Options to publish a message on a SNS endpoint.
42
+ */
43
+ export interface SNSPublishParams {
44
+ /**
45
+ * The endpoint of the notification.
46
+ */
47
+ endpoint: string;
48
+ /**
49
+ * The message to send.
50
+ */
51
+ message?: string;
52
+ /**
53
+ * The platform receiver; used to structure the message.
54
+ */
55
+ platform?: PushNotificationsPlatforms;
56
+ /**
57
+ * If set, message and platform will be ignored and the content of this attribute will be preferred.
58
+ */
59
+ json?: any;
60
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SNS = void 0;
27
+ const AWSSNS = __importStar(require("@aws-sdk/client-sns"));
28
+ const idea_toolbox_1 = require("idea-toolbox");
29
+ const lambdaLogger_1 = require("./lambdaLogger");
30
+ /**
31
+ * A wrapper for AWS Simple Notification Service.
32
+ */
33
+ class SNS {
34
+ constructor(options = {}) {
35
+ this.logger = new lambdaLogger_1.LambdaLogger();
36
+ this.client = new AWSSNS.SNSClient({ region: options.region });
37
+ }
38
+ /**
39
+ * Create a new endpoint in the SNS platform specified.
40
+ * @return platform endpoint ARN
41
+ */
42
+ async createPlatormEndpoint(platform, token, options) {
43
+ let platformARN;
44
+ switch (platform) {
45
+ case idea_toolbox_1.PushNotificationsPlatforms.APNS:
46
+ platformARN = options.appleArn;
47
+ break;
48
+ case idea_toolbox_1.PushNotificationsPlatforms.APNS_SANDBOX:
49
+ platformARN = options.appleDevArn;
50
+ break;
51
+ case idea_toolbox_1.PushNotificationsPlatforms.FCM:
52
+ platformARN = options.androidArn;
53
+ break;
54
+ default:
55
+ throw new Error('Unsupported platform');
56
+ }
57
+ this.logger.trace('SNS add platform endpoint');
58
+ const command = new AWSSNS.CreatePlatformEndpointCommand({ PlatformApplicationArn: platformARN, Token: token });
59
+ const { EndpointArn } = await this.client.send(command);
60
+ return EndpointArn;
61
+ }
62
+ /**
63
+ * Publish a message to a SNS endpoint.
64
+ */
65
+ async publish(options) {
66
+ let structuredMessage;
67
+ if (options.json)
68
+ structuredMessage = { default: JSON.stringify(options.json) };
69
+ else
70
+ switch (options.platform) {
71
+ case idea_toolbox_1.PushNotificationsPlatforms.APNS:
72
+ structuredMessage = { APNS: JSON.stringify({ aps: { alert: options.message } }) };
73
+ break;
74
+ case idea_toolbox_1.PushNotificationsPlatforms.APNS_SANDBOX:
75
+ structuredMessage = { APNS_SANDBOX: JSON.stringify({ aps: { alert: options.message } }) };
76
+ break;
77
+ case idea_toolbox_1.PushNotificationsPlatforms.FCM:
78
+ structuredMessage = {
79
+ GCM: JSON.stringify({ notification: { body: options.message, title: options.message } })
80
+ };
81
+ break;
82
+ default:
83
+ throw new Error('Unsupported platform');
84
+ }
85
+ this.logger.trace('SNS publish in topic');
86
+ const command = new AWSSNS.PublishCommand({
87
+ MessageStructure: 'json',
88
+ Message: JSON.stringify(structuredMessage),
89
+ TargetArn: options.endpoint
90
+ });
91
+ return await this.client.send(command);
92
+ }
93
+ }
94
+ exports.SNS = SNS;
@@ -0,0 +1,22 @@
1
+ import * as AWSSystemsManager from '@aws-sdk/client-ssm';
2
+ /**
3
+ * A wrapper for AWS Systems Manager (SSM).
4
+ */
5
+ export declare class SystemsManager {
6
+ protected ssm: AWSSystemsManager.SSMClient;
7
+ protected cache: Map<string, string>;
8
+ constructor();
9
+ /**
10
+ * Get a parameter by its name (path).
11
+ */
12
+ getParameterByName(name: string, options?: {
13
+ noCache?: boolean;
14
+ withDecryption?: boolean;
15
+ }): Promise<string>;
16
+ /**
17
+ * Get a parameter (with decryption) by its name (path).
18
+ */
19
+ getSecretByName(name: string, options?: {
20
+ noCache?: boolean;
21
+ }): Promise<string>;
22
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SystemsManager = void 0;
27
+ const AWSSystemsManager = __importStar(require("@aws-sdk/client-ssm"));
28
+ /**
29
+ * A wrapper for AWS Systems Manager (SSM).
30
+ */
31
+ class SystemsManager {
32
+ constructor() {
33
+ this.cache = new Map();
34
+ this.ssm = new AWSSystemsManager.SSMClient();
35
+ }
36
+ /**
37
+ * Get a parameter by its name (path).
38
+ */
39
+ async getParameterByName(name, options = {}) {
40
+ if (!options.noCache && this.cache.has(name))
41
+ return this.cache.get(name);
42
+ const command = new AWSSystemsManager.GetParameterCommand({ Name: name, WithDecryption: options.withDecryption });
43
+ const { Parameter } = await this.ssm.send(command);
44
+ this.cache.set(name, Parameter.Value);
45
+ return Parameter.Value;
46
+ }
47
+ /**
48
+ * Get a parameter (with decryption) by its name (path).
49
+ */
50
+ async getSecretByName(name, options = {}) {
51
+ return this.getParameterByName(name, { ...options, withDecryption: true });
52
+ }
53
+ }
54
+ exports.SystemsManager = SystemsManager;
@@ -0,0 +1,11 @@
1
+ import { DynamoDBRecord } from 'aws-lambda';
2
+ import { GenericController } from './genericController';
3
+ /**
4
+ * An abstract class to inherit to manage AWS DDB streams in an AWS Lambda function.
5
+ */
6
+ export declare abstract class StreamController extends GenericController {
7
+ records: any[];
8
+ constructor(event: any, callback: any);
9
+ protected abstract handleRecord(record: DynamoDBRecord): Promise<void>;
10
+ handleRequest(): Promise<void>;
11
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamController = void 0;
4
+ const genericController_1 = require("./genericController");
5
+ /**
6
+ * An abstract class to inherit to manage AWS DDB streams in an AWS Lambda function.
7
+ */
8
+ class StreamController extends genericController_1.GenericController {
9
+ constructor(event, callback) {
10
+ super(event, callback);
11
+ this.records = event.Records ?? [];
12
+ }
13
+ async handleRequest() {
14
+ this.logger.info('START', { streamOfRecords: this.records.length ?? 0 });
15
+ await Promise.all(this.records.map(record => this.handleRecord(record)))
16
+ .then(() => this.done())
17
+ .catch((err) => this.done(this.handleControllerError(err, 'STREAM-ERROR', 'Operation failed')));
18
+ }
19
+ }
20
+ exports.StreamController = StreamController;
@@ -0,0 +1,61 @@
1
+ import * as AWSTranslate from '@aws-sdk/client-translate';
2
+ import { Languages, PDFEntity, PDFTemplateSection } from 'idea-toolbox';
3
+ /**
4
+ * A wrapper for Amazon Translate.
5
+ */
6
+ export declare class Translate {
7
+ protected translate: AWSTranslate.TranslateClient;
8
+ /**
9
+ * Default input language code.
10
+ */
11
+ sourceLanguageCode: string;
12
+ /**
13
+ * Default output language code.
14
+ */
15
+ targetLanguageCode: string;
16
+ /**
17
+ * Default terminology list.
18
+ */
19
+ terminologyNames: string[];
20
+ /**
21
+ * Initialize a new Translate helper object.
22
+ */
23
+ constructor(options?: {
24
+ region?: string;
25
+ });
26
+ /**
27
+ * Translates input text from the source language to the target language.
28
+ * @param params the parameters for translateText
29
+ */
30
+ text(params: TranslateParameters): Promise<string>;
31
+ /**
32
+ * Get the contents of a PDF template (against a PDFEntity) translated in the desired language,
33
+ * if the latter isn't between the ones already available.
34
+ * @return an object that maps original texts with their translations (or nothing).
35
+ */
36
+ pdfTemplate(entity: PDFEntity, template: PDFTemplateSection[], language: string, languages: Languages): Promise<Record<string, string>>;
37
+ /**
38
+ * Analyse a PDFTemplate to extract terms to translate based on a PDFEntity (using a sourceLanguage as reference).
39
+ */
40
+ protected analysePDFTemplateForTermsToTranslate(template: PDFTemplateSection[], entity: PDFEntity, sourceLanguage: string): Promise<Set<string>>;
41
+ }
42
+ export interface TranslateParameters {
43
+ /**
44
+ * The text to translate. Required.
45
+ * The text string can be a maximum of 5,000 bytes long; depending on the char set, it may be fewer than 5,000 chars.
46
+ */
47
+ text: string;
48
+ /**
49
+ * The input language.
50
+ */
51
+ sourceLanguageCode?: string;
52
+ /**
53
+ * The output language.
54
+ */
55
+ targetLanguageCode?: string;
56
+ /**
57
+ * The name of the terminology list file to be used in the TranslateText request.
58
+ * Terminology lists can contain a maximum of 256 terms.
59
+ */
60
+ terminologyNames?: string[];
61
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.Translate = void 0;
27
+ const AWSTranslate = __importStar(require("@aws-sdk/client-translate"));
28
+ const idea_toolbox_1 = require("idea-toolbox");
29
+ /**
30
+ * A wrapper for Amazon Translate.
31
+ */
32
+ class Translate {
33
+ /**
34
+ * Initialize a new Translate helper object.
35
+ */
36
+ constructor(options = {}) {
37
+ /**
38
+ * Default input language code.
39
+ */
40
+ this.sourceLanguageCode = 'en';
41
+ /**
42
+ * Default output language code.
43
+ */
44
+ this.targetLanguageCode = 'en';
45
+ /**
46
+ * Default terminology list.
47
+ */
48
+ this.terminologyNames = [];
49
+ this.translate = new AWSTranslate.TranslateClient({ region: options.region });
50
+ }
51
+ /**
52
+ * Translates input text from the source language to the target language.
53
+ * @param params the parameters for translateText
54
+ */
55
+ async text(params) {
56
+ if (params.sourceLanguageCode)
57
+ this.sourceLanguageCode = params.sourceLanguageCode;
58
+ if (params.targetLanguageCode)
59
+ this.targetLanguageCode = params.targetLanguageCode;
60
+ if (params.terminologyNames)
61
+ this.terminologyNames = params.terminologyNames;
62
+ if (!this.sourceLanguageCode || !this.targetLanguageCode || !params.text)
63
+ throw new Error('Bad parameters');
64
+ const command = new AWSTranslate.TranslateTextCommand({
65
+ Text: params.text,
66
+ SourceLanguageCode: this.sourceLanguageCode,
67
+ TargetLanguageCode: this.targetLanguageCode,
68
+ TerminologyNames: this.terminologyNames
69
+ });
70
+ const { TranslatedText } = await this.translate.send(command);
71
+ return TranslatedText;
72
+ }
73
+ /**
74
+ * Get the contents of a PDF template (against a PDFEntity) translated in the desired language,
75
+ * if the latter isn't between the ones already available.
76
+ * @return an object that maps original texts with their translations (or nothing).
77
+ */
78
+ async pdfTemplate(entity, template, language, languages) {
79
+ // if the language is included in the ones supported by the team, skip
80
+ if (languages.available.some(l => l === language))
81
+ return null;
82
+ // analyse the template to extract terms to translate based on the entity (using a sourceLanguage as reference)
83
+ const termsToTranslate = Array.from(await this.analysePDFTemplateForTermsToTranslate(template, entity, languages.default));
84
+ const translations = {};
85
+ for (let i = 0; i < termsToTranslate.length; i++) {
86
+ const original = termsToTranslate[i];
87
+ const translated = await this.text({
88
+ sourceLanguageCode: languages.default,
89
+ targetLanguageCode: language,
90
+ text: original
91
+ });
92
+ translations[original] = translated
93
+ // fix markdown issue (the translations add a space before and after asterisks)
94
+ .replace(/\*\* /gm, '**')
95
+ .replace(/ \*\*/gm, '**');
96
+ }
97
+ return translations;
98
+ }
99
+ /**
100
+ * Analyse a PDFTemplate to extract terms to translate based on a PDFEntity (using a sourceLanguage as reference).
101
+ */
102
+ async analysePDFTemplateForTermsToTranslate(template, entity, sourceLanguage) {
103
+ const toTranslate = new Set();
104
+ // gather the terms to translate from contents available on this level
105
+ template
106
+ .filter(s => s.isEither(idea_toolbox_1.PDFTemplateSectionTypes.ROW, idea_toolbox_1.PDFTemplateSectionTypes.HEADER))
107
+ .forEach(s => {
108
+ switch (s.type) {
109
+ case idea_toolbox_1.PDFTemplateSectionTypes.ROW:
110
+ s.columns
111
+ .filter((_, index) => s.doesColumnContainAField(index))
112
+ .forEach(field => {
113
+ field = field;
114
+ if (field.isComplex()) {
115
+ const complex = field;
116
+ toTranslate.add(complex.content[sourceLanguage]);
117
+ }
118
+ else {
119
+ const simple = field;
120
+ toTranslate.add(simple.label[sourceLanguage]);
121
+ // try to consider only notes (long fields)
122
+ if (typeof entity[simple.code] === 'string' && entity[simple.code].length > 50)
123
+ toTranslate.add(entity[simple.code]);
124
+ }
125
+ });
126
+ break;
127
+ case idea_toolbox_1.PDFTemplateSectionTypes.HEADER:
128
+ toTranslate.add(s.title[sourceLanguage]);
129
+ break;
130
+ }
131
+ });
132
+ // gather inner sections in a flat structure for further elaboraton
133
+ const innerSections = new Array();
134
+ template
135
+ .filter(s => s.isEither(idea_toolbox_1.PDFTemplateSectionTypes.INNER_SECTION, idea_toolbox_1.PDFTemplateSectionTypes.REPEATED_INNER_SECTION))
136
+ .forEach(s => {
137
+ switch (s.type) {
138
+ case idea_toolbox_1.PDFTemplateSectionTypes.INNER_SECTION:
139
+ innerSections.push({ data: entity[s.context], template: s.innerTemplate });
140
+ break;
141
+ case idea_toolbox_1.PDFTemplateSectionTypes.REPEATED_INNER_SECTION:
142
+ entity[s.context].forEach((element) => innerSections.push({ data: element, template: s.innerTemplate }));
143
+ break;
144
+ }
145
+ });
146
+ // run (inception) the inner sections to gather terms to translate from inner levels
147
+ for (let i = 0; i < innerSections.length; i++) {
148
+ const s = innerSections[i];
149
+ const res = await this.analysePDFTemplateForTermsToTranslate(s.template, s.data, sourceLanguage);
150
+ res.forEach(x => toTranslate.add(x));
151
+ }
152
+ return toTranslate;
153
+ }
154
+ }
155
+ exports.Translate = Translate;