feat: Crowdin integration scripts/actions (#40657)

This commit is contained in:
Randell Dawson
2021-01-12 11:20:54 -07:00
committed by GitHub
parent ab222e31e7
commit 0095583028
16 changed files with 670 additions and 69 deletions

View File

@@ -0,0 +1,7 @@
require('dotenv').config();
const authHeader = {
Authorization: `Bearer ${process.env.CROWDIN_PERSONAL_TOKEN}`
};
module.exports = authHeader;

View File

@@ -0,0 +1,5 @@
const delay = (time = 2000) => {
return new Promise(resolve => setTimeout(() => resolve(true), time));
};
module.exports = delay;

View 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
};

View 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
};

View 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;

View 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
};