@enplug/scripts 1.11.4-dev7 → 1.11.4-dev70

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,6 +1,7 @@
1
1
  const axios = require('axios');
2
2
  const chalk = require('chalk');
3
3
  const fs = require('fs');
4
+ const path = require('path');
4
5
  const FormData = require('form-data');
5
6
  const inquirer = require('inquirer');
6
7
  const loadDevPrivateFile = require('../loadDevPrivateFile');
@@ -13,62 +14,9 @@ const { catchError, switchMap, tap, filter } = require('rxjs/operators');
13
14
  const HTTP_UNAUTHORIZED_STATUS_CODE = 401;
14
15
  const HTTP_NOT_FOUND_STATUS_CODE = 404;
15
16
 
16
- // const CROWDIN_PROJECT_ID = 'enplug';
17
17
  const CROWDIN_PROJECT_ID = '401630';
18
- // const CROWDIN_PROJECT_URL = `https://api.crowdin.com/api/project/${CROWDIN_PROJECT_ID}`;
19
18
  const CROWDIN_PROJECT_URL = `https://api.crowdin.com/api/v2/projects/${CROWDIN_PROJECT_ID}`;
20
19
  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
20
 
73
21
  function getCrowdinCredentials() {
74
22
  try {
@@ -108,108 +56,72 @@ function getCrowdinConfig() {
108
56
  return pkg.config.crowdin;
109
57
  }
110
58
 
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;
59
+ async function findCrowdinAppDirectoryId(credentials, crowdinPath, appName) {
60
+ try {
61
+ if(crowdinPath.startsWith('apps') || appName !== 'en.json') { // path is apps/{app-name}/... or dashboard/{data-connector or regions-map or uploader}/en.json
62
+ const url = `${CROWDIN_PROJECT_URL}/directories?filter=${appName}`;
63
+ const AuthStr = 'Bearer '.concat(credentials.token);
64
+ const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
65
+ const directory = resp.data.data.find(d => d.data.name === appName );
66
+ return directory ? directory.data.id : undefined;
67
+ } 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
68
+ const url = `${CROWDIN_PROJECT_URL}/directories?filter=dashboard`;
69
+ const AuthStr = 'Bearer '.concat(credentials.token);
70
+ const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
71
+ const directory = resp.data.data.find(d => d.data.name === 'dashboard' && d.data.title === 'Dashboard' && d.data.path === '/dashboard');
72
+ return directory ? directory.data.id : undefined;
73
+ }
74
+ } catch (err) {
75
+ if (err.response.status === HTTP_UNAUTHORIZED_STATUS_CODE) {
76
+ console.error(`\n${chalk.red.bold('Provided Crowdin token is invalid')}`);
77
+ console.log(`Check the ${chalk.default.yellow('dev.private.json')} file.`);
78
+ return throwError(err);
79
+ }
149
80
  }
150
81
  }
151
82
 
152
83
  async function findCrowdinAppSubFolderId(credentials, directoryId, subfolderName) {
153
84
  const url = `${CROWDIN_PROJECT_URL}/directories?directoryId=${directoryId}`;
154
85
  const AuthStr = 'Bearer '.concat(credentials.token);
155
- const directories = await axios.get(url, { headers: { Authorization: AuthStr } });
156
- console.log('subdirectories', directories.data.data)
157
-
158
- return directories.data.data.find(d => d.data.name === subfolderName).id;
86
+ const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
87
+ const subdirectory = resp.data.data.find(d => d.data.name === subfolderName);
88
+ return subdirectory ? subdirectory.data.id : undefined;
159
89
  }
160
90
 
161
91
  async function getFileIdIfExists(credentials, directoryId, fileName) {
162
92
  const url = `${CROWDIN_PROJECT_URL}/files?directoryId=${directoryId}`;
163
93
  const AuthStr = 'Bearer '.concat(credentials.token);
164
- const files = await axios.get(url, { headers: { Authorization: AuthStr } });
165
- return files.data.find(d => d.data.name === fileName).id;
94
+ const resp = await axios.get(url, { headers: { Authorization: AuthStr } });
95
+ const file = resp.data.data.find(d => d.data.name === fileName);
96
+ return file ? file.data.id : undefined;
166
97
  }
167
98
 
168
- async function uploadFileToCrowdinStorage(credentials, crowdinPath, localPath) {
99
+ function postFileToCrowdin(credentials, localPath) {
100
+ const AuthStr = 'Bearer '.concat(credentials.token);
101
+ const file = fs.readFileSync(localPath);
102
+ const url = `${CROWDIN_STORAGE_URL}`;
103
+
104
+ return from(axios.post(url, file, { headers: {
105
+ "Authorization": AuthStr,
106
+ "Content-Type": 'application/json',
107
+ "Crowdin-API-FileName": 'en.json'
108
+ } }));
109
+ }
110
+
111
+ async function uploadFileToCrowdinStorage(credentials, localPath) {
169
112
  if (!fs.existsSync(localPath)) {
170
113
  console.error(`${chalk.red.bold('Local file does not exists')} ${chalk.yellow.bold(`[${localPath}]`)}`);
171
114
  throw new Error('Local file does not exist');
172
115
  }
173
-
174
- return postFileToCrowdin('update', credentials, crowdinPath, localPath).pipe(
116
+ return postFileToCrowdin(credentials, localPath).pipe(
175
117
  catchError(error => {
176
- if (error.response.status === HTTP_NOT_FOUND_STATUS_CODE) {
177
- return promptAddFile(crowdinPath).pipe(
178
- filter(({addFileConfirm}) => addFileConfirm === true),
179
- switchMap(() => postFileToCrowdin('add', credentials, crowdinPath, localPath))
180
- );
181
- }
182
-
118
+ console('ERROR could not upload file to crowdin storage')
183
119
  return throwError(error);
184
120
  }),
185
- tap({
186
- next: response => {
187
- if (response.data) {
188
- console.log(`${chalk.green.bold('Translations uploaded to Crowdin')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
189
- } else {
190
- console.error('Unexpected result');
191
- console.log(response);
192
- }
193
- },
194
- error: error => {
195
- const crowdinError = error.response && error.response.data && error.response.data.error;
196
-
197
- if (crowdinError && crowdinError.code === CROWDIN_DIRECTORY_NOT_FOUND_ERROR_CODE) {
198
- console.error(`\n${chalk.red.bold('Directory does not exist')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
199
- console.log('Create the directory in the Crowdin panel first.');
200
- } else if (error.response.status === HTTP_UNAUTHORIZED_STATUS_CODE) {
201
- console.error(`\n${chalk.red.bold('Provided Crowdin credentials are incorrect')}`);
202
- console.log(`Check the ${chalk.default.yellow('dev.private.json')} file.`);
203
- } else {
204
- console.error('\nUnexpected error:');
205
- console.error(error);
206
- }
207
- }
208
- })
209
121
  ).toPromise();
210
122
  }
211
123
 
212
- function updateCrowdinFile(credentials, storageId, fileId) {
124
+ async function updateCrowdinFile(credentials, crowdinPath, storageId, fileId) {
213
125
  const url = `${CROWDIN_PROJECT_URL}/files/${fileId}`;
214
126
  const AuthStr = 'Bearer '.concat(credentials.token);
215
127
  const body = {
@@ -224,19 +136,70 @@ function updateCrowdinFile(credentials, storageId, fileId) {
224
136
  },
225
137
  "replaceModifiedContext": false
226
138
  }
227
- return axios.put(url, body, { headers: { Authorization: AuthStr } });
139
+ return from(axios.put(url, body, { headers: { Authorization: AuthStr } })).pipe(tap({
140
+ next: response => {
141
+ if (response.data) {
142
+ console.log(`${chalk.green.bold('Translations uploaded to Crowdin')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
143
+ } else {
144
+ console.error('Unexpected result');
145
+ console.log(response);
146
+ }
147
+ },
148
+ error: error => {
149
+ console.error('\nUnexpected error:');
150
+ console.error(error);
151
+ }
152
+ })).toPromise();
153
+ }
154
+
155
+ async function addCrowdinFile(credentials, crowdinPath, storageId, folderId) {
156
+ const url = `${CROWDIN_PROJECT_URL}/files`;
157
+ const AuthStr = 'Bearer '.concat(credentials.token);
158
+ const body = {
159
+ "storageId": storageId,
160
+ "name": "en.json",
161
+ "directoryId": folderId,
162
+ "context": null,
163
+ "type": "json",
164
+ "parserVersion": 1,
165
+ "importOptions": {
166
+ "contentSegmentation": false,
167
+ "customSegmentation": false
168
+ },
169
+ "exportOptions": {
170
+ "exportPattern": "%locale%.json"
171
+ },
172
+ "excludedTargetLanguages": null
173
+ }
174
+ return from(axios.post(url, body, { headers: { Authorization: AuthStr } })).pipe(tap({
175
+ next: response => {
176
+ if (response.data) {
177
+ console.log(`${chalk.green.bold('Translations uploaded to Crowdin')} ${chalk.yellow.bold(`[${crowdinPath}]`)}`);
178
+ } else {
179
+ console.error('Unexpected result');
180
+ console.log(response);
181
+ }
182
+ },
183
+ error: error => {
184
+ console.error('\nUnexpected error:');
185
+ console.error(error);
186
+ }
187
+ })).toPromise();
228
188
  }
229
189
 
230
- function fetchFileFromCrowdin(credentials, crowdinPath, language) {
231
- const url = `${CROWDIN_PROJECT_URL}/export-file`;
232
- const params = {
233
- 'login': credentials.login,
234
- 'account-key': credentials.accountKey,
235
- 'file': crowdinPath,
236
- 'language': language
190
+ function fetchFileFromCrowdin(credentials, language, fileId) {
191
+ const url = `${CROWDIN_PROJECT_URL}/translations/exports`;
192
+ const AuthStr = 'Bearer '.concat(credentials.token);
193
+ const body = {
194
+ "targetLanguageId": language,
195
+ "fileIds": [fileId]
237
196
  };
238
197
 
239
- return axios.get(url, { params });
198
+ return axios.post(url, body, { headers: { Authorization: AuthStr } });
199
+ }
200
+
201
+ function readFakeTranslationsFile(url) {
202
+ return axios.get(url);
240
203
  }
241
204
 
242
205
  function promptAddFile(crowdinPath) {
@@ -249,4 +212,4 @@ function promptAddFile(crowdinPath) {
249
212
  }));
250
213
  }
251
214
 
252
- module.exports = { getCrowdinCredentials, getCrowdinConfig, findCrowdinAppDirectoryId, findCrowdinAppSubFolderId, getFileIdIfExists, uploadFileToCrowdinStorage, updateCrowdinFile, uploadFileToCrowdin, fetchFileFromCrowdin };
215
+ module.exports = { getCrowdinCredentials, getCrowdinConfig, findCrowdinAppDirectoryId, findCrowdinAppSubFolderId, getFileIdIfExists, uploadFileToCrowdinStorage, updateCrowdinFile, addCrowdinFile, fetchFileFromCrowdin, readFakeTranslationsFile };
@@ -1,6 +1,7 @@
1
1
  const chalk = require('chalk');
2
2
 
3
3
  function uploadTranslationToS3(s3, bucket, remotePath, translation) {
4
+ console.log('translation', translation)
4
5
  let normalizedPath = remotePath.replace(/\\/g, '/');
5
6
  if (normalizedPath[0] === '/') { normalizedPath = normalizedPath.slice(1); }
6
7
 
@@ -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,79 @@ 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 = crowdinPath.startsWith('/') ? crowdinPath.substr(1) : 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
+ console.log('fakeTranslation', fakeTranslation.data);
133
+
134
+ await uploadTranslationToS3(s3Client, bucket, s3FakeTranslationPath, JSON.stringify(fakeTranslation.data));
135
+ }
136
+
92
137
  async function promptForceContinue() {
93
138
  return inquirer.prompt({
94
139
  type: 'confirm',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enplug/scripts",
3
- "version": "1.11.4-dev7",
3
+ "version": "1.11.4-dev70",
4
4
  "description": "Enplug scripts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",