@enplug/scripts 1.11.4-dev9 → 1.11.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.
@@ -1,7 +1,6 @@
1
1
  const axios = require('axios');
2
2
  const chalk = require('chalk');
3
3
  const fs = require('fs');
4
- const FormData = require('form-data');
5
4
  const inquirer = require('inquirer');
6
5
  const loadDevPrivateFile = require('../loadDevPrivateFile');
7
6
  const rootPath = __dirname.split('node_modules')[0];
@@ -13,62 +12,9 @@ const { catchError, switchMap, tap, filter } = require('rxjs/operators');
13
12
  const HTTP_UNAUTHORIZED_STATUS_CODE = 401;
14
13
  const HTTP_NOT_FOUND_STATUS_CODE = 404;
15
14
 
16
- // const CROWDIN_PROJECT_ID = 'enplug';
17
15
  const CROWDIN_PROJECT_ID = '401630';
18
- // const CROWDIN_PROJECT_URL = `https://api.crowdin.com/api/project/${CROWDIN_PROJECT_ID}`;
19
16
  const CROWDIN_PROJECT_URL = `https://api.crowdin.com/api/v2/projects/${CROWDIN_PROJECT_ID}`;
20
17
  const CROWDIN_STORAGE_URL = `https://api.crowdin.com/api/v2/storages`;
21
- const CROWDIN_DIRECTORY_NOT_FOUND_ERROR_CODE = 17;
22
-
23
- async function uploadFileToCrowdin(credentials, crowdinPath, localPath) {
24
- if (!credentials || !credentials.login || !credentials.accountKey) {
25
- console.error(`${chalk.red.bold('Crowdin credentials not provided')}`);
26
- console.log(`Make sure that the ${chalk.default.yellow('dev.private.json')} file contains ${chalk.default.yellow('crowdinCredentials: { login, accountKey }')}`);
27
- throw new Error('Crowdin credentials not provided');
28
- }
29
-
30
- if (!fs.existsSync(localPath)) {
31
- console.error(`${chalk.red.bold('Local file does not exists')} ${chalk.yellow.bold(`[${localPath}]`)}`);
32
- throw new Error('Local file does not exist');
33
- }
34
-
35
- return postFileToCrowdin('update', credentials, crowdinPath, localPath).pipe(
36
- catchError(error => {
37
- if (error.response.status === HTTP_NOT_FOUND_STATUS_CODE) {
38
- return promptAddFile(crowdinPath).pipe(
39
- filter(({addFileConfirm}) => addFileConfirm === true),
40
- switchMap(() => postFileToCrowdin('add', credentials, crowdinPath, localPath))
41
- );
42
- }
43
-
44
- return throwError(error);
45
- }),
46
- tap({
47
- next: response => {
48
- if (response.data.success) {
49
- console.log(`${chalk.green.bold('Translations uploaded to Crowdin')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
50
- } else {
51
- console.error('Unexpected result');
52
- console.log(response);
53
- }
54
- },
55
- error: error => {
56
- const crowdinError = error.response && error.response.data && error.response.data.error;
57
-
58
- if (crowdinError && crowdinError.code === CROWDIN_DIRECTORY_NOT_FOUND_ERROR_CODE) {
59
- console.error(`\n${chalk.red.bold('Directory does not exist')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
60
- console.log('Create the directory in the Crowdin panel first.');
61
- } else if (error.response.status === HTTP_UNAUTHORIZED_STATUS_CODE) {
62
- console.error(`\n${chalk.red.bold('Provided Crowdin credentials are incorrect')}`);
63
- console.log(`Check the ${chalk.default.yellow('dev.private.json')} file.`);
64
- } else {
65
- console.error('\nUnexpected error:');
66
- console.error(error);
67
- }
68
- }
69
- })
70
- ).toPromise();
71
- }
72
18
 
73
19
  function getCrowdinCredentials() {
74
20
  try {
@@ -108,44 +54,27 @@ function getCrowdinConfig() {
108
54
  return pkg.config.crowdin;
109
55
  }
110
56
 
111
- function generateFormData(crowdinPath, localPath) {
112
- const formData = new FormData();
113
-
114
- // formData.append('update_option', 'update_without_changes'); // Previous translations should remain valid
115
- formData.append('json', '');
116
- formData.append(`files[${crowdinPath}]`, fs.createReadStream(localPath));
117
- formData.append(`export_patterns[${crowdinPath}]`, '%locale%.json');
118
- return formData;
119
- }
120
-
121
- function postFileToCrowdin(operation, credentials, crowdinPath, localPath) {
122
- const AuthStr = 'Bearer '.concat(credentials.token);
123
- const formData = generateFormData(crowdinPath, localPath);
124
-
125
- const url = `${CROWDIN_STORAGE_URL}`;
126
-
127
- return from(axios.post(url, formData, { headers: {
128
- "Authorization": AuthStr,
129
- "Content-Type": "application/json",
130
- "Crowdin-API-FileName": "en.json"
131
- } }));
132
- }
133
-
134
- async function findCrowdinAppDirectoryId(credentials, crowdinPath) {
135
- path = crowdinPath.startsWith('/') ? crowdinPath.substr(1) : crowdinPath;
136
- if(path.startsWith('apps')){
137
- const appName = path.split('/')[1];
138
- const url = `${CROWDIN_PROJECT_URL}/directories?filter=${appName}`;
139
- const AuthStr = 'Bearer '.concat(credentials.token);
140
- const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
141
- const directory = resp.data.data.find(d => d.data.name === appName );
142
- return directory.data.id
143
- } else if(path.startsWith('dashboard')) {
144
- const url = `${CROWDIN_PROJECT_URL}/directories?filter=dashboard`;
145
- const AuthStr = 'Bearer '.concat(credentials.token);
146
- const resp = axios.get(url, { headers: { Authorization: AuthStr } });
147
- const directory = resp.data.data.find(d => d.data.name === 'dashboard' && d.data.title === 'Dashboard' && d.data.path === '/dashboard');
148
- return directory.data.id;
57
+ async function findCrowdinAppDirectoryId(credentials, crowdinPath, appName) {
58
+ try {
59
+ if(crowdinPath.startsWith('apps') || appName !== 'en.json') { // path is apps/{app-name}/... or dashboard/{data-connector or regions-map or uploader}/en.json
60
+ const url = `${CROWDIN_PROJECT_URL}/directories?filter=${appName}`;
61
+ const AuthStr = 'Bearer '.concat(credentials.token);
62
+ const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
63
+ const directory = resp.data.data.find(d => d.data.name === appName );
64
+ return directory ? directory.data.id : undefined;
65
+ } else if((crowdinPath.startsWith('dashboard') || crowdinPath.startsWith('player-web') || crowdinPath.startsWith('components')) && appName === 'en.json') { //path is dashboard/en.json or player-web/en.json or components/en.json
66
+ const url = `${CROWDIN_PROJECT_URL}/directories?filter=dashboard`;
67
+ const AuthStr = 'Bearer '.concat(credentials.token);
68
+ const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
69
+ const directory = resp.data.data.find(d => d.data.name === 'dashboard' && d.data.title === 'Dashboard' && d.data.path === '/dashboard');
70
+ return directory ? directory.data.id : undefined;
71
+ }
72
+ } catch (err) {
73
+ if (err.response.status === HTTP_UNAUTHORIZED_STATUS_CODE) {
74
+ console.error(`\n${chalk.red.bold('Provided Crowdin token is invalid')}`);
75
+ console.log(`Check the ${chalk.default.yellow('dev.private.json')} file.`);
76
+ return throwError(err);
77
+ }
149
78
  }
150
79
  }
151
80
 
@@ -153,64 +82,44 @@ async function findCrowdinAppSubFolderId(credentials, directoryId, subfolderName
153
82
  const url = `${CROWDIN_PROJECT_URL}/directories?directoryId=${directoryId}`;
154
83
  const AuthStr = 'Bearer '.concat(credentials.token);
155
84
  const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
156
- const subdirectory = resp.data.data.find(d => d.data.name === subfolderName)
157
- return subdirectory.data.id;
85
+ const subdirectory = resp.data.data.find(d => d.data.name === subfolderName);
86
+ return subdirectory ? subdirectory.data.id : undefined;
158
87
  }
159
88
 
160
89
  async function getFileIdIfExists(credentials, directoryId, fileName) {
161
90
  const url = `${CROWDIN_PROJECT_URL}/files?directoryId=${directoryId}`;
162
91
  const AuthStr = 'Bearer '.concat(credentials.token);
163
92
  const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
164
- const file = resp.data.find(d => d.data.name === fileName);
165
- console.log('file found', file);
166
- return file.data.id;
93
+ const file = resp.data.data.find(d => d.data.name === fileName);
94
+ return file ? file.data.id : undefined;
167
95
  }
168
96
 
169
- async function uploadFileToCrowdinStorage(credentials, crowdinPath, localPath) {
97
+ function postFileToCrowdin(credentials, localPath) {
98
+ const AuthStr = 'Bearer '.concat(credentials.token);
99
+ const file = fs.readFileSync(localPath);
100
+ const url = `${CROWDIN_STORAGE_URL}`;
101
+
102
+ return from(axios.post(url, file, { headers: {
103
+ "Authorization": AuthStr,
104
+ "Content-Type": 'application/json',
105
+ "Crowdin-API-FileName": 'en.json'
106
+ } }));
107
+ }
108
+
109
+ async function uploadFileToCrowdinStorage(credentials, localPath) {
170
110
  if (!fs.existsSync(localPath)) {
171
111
  console.error(`${chalk.red.bold('Local file does not exists')} ${chalk.yellow.bold(`[${localPath}]`)}`);
172
112
  throw new Error('Local file does not exist');
173
113
  }
174
-
175
- return postFileToCrowdin('update', credentials, crowdinPath, localPath).pipe(
114
+ return postFileToCrowdin(credentials, localPath).pipe(
176
115
  catchError(error => {
177
- if (error.response.status === HTTP_NOT_FOUND_STATUS_CODE) {
178
- return promptAddFile(crowdinPath).pipe(
179
- filter(({addFileConfirm}) => addFileConfirm === true),
180
- switchMap(() => postFileToCrowdin('add', credentials, crowdinPath, localPath))
181
- );
182
- }
183
-
116
+ console('ERROR could not upload file to crowdin storage')
184
117
  return throwError(error);
185
118
  }),
186
- tap({
187
- next: response => {
188
- if (response.data) {
189
- console.log(`${chalk.green.bold('Translations uploaded to Crowdin')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
190
- } else {
191
- console.error('Unexpected result');
192
- console.log(response);
193
- }
194
- },
195
- error: error => {
196
- const crowdinError = error.response && error.response.data && error.response.data.error;
197
-
198
- if (crowdinError && crowdinError.code === CROWDIN_DIRECTORY_NOT_FOUND_ERROR_CODE) {
199
- console.error(`\n${chalk.red.bold('Directory does not exist')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
200
- console.log('Create the directory in the Crowdin panel first.');
201
- } else if (error.response.status === HTTP_UNAUTHORIZED_STATUS_CODE) {
202
- console.error(`\n${chalk.red.bold('Provided Crowdin credentials are incorrect')}`);
203
- console.log(`Check the ${chalk.default.yellow('dev.private.json')} file.`);
204
- } else {
205
- console.error('\nUnexpected error:');
206
- console.error(error);
207
- }
208
- }
209
- })
210
119
  ).toPromise();
211
120
  }
212
121
 
213
- function updateCrowdinFile(credentials, storageId, fileId) {
122
+ async function updateCrowdinFile(credentials, crowdinPath, storageId, fileId) {
214
123
  const url = `${CROWDIN_PROJECT_URL}/files/${fileId}`;
215
124
  const AuthStr = 'Bearer '.concat(credentials.token);
216
125
  const body = {
@@ -225,19 +134,69 @@ function updateCrowdinFile(credentials, storageId, fileId) {
225
134
  },
226
135
  "replaceModifiedContext": false
227
136
  }
228
- return axios.put(url, body, { headers: { Authorization: AuthStr } });
137
+ return from(axios.put(url, body, { headers: { Authorization: AuthStr } })).pipe(tap({
138
+ next: response => {
139
+ if (response.data) {
140
+ console.log(`${chalk.green.bold('Translations uploaded to Crowdin')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
141
+ } else {
142
+ console.error('Unexpected result');
143
+ console.log(response);
144
+ }
145
+ },
146
+ error: error => {
147
+ console.error('\nUnexpected error:');
148
+ console.error(error);
149
+ }
150
+ })).toPromise();
151
+ }
152
+
153
+ async function addCrowdinFile(credentials, crowdinPath, storageId, folderId) {
154
+ const url = `${CROWDIN_PROJECT_URL}/files`;
155
+ const AuthStr = 'Bearer '.concat(credentials.token);
156
+ const body = {
157
+ "storageId": storageId,
158
+ "name": "en.json",
159
+ "directoryId": folderId,
160
+ "context": null,
161
+ "type": "json",
162
+ "parserVersion": 1,
163
+ "importOptions": {
164
+ "contentSegmentation": false,
165
+ "customSegmentation": false
166
+ },
167
+ "exportOptions": {
168
+ "exportPattern": "%locale%.json"
169
+ },
170
+ "excludedTargetLanguages": null
171
+ }
172
+ return from(axios.post(url, body, { headers: { Authorization: AuthStr } })).pipe(tap({
173
+ next: response => {
174
+ if (response.data) {
175
+ console.log(`${chalk.green.bold('Translations uploaded to Crowdin')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
176
+ } else {
177
+ console.error('Unexpected result');
178
+ console.log(response);
179
+ }
180
+ },
181
+ error: error => {
182
+ console.error('\nUnexpected error:');
183
+ console.error(error);
184
+ }
185
+ })).toPromise();
229
186
  }
230
187
 
231
- function fetchFileFromCrowdin(credentials, crowdinPath, language) {
232
- const url = `${CROWDIN_PROJECT_URL}/export-file`;
233
- const params = {
234
- 'login': credentials.login,
235
- 'account-key': credentials.accountKey,
236
- 'file': crowdinPath,
237
- 'language': language
188
+ function fetchFileFromCrowdin(credentials, language, fileId) {
189
+ const url = `${CROWDIN_PROJECT_URL}/translations/exports`;
190
+ const AuthStr = 'Bearer '.concat(credentials.token);
191
+ const body = {
192
+ "targetLanguageId": language,
193
+ "fileIds": [fileId]
238
194
  };
195
+ return axios.post(url, body, { headers: { Authorization: AuthStr } });
196
+ }
239
197
 
240
- return axios.get(url, { params });
198
+ function readFakeTranslationsFile(url) {
199
+ return axios.get(url);
241
200
  }
242
201
 
243
202
  function promptAddFile(crowdinPath) {
@@ -250,4 +209,4 @@ function promptAddFile(crowdinPath) {
250
209
  }));
251
210
  }
252
211
 
253
- module.exports = { getCrowdinCredentials, getCrowdinConfig, findCrowdinAppDirectoryId, findCrowdinAppSubFolderId, getFileIdIfExists, uploadFileToCrowdinStorage, updateCrowdinFile, uploadFileToCrowdin, fetchFileFromCrowdin };
212
+ module.exports = { getCrowdinCredentials, getCrowdinConfig, findCrowdinAppDirectoryId, findCrowdinAppSubFolderId, getFileIdIfExists, uploadFileToCrowdinStorage, updateCrowdinFile, addCrowdinFile, fetchFileFromCrowdin, readFakeTranslationsFile };
@@ -5,7 +5,7 @@ const inquirer = require('inquirer');
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
7
 
8
- const { getCrowdinCredentials, getCrowdinConfig, findCrowdinAppDirectoryId, findCrowdinAppSubFolderId, getFileIdIfExists, uploadFileToCrowdinStorage, updateCrowdinFile, uploadFileToCrowdin, fetchFileFromCrowdin } = require('./crowdin');
8
+ const { getCrowdinCredentials, getCrowdinConfig, findCrowdinAppDirectoryId, findCrowdinAppSubFolderId, getFileIdIfExists, uploadFileToCrowdinStorage, updateCrowdinFile, addCrowdinFile, fetchFileFromCrowdin, readFakeTranslationsFile } = require('./crowdin');
9
9
  const { checkKeys, validateTranslationFile } = require('./transloco');
10
10
  const { uploadTranslationToS3 } = require('./translation-s3');
11
11
  const getPackageJson = require('../getPackageJson');
@@ -61,34 +61,77 @@ async function syncTranslations(s3Client, bucket) {
61
61
  await uploadTranslationToS3(s3Client, bucket, s3EnPath, enTranslation);
62
62
 
63
63
  if (isProduction) {
64
- const appDirectoryId = await findCrowdinAppDirectoryId(credentials, config.crowdinPath);
65
- console.log('found directoryId', appDirectoryId);
66
- if(config.crowdinPath.includes('apps')) {
67
- const path = config.crowdinPath.startsWith('/') ? config.crowdinPath.substr(1) : config.crowdinPath;
68
- const subfolderName = path.split('/')[2];
64
+ const crowdinPath = config.crowdinPath.startsWith('/') ? config.crowdinPath.substr(1) : config.crowdinPath;
65
+ const crowdinPathSections = crowdinPath.split('/');
66
+ const appName = crowdinPathSections[1];
67
+ const appDirectoryId = await findCrowdinAppDirectoryId(credentials, crowdinPath, appName);
68
+ if(appDirectoryId === undefined) {
69
+ console.error(`\n${chalk.red.bold('Directory does not exist')} ${chalk.yellow.bold(`${appName}`)}`);
70
+ console.log('Create the directory in the Crowdin panel first.');
71
+ return;
72
+ }
73
+ if(crowdinPathSections[0] === 'apps' && appDirectoryId) {
74
+ if(crowdinPathSections.length !== 4 && crowdinPathSections[3] !== 'en.json') {
75
+ console.error(`\n${chalk.red.bold('Crowdin path provided does not match defined format, make sure the path is as follows /apps/{app-id}/dashboard/en.json or /apps/{app-id}/app/en.json')}`);
76
+ return;
77
+ }
78
+ const subfolderName = crowdinPathSections[2]; //dashboard or app folder
69
79
  const folderId = await findCrowdinAppSubFolderId(credentials, appDirectoryId, subfolderName);
70
- console.log('found folderId', folderId);
71
- const fileId = await getFileIdIfExists(credentials, appDirectoryId, path.split('/')[3]);
72
- console.log('found fileId', fileId);
73
- const storageId = await uploadFileToCrowdinStorage(credentials, config.crowdinPath, config.localPath)
80
+ if(folderId === undefined) {
81
+ console.error(`Could not find ${chalk.yellow.bold(`[${subfolderName}]`)} folder`);
82
+ }
83
+ const fileId = await getFileIdIfExists(credentials, folderId, crowdinPathSections[3]); // en.json file id to update
74
84
  if(fileId) {
75
- await updateCrowdinFile(credentials, storageId, fileId);
85
+ const storageId = await uploadFileToCrowdinStorage(credentials, config.localPath);
86
+ if(fileId && storageId) {
87
+ await updateCrowdinFile(credentials, crowdinPath, storageId.data.data.id, fileId);
88
+ await updateFrakeTranslations(credentials, fileId, s3EnPath, s3Client, bucket);
89
+ }
90
+ } else {
91
+ const storageId = await uploadFileToCrowdinStorage(credentials, config.localPath);
92
+ if(storageId) {
93
+ const result = await addCrowdinFile(credentials, crowdinPath, storageId.data.data.id, folderId);
94
+ await updateFrakeTranslations(credentials, result.data.data.id, s3EnPath, s3Client, bucket);
95
+ }
96
+ }
97
+ } else if((crowdinPathSections[0] === 'dashboard' || crowdinPathSections[0] === 'player-web' || crowdinPathSections[0] === 'components') && appDirectoryId) {
98
+ if(crowdinPathSections[1] === 'en.json') {
99
+ const fileId = await getFileIdIfExists(credentials, appDirectoryId, crowdinPathSections[1]); // en.json file id to update
100
+ if(fileId) {
101
+ const storageId = await uploadFileToCrowdinStorage(credentials, config.localPath);
102
+ if(fileId && storageId) {
103
+ await updateCrowdinFile(credentials, crowdinPath, storageId.data.data.id, fileId);
104
+ await updateFrakeTranslations(credentials, fileId, s3EnPath, s3Client, bucket);
105
+ }
106
+ } else {
107
+ console.error(`Could not upload ${chalk.yellow.bold(`${rowdinPathSections[1]}`)}. Only en.json supported.`);
108
+ }
109
+ } else { // data-connector, regions-map and uploader in dashboard folder
110
+ const folderId = await findCrowdinAppDirectoryId(credentials, crowdinPath, crowdinPathSections[1]);
111
+ if(folderId === undefined) {
112
+ console.error(`Could not find ${chalk.yellow.bold(`${folderId}`)} folder`);
113
+ }
114
+ const fileId = await getFileIdIfExists(credentials, folderId, crowdinPathSections[2]); // en.json file id to update
115
+ const storageId = await uploadFileToCrowdinStorage(credentials, config.localPath);
116
+ if(fileId && storageId) {
117
+ await updateCrowdinFile(credentials, crowdinPath, storageId.data.data.id, fileId);
118
+ await updateFrakeTranslations(credentials, fileId, s3EnPath, s3Client, bucket);
119
+ }
76
120
  }
77
- } else {
78
- // TODO Update dashboard transtlations file
79
121
  }
80
- // await uploadFileToCrowdin(credentials, config.crowdinPath, config.localPath);
81
- // const { data: fakeTranslation } = await fetchFileFromCrowdin(credentials, config.crowdinPath, FAKE_IN_CONTEXT_LANGUAGE);
82
-
83
- // const s3TranslationsPath = path.parse(s3EnPath).dir;
84
- // const s3FakeTranslationPath = path.join(s3TranslationsPath, FAKE_IN_CONTEXT_LANGUAGE_FILE);
85
-
86
- // await uploadTranslationToS3(s3Client, bucket, s3FakeTranslationPath, JSON.stringify(fakeTranslation));
87
122
  }
88
123
  }
89
124
  }
90
125
  }
91
126
 
127
+ async function updateFrakeTranslations(credentials, fileId, s3EnPath, s3Client, bucket) {
128
+ const { data: fakeTranslationUrl } = await fetchFileFromCrowdin(credentials, FAKE_IN_CONTEXT_LANGUAGE, fileId);
129
+ const s3TranslationsPath = path.parse(s3EnPath).dir;
130
+ const s3FakeTranslationPath = path.join(s3TranslationsPath, FAKE_IN_CONTEXT_LANGUAGE_FILE);
131
+ const fakeTranslation = await readFakeTranslationsFile(fakeTranslationUrl.data.url);
132
+ await uploadTranslationToS3(s3Client, bucket, s3FakeTranslationPath, JSON.stringify(fakeTranslation.data));
133
+ }
134
+
92
135
  async function promptForceContinue() {
93
136
  return inquirer.prompt({
94
137
  type: 'confirm',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enplug/scripts",
3
- "version": "1.11.4-dev9",
3
+ "version": "1.11.5",
4
4
  "description": "Enplug scripts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",