Есть такой код , написан не мной он занимается тем что отправляет на sendgrid.com/dynamic-templates шаблоны станиц , проблема заключается в том что раньше было. 2 языка английский и немецкий и мы добавили еще 9 языков . и когда происходит deploy сервер от sendgrid принимает где то 100 запросов а потом отправляет ошибку Error: getaddrinfo ENOTFOUND api.sendgrid.com api.sendgrid.com:443 , если отправлять опять маленькое количество шаблонов например опять 2 языка , то ошибки нету , я пришел к такому выводу , что этот код отправляет моментально много запросов , а sendgrid просто видит что ему пытаются отправить очень много запросов за один раз и просто возвращает ошибку . Проблема в том что с таким я не работал , и я не знаю как мне сделать промежуточные паузы между запросами , например если выгружены все шаблоны он будет их обновлять updateTemplateVersion() и если их там 200 а сервер после 100 отправит ошибку , как мне в async/await сделать промежуточную паузу хотя бы между каждым запросов или через например каждые 50 на несколько секунд . если вы взгляните на код и сможете мне помочь буду очень благодарен !!!!
const api = axios.create({
baseURL: "https://api.sendgrid.com/v3/",
headers: {
"Authorization": "Bearer " + token,
},
});
// Loads the subjects from the given json translation file that looks like
// {
// "some-name-subject": "foo",
// "something-else": "bar",
// ...
// }
// and returns the subjects in the form
// {
// "some-name": "foo",
// ...
// }
// without the "-subject" suffix and without non-subject translation keys.
async function loadSubjectsFromFile(path) {
// parse translation file
let translations = JSON.parse(fs.readFileSync(path))
// find subject translation keys
var onlySubjects = {};
for (const key in translations) {
if (!translations.hasOwnProperty(key)) continue;
// check for "-subject" suffix
const match = key.match(/^([-_a-zA-Z0-9]+)-subject$/);
if (match === null) continue;
// use name without the "-subject" suffix
onlySubjects[match[1]] = translations[key];
}
return onlySubjects;
}
// Loads all subjects from the translation files in the ./lang directory and
// returns them by language in the form
// {
// "en": {
// "some-name": "some-name subject translation string",
// ...
// },
// ...
// }
// .
async function loadAllSubjects() {
// find translation files
let files = await glob("./lang/*/index.json");
// extract language from filename
files = files.map(path => path.match(/\/([a-z]{2})\/index\.json$/))
.filter(match => match !== null)
.map(({ 1: language, input: path }) => ({ language, path }));
// load subjects from file
const addSubjects = file => loadSubjectsFromFile(file.path)
.then(subjects => ({ ...file, subjects }));
files = await Promise.all(files.map(addSubjects));
// transform output
return files.reduce((prev, file) => ({ ...prev, [file.language]: file.subjects }), {});
}
// Loads all templates from the ./dist directory and returns them in the form
// [
// {
// "project": "some-project",
// "name": "some-name",
// "language": "en",
// "content": "...",
// },
// ...
// ]
// .
async function loadAllTemplates() {
// find template files
let files = await glob("./dist/*/*.html");
// extract project, name and language from filename
files = files.map(path => path.match(/\/([-_a-zA-Z0-9]+)\/([-_a-zA-Z0-9]+)-([a-z]{2})\.html$/))
.filter(match => match !== null)
.map(({ 1: project, 2: name, 3: language, input: path }) => ({ project, name, language, path }));
// load files
const addContent = file => fs.promises.readFile(file.path)
.then(content => ({ ...file, content: content.toString() }));
files = await Promise.all(files.map(addContent));
// replace placeholders
files = files.map(f => ({...f, content: f.content
.replace("PACE_ID_LOGO_DARK", paceIdLogoDarkUrl)
.replace("PACE_PAY_LOGO_DARK", pacePayLogoDarkUrl)
.replace("COFU_LOGO_DARK", cofuLogoDarkUrl)
.replace("PACE_ID_LOGO", paceIdLogoUrl)
.replace("PACE_PAY_LOGO", pacePayLogoUrl)
.replace("COFU_LOGO", cofuLogoUrl)
.replace(/FEEDBACK_BULLET/g, bulletIconUrl)
.replace(/MADE_WITH_LOVE_DARK/g, madeWithLoveDarkUrl)
.replace(/MADE_WITH_LOVE/g, madeWithLoveUrl)
.replace(/SIGN_PHIL_DARK/g, signPhilDark)
.replace(/SIGN_PHIL/g, signPhil)
.replace(/PHIL_PICTURE/g, picturePhil)
}));
// transform output
return files.map(({ project, name, language, content }) => ({ project, name, language, content }));
}
// Loads all templates from the ./dist directory and subjects from the ./lang
// directory and returns them in the form
// [
// {
// "name": "some-project.en.some-name",
// "content": "...",
// "subject": "some-name subject translation string",
// },
// ...
// ]
// .
async function loadTemplatesWithSubjects() {
const subjects = await loadAllSubjects();
const templates = await loadAllTemplates();
const mustFindSubject = (language, name) => {
const subject = subjects[language][name];
if (!subject) throw `no subject found for "*.${language}.${name}"`;
return subject
}
// combine templates and subjects
return templates.map(({ project, name, language, content }) => ({
name: `${project}.${language}.${name}`,
content,
subject: mustFindSubject(language, name),
}))
}
// Loads all templates from remote and returns them in the form
// [
// {
// "name": "some-project.en.some-name",
// "id": "d-ebe8ecc4afc6413f9c24bef2abfca73b",
// "version": "8ab2d9bd-d60b-40b9-8228-e9042e6820a1",
// },
// ...
// ]
// .
async function loadRemoteTemplates() {
const response = await api.get("/templates?page_size=100&generations=dynamic");
// filter templates by name "some-project.language.some-name"
const templates = response.data.result.filter(t => t.name.match(/^[-_a-zA-Z0-9]+\.[a-z]{2}\.[-_a-zA-Z0-9]+$/) !== null);
// transform output
return templates.map(({ id, name, versions }) => ({
name,
id,
version: (v => v ? v.id : undefined)(versions.find(v => v.active)),
}))
}
// Loads all templates from the filesystem and remote and returns them in the
// form
// [
// {
// "name": "some-project.en.some-name",
// "content": "...",
// "subject": "some-name subject translation string",
// "id": "d-ebe8ecc4afc6413f9c24bef2abfca73b",
// "version": "8ab2d9bd-d60b-40b9-8228-e9042e6820a1",
// },
// ...
// ]
// . Note that per template most properties can be undefined. At minimum a
// template has the "name" and either the "content" or "id" property given.
async function matchLocalAndRemoteTemplates() {
const local = await loadTemplatesWithSubjects();
const remote = await loadRemoteTemplates();
// combine local and remote
return local.concat(remote).reduce((prev, template) => {
const existing = prev.find(t => t.name === template.name);
return existing
? [ // consolidate
...prev.filter(t => t.name !== existing.name),
{ ...existing, ...template },
]
: [...prev, template]; // add
}, []);
}
async function updateTemplateVersion(template) {
console.log("updating template version", template.name)
const resp = await api.patch(`/templates/${template.id}/versions/${template.version}`, {
active: 1,
subject: template.subject,
html_content: template.content,
name: versionName,
})
if (resp.status !== 200) throw `failed to update template ${template.name}: ${resp.status}: ${resp.data}`;
return template;
}
async function createTemplate(template) {
console.log("creating template", template.name)
const resp = await api.post(`/templates`, {
name: template.name,
generation: "dynamic",
})
if (resp.status !== 201) throw `failed to create template ${template.name}: ${resp.status}: ${resp.data}`;
template.id = resp.data.id
return template;
}
async function createTemplateVersion(template) {
console.log("creating template version", template.name)
const resp = await api.post(`/templates/${template.id}/versions`, {
active: 1,
subject: template.subject,
html_content: template.content,
name: versionName,
})
if (resp.status !== 201) throw `failed to create template version ${template.name}: ${resp.status}: ${resp.data}`;
template.version = resp.data.id
return template;
}
// update remote
matchLocalAndRemoteTemplates()
// safeguard: ignore all templates of projects we don't know
.then(templates => templates.map(t => ({...t, project: t.name.split('.')[0]})))
.then(templates => templates.filter(t => supportedProjects.includes(t.project)))
// update template versions that exist remotely and locally
.then(templates => Promise.all(templates.map(t => t.version && t.content ? updateTemplateVersion(t) : t)))
// create templates that don't exist remotely, but locally
.then(templates => Promise.all(templates.map(t => !t.id && t.content ? createTemplate(t) : t)))
// create template versions that don't exist remotely, but locally
.then(templates => Promise.all(templates.map(t => !t.version && t.content ? createTemplateVersion(t) : t)))
// delete templates that exist remotely, but not locally (disabled because of partner-mails)
//.then(templates => Promise.all(templates.map(t => t.id && !t.content ? deleteTemplate(t) : t)))
// report all errors
.catch(err => {
console.error(err);
process.exit(1);
});