feat: Crowdin integration scripts/actions (#40657)
This commit is contained in:
7
tools/crowdin/utils/auth-header.js
Normal file
7
tools/crowdin/utils/auth-header.js
Normal file
@@ -0,0 +1,7 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const authHeader = {
|
||||
Authorization: `Bearer ${process.env.CROWDIN_PERSONAL_TOKEN}`
|
||||
};
|
||||
|
||||
module.exports = authHeader;
|
5
tools/crowdin/utils/delay.js
Normal file
5
tools/crowdin/utils/delay.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const delay = (time = 2000) => {
|
||||
return new Promise(resolve => setTimeout(() => resolve(true), time));
|
||||
};
|
||||
|
||||
module.exports = delay;
|
86
tools/crowdin/utils/dirs.js
Normal file
86
tools/crowdin/utils/dirs.js
Normal file
@@ -0,0 +1,86 @@
|
||||
const makeRequest = require('./make-request');
|
||||
const delay = require('./delay');
|
||||
const authHeader = require('./auth-header');
|
||||
|
||||
const getDirs = async projectId => {
|
||||
let headers = { ...authHeader };
|
||||
let done = false;
|
||||
let offset = 0;
|
||||
let files = [];
|
||||
while (!done) {
|
||||
const endPoint = `projects/${projectId}/directories?limit=500&offset=${offset}`;
|
||||
await delay(1000);
|
||||
const response = await makeRequest({
|
||||
method: 'get',
|
||||
endPoint,
|
||||
headers
|
||||
});
|
||||
if (response.data) {
|
||||
if (response.data.length) {
|
||||
files = [...files, ...response.data];
|
||||
offset += 500;
|
||||
} else {
|
||||
done = true;
|
||||
return files;
|
||||
}
|
||||
} else {
|
||||
const { error } = response;
|
||||
console.log(error.errorcode);
|
||||
console.log(error.messsage);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const addDir = async (projectId, dirName, parentDirId) => {
|
||||
let headers = { ...authHeader };
|
||||
const endPoint = `projects/${projectId}/directories`;
|
||||
let body = {
|
||||
name: dirName
|
||||
};
|
||||
if (parentDirId) {
|
||||
body = { ...body, directoryId: parentDirId };
|
||||
}
|
||||
|
||||
const response = await makeRequest({
|
||||
method: 'post',
|
||||
endPoint,
|
||||
headers,
|
||||
body
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
const createDirs = async (crowdinDirs, dirPath) => {
|
||||
// superParent is the top level directory on crowdin
|
||||
const superParent = crowdinDirs.find(dir => !dir.data.directoryId);
|
||||
let lastParentId = superParent.data.id;
|
||||
|
||||
const splitDirPath = dirPath.split('/');
|
||||
splitDirPath.shift();
|
||||
|
||||
// we are assuming that the first directory in 'newFile' is the same as the superParent
|
||||
// maybe throw a check in here to verify that's true
|
||||
const findCurrDir = (directory, crowdinDirs) => {
|
||||
return crowdinDirs.find(({ data: { name, directoryId } }) => {
|
||||
return name === directory && directoryId === lastParentId;
|
||||
});
|
||||
};
|
||||
|
||||
for (let directory of splitDirPath) {
|
||||
const currentDirectory = findCurrDir(directory, crowdinDirs);
|
||||
if (!currentDirectory) {
|
||||
const response = await addDir(10, directory, lastParentId);
|
||||
lastParentId = response.data.id;
|
||||
} else {
|
||||
lastParentId = currentDirectory.data.id;
|
||||
}
|
||||
}
|
||||
return lastParentId;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addDir,
|
||||
getDirs,
|
||||
createDirs
|
||||
};
|
124
tools/crowdin/utils/files.js
Normal file
124
tools/crowdin/utils/files.js
Normal file
@@ -0,0 +1,124 @@
|
||||
const makeRequest = require('./make-request');
|
||||
const delay = require('./delay');
|
||||
const authHeader = require('./auth-header');
|
||||
|
||||
const addFile = async (projectId, filename, fileContent, directoryId) => {
|
||||
let headers = { ...authHeader };
|
||||
headers['Crowdin-API-FileName'] = filename;
|
||||
const endPoint = `storages`;
|
||||
const contentType = 'application/text';
|
||||
const body = fileContent;
|
||||
const storageResponse = await makeRequest({
|
||||
method: 'post',
|
||||
contentType,
|
||||
endPoint,
|
||||
headers,
|
||||
body
|
||||
});
|
||||
if (storageResponse.data) {
|
||||
const fileBody = {
|
||||
storageId: storageResponse.data.id,
|
||||
name: filename,
|
||||
directoryId
|
||||
};
|
||||
const fileResponse = await makeRequest({
|
||||
method: 'post',
|
||||
endPoint: `projects/${projectId}/files`,
|
||||
headers,
|
||||
body: fileBody
|
||||
});
|
||||
if (fileResponse.data) {
|
||||
return fileResponse.data;
|
||||
} else {
|
||||
console.log('error');
|
||||
console.dir(fileResponse, { depth: null, colors: true });
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const updateFile = async (projectId, fileId, fileContent) => {
|
||||
let headers = { ...authHeader };
|
||||
const endPoint = `storages`;
|
||||
const contentType = 'application/text';
|
||||
const body = fileContent;
|
||||
const storageResponse = await makeRequest({
|
||||
method: 'post',
|
||||
contentType,
|
||||
endPoint,
|
||||
headers,
|
||||
body
|
||||
});
|
||||
if (storageResponse.data) {
|
||||
const fileBody = {
|
||||
storageId: storageResponse.data.id
|
||||
};
|
||||
const fileResponse = await makeRequest({
|
||||
method: 'put',
|
||||
endPoint: `projects/${projectId}/files${fileId}`,
|
||||
headers,
|
||||
body: fileBody
|
||||
});
|
||||
if (fileResponse.data) {
|
||||
return fileResponse.data;
|
||||
} else {
|
||||
console.log('error');
|
||||
console.dir(fileResponse, { depth: null, colors: true });
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const deleteFile = async (projectId, fileId, filePath) => {
|
||||
let headers = { ...authHeader };
|
||||
const endPoint = `projects/${projectId}/files/${fileId}`;
|
||||
await makeRequest({
|
||||
method: 'delete',
|
||||
endPoint,
|
||||
headers
|
||||
});
|
||||
console.log(`Deleted ${filePath} from Crowdin project`);
|
||||
return null;
|
||||
};
|
||||
|
||||
const getFiles = async projectId => {
|
||||
let headers = { ...authHeader };
|
||||
let done = false;
|
||||
let offset = 0;
|
||||
let files = [];
|
||||
while (!done) {
|
||||
const endPoint = `projects/${projectId}/files?limit=500&offset=${offset}`;
|
||||
await delay(1000);
|
||||
const response = await makeRequest({
|
||||
method: 'get',
|
||||
endPoint,
|
||||
headers
|
||||
});
|
||||
if (response.data) {
|
||||
if (response.data.length) {
|
||||
files = [...files, ...response.data];
|
||||
offset += 500;
|
||||
} else {
|
||||
done = true;
|
||||
files = files.map(({ data: { directoryId, id: fileId, path } }) => {
|
||||
// remove leading forwardslash
|
||||
path = path.slice(1);
|
||||
return { directoryId, fileId, path };
|
||||
});
|
||||
return files;
|
||||
}
|
||||
} else {
|
||||
const { error } = response;
|
||||
console.log(error.errorcode);
|
||||
console.log(error.messsage);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addFile,
|
||||
updateFile,
|
||||
deleteFile,
|
||||
getFiles
|
||||
};
|
34
tools/crowdin/utils/make-request.js
Normal file
34
tools/crowdin/utils/make-request.js
Normal file
@@ -0,0 +1,34 @@
|
||||
require('dotenv').config();
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const makeRequest = async ({
|
||||
method,
|
||||
endPoint,
|
||||
contentType = 'application/json',
|
||||
accept = 'application/json',
|
||||
headers,
|
||||
body
|
||||
}) => {
|
||||
headers = { ...headers, 'Content-Type': contentType, Accept: accept };
|
||||
const apiUrl = process.env.CROWDIN_API_URL + endPoint;
|
||||
|
||||
if (contentType === 'application/x-www-form-urlencoded') {
|
||||
body = Object.entries(body)
|
||||
.reduce((formDataArr, [key, value]) => {
|
||||
return formDataArr.concat(`${key}=${value}`);
|
||||
}, [])
|
||||
.join('&');
|
||||
} else if (contentType === 'application/json') {
|
||||
body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const response = await fetch(apiUrl, { headers, method, body });
|
||||
if (method !== 'delete') {
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = makeRequest;
|
74
tools/crowdin/utils/strings.js
Normal file
74
tools/crowdin/utils/strings.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const authHeader = require('./auth-header');
|
||||
const makeRequest = require('./make-request');
|
||||
|
||||
const isHeading = str => /\h\d/.test(str);
|
||||
const isCode = str => /^\/pre\/code|\/code$/.test(str);
|
||||
|
||||
const shouldHide = (text, context, challengeTitle) => {
|
||||
if (isHeading(context) || isCode(context)) {
|
||||
return true;
|
||||
}
|
||||
return text !== challengeTitle && context.includes('id=front-matter');
|
||||
};
|
||||
|
||||
const getStrings = async ({ projectId, fileId }) => {
|
||||
let headers = { ...authHeader };
|
||||
const endPoint = `projects/${projectId}/strings?fileId=${fileId}&limit=500`;
|
||||
const strings = await makeRequest({ method: 'get', endPoint, headers });
|
||||
if (strings.data) {
|
||||
return strings.data;
|
||||
} else {
|
||||
const { error, errors } = strings;
|
||||
console.error(error ? error : errors);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const updateString = async ({ projectId, stringId, propsToUpdate }) => {
|
||||
let headers = { ...authHeader };
|
||||
const endPoint = `projects/${projectId}/strings/${stringId}`;
|
||||
const body = propsToUpdate.map(({ path, value }) => ({
|
||||
op: 'replace',
|
||||
path,
|
||||
value
|
||||
}));
|
||||
await makeRequest({
|
||||
method: 'patch',
|
||||
endPoint,
|
||||
headers,
|
||||
body
|
||||
});
|
||||
};
|
||||
|
||||
const changeHiddenStatus = async (projectId, stringId, newStatus) => {
|
||||
await updateString({
|
||||
projectId,
|
||||
stringId,
|
||||
propsToUpdate: [{ path: '/isHidden', value: newStatus }]
|
||||
});
|
||||
};
|
||||
|
||||
const updateFileStrings = async ({ projectId, fileId, challengeTitle }) => {
|
||||
const fileStrings = await getStrings({
|
||||
projectId,
|
||||
fileId
|
||||
});
|
||||
|
||||
for (let {
|
||||
data: { id: stringId, text, isHidden, context }
|
||||
} of fileStrings) {
|
||||
const hideString = shouldHide(text, context, challengeTitle);
|
||||
if (!isHidden && hideString) {
|
||||
changeHiddenStatus(projectId, stringId, true);
|
||||
} else if (isHidden && !hideString) {
|
||||
changeHiddenStatus(projectId, stringId, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getStrings,
|
||||
changeHiddenStatus,
|
||||
updateString,
|
||||
updateFileStrings
|
||||
};
|
Reference in New Issue
Block a user