/**
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
*/
/* globals app, module, __dirname, Promise, process, console, require */
/* jshint esversion: 6 */
var request = require('request'),
fs = require('fs');
//
// NOTE: re-implement this object based on server and API requirements
//
var port = process.env.CECLSP_PORT || 8084,
lingotekServerPrefix = 'https://myaccount.lingotek.com/api',
urls = {
COMMUNITY: lingotekServerPrefix + '/community',
PROJECT: lingotekServerPrefix + '/project',
DOCUMENT: lingotekServerPrefix + '/document',
WORKFLOW: lingotekServerPrefix + '/workflow'
};
// get the proxy server from the environment, if any
var HTTPS_PROXY = process.env.HTTPS_PROXY;
var HTTP_PROXY = process.env.HTTP_PROXY;
var addProxy = function (options, url) {
// get appropriate proxy based on protocol (default to the other one if not specified)
var proxy = url.indexOf('https') === 0 ? (HTTPS_PROXY || HTTP_PROXY) : (HTTP_PROXY || HTTPS_PROXY);
if (proxy) {
options.proxy = proxy;
}
};
/**
* Connector's interface to the Language Service Provider.</br>
* Handles all the required GET/POST/DELETE requests.
* @constructor
* @alias ConnectorApi
*/
var ConnectorInterface = function () {};
ConnectorInterface.prototype = {
getAccountInfo: function (authToken) {
return Promise.reject('connectorApi.getAccountInfo(): not implemented');
},
getLocales: function (authToken) {
return Promise.reject('connectorApi.getLocales(): not implemented');
},
patchDocument: function (authToken, id, args) {
return Promise.reject('connectorApi.patchDocument(): not implemented');
},
deleteDocument: function (authToken, id) {
return Promise.reject('connectorApi.deleteDocument(): not implemented');
},
/**
* Import a document into the Language Service Provider.
*
* @param {string} projectId Identifier of the project that will include the document.
* @param {string} authToken Authorization header token to include in the request.
* @param {string} name Name of the document.
* @param {string} content JSON content of the document.
* @param {string} sourceLocale The source locale for the document.
* @param {string} additionalData Additional Data to apply on each document.
* @returns {Promise.<object>} The response from the Language Service Provider.
*/
addDocument: function (projectId, authToken, name, content, sourceLocale, additionalData) {
var error = '';
if (!projectId || !authToken, !name || !content || !sourceLocale) {
// invalid parameters
error = 'required parameters: projectId, authToken, name, content';
} else {
// validate content is JSON & not empty
try {
var contentJSON = JSON.parse(content);
if (Object.keys(contentJSON).length === 0) {
// ToDo: create a "translated" document for this as it's already translated
error = 'content parameter JSON must not be an empty object';
}
} catch (e) {
error = 'content parameter must be JSON';
}
}
// if there is an error, report it
if (error) {
return Promise.reject('connectorApi.addDocument(): ' + error);
}
// Remove file extension from name
var nameLessExt = name && name.replace(/\.[^.]+$/, "");
// console.log('lingotekProvider name', name, 'nameLessExt', nameLessExt);
return new Promise(function (resolve, reject) {
var options = {
url: urls.DOCUMENT,
auth: {
bearer: authToken
},
form: {
'project_id': projectId,
'locale_code': sourceLocale,
'format': 'JSON',
'charset': 'UTF-8',
'title': nameLessExt,
'content': content
}
};
if (Array.isArray(additionalData)) {
additionalData.forEach(function(item) {
options.form[item.parameter] = item.value;
});
}
addProxy(options, options.url);
request.post(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
body = JSON.parse(body);
return resolve(body);
} else {
console.log('addDocument failure data', body);
return reject(body);
}
});
});
},
addBinaryFile: function (projectId, authToken, binaryFileSpec, file, sourceLocale, additionalData) {
var error = '',
format = '';
if (!projectId || !authToken, !file.name || !sourceLocale) {
// invalid parameters
error = 'required parameters: projectId, authToken, name';
return Promise.reject('connectorApi.addBinaryFile(): ' + error);
} else {
var ext = '';
var extIndex = file.name.lastIndexOf('.');
if (extIndex > 0) {
ext = file.name.substring(extIndex + 1);
format = ext.toUpperCase();
}
}
return new Promise(function (resolve, reject) {
//
var options = {
url: urls.DOCUMENT,
auth: {
bearer: authToken
},
headers: {
'Content-Type': 'multipart/form-data'
}
};
addProxy(options, options.url);
var multiPartFormRequest = request.post(options, function (error, response, body) {
if (error) {
console.log('lingotekProvider addBinaryFile 1 error', error);
return reject(error);
}
var data;
try {
data = JSON.parse(body);
} catch (err) {
data = body;
}
if (response && response.statusCode >= 200 && response.statusCode < 300) {
return resolve(data);
} else {
console.log('addBinaryFile failure data', data);
return reject(data);
}
});
//
// populate the form body
var form = multiPartFormRequest.form();
form.append('project_id', projectId);
form.append('locale_code', sourceLocale);
form.append('format', format);
form.append('title', file.name);
form.append('content', fs.createReadStream(binaryFileSpec));
if (Array.isArray(additionalData)) {
additionalData.forEach(function(item) {
form.append(item.parameter, item.value);
});
}
});
},
/**
* Get the document identified by the Language service provider Id
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} id Language Service Provider identifier for the document.
* @returns {Promise.<string>} The content of the document.
*/
getDocument: function (authToken, id) {
return new Promise(function (resolve, reject) {
var options = {
url: urls.DOCUMENT + '/' + id,
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var document = JSON.parse(body);
return resolve(document);
} else {
console.log('lingotekProvider getDocument error', error, 'httpResponse.statusCode', httpResponse.statusCode, 'body', body);
return reject(body);
}
});
});
},
/**
* Get the document identified by the Language service provider Id
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} id Language Service Provider identifier for the document.
* @returns {Promise.<string>} The content of the document.
*/
getDocumentImportProcess: function (authToken, id) {
return new Promise(function (resolve, reject) {
var options = {
url: urls.DOCUMENT + '/' + id + '/import-process',
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var document = JSON.parse(body);
return resolve(document);
} else {
console.log('lingotekProvider getDocument error', error, 'httpResponse.statusCode', httpResponse.statusCode, 'body', body);
return reject(body);
}
});
});
},
documentExists: function (authToken, id) {
return Promise.reject('connectorApi.documentExists(): not implemented');
},
/**
* Get the statuses of all the locales of the document in the Language Service Provider.
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} id Language Service Provider identifier for the document.
* @returns {Promise.<object>} The Language Service Provider GET "/:id/translation" response.
*/
getDocumentTranslationStatuses: function (authToken, id) {
// confirm parameters
if (!authToken || !id) {
return Promise.reject('connectorApi.getDocumentTranslationStatuses(): required parameters: authToken, id');
}
return new Promise(function (resolve, reject) {
var options = {
url: urls.DOCUMENT + '/' + id + '/translation',
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var translation = JSON.parse(body);
return resolve(translation);
} else {
return reject(error);
}
});
});
},
/**
* Get the status of the specified locale of the document in the Language Service Provider.
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} id Language Service Provider identifier for the document.
* @param {string} locale Name of the locale to check.
* @returns {Promise.<object>} The Language Service Provider GET "/:id/translation" response specific for the requested locale.
*/
getDocumentTranslationStatus: function (authToken, id, locale) {
// confirm parameters
if (!authToken || !id) {
return Promise.reject('connectorApi.getTranslationStatus(): required parameters: authToken, id');
}
return this.getDocumentTranslationStatuses(id).then(function (translationStatuses) {
var translations = translationStatuses.entities || [],
translation;
for (var i = 0; i < translations.length; i++) {
if (translations[i].locale_code === locale) {
translation = translations[i];
break;
}
}
return Promise.resolve(translation);
});
},
getDocumentInfo: function (authToken, id) {
return Promise.reject('connectorApi.getDocumentInfo(): not implemented');
},
getDocumentStatus: function (authToken, id) {
return Promise.reject('connectorApi.getDocumentStatus(): not implemented');
},
/**
* Request the document be translated into the given locale.
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} projectId Identifier of the project that will include the document.
* @param {string} id Language Service Provider identifier for the document.
* @param {string} locale Name of the locale to check.
* @returns {Promise.<object>} The Language Service Provider POST "/:id/translation" response.
*/
addTranslation: function (authToken, projectId, id, locale) {
// confirm parameters
if (!authToken || !projectId || !id || !locale) {
return Promise.reject('connectorApi.addTranslation(): required parameters: authToken, projectId, id, locale');
}
return new Promise(function (resolve, reject) {
var options = {
url: urls.DOCUMENT + '/' + id + '/translation',
auth: {
bearer: authToken
},
form: {
'locale_code': locale
}
};
addProxy(options, options.url);
request.post(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
body = JSON.parse(body);
return resolve(body);
} else {
return reject(error);
}
});
});
},
/**
* Get the translation of the document for the given locale.
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} id Language Service Provider identifier for the document.
* @param {string} locale Name of the locale to check.
* @returns {Promise.<string>} The Language Service Provider GET "/:id/translation/:locale" response.
*/
getTranslation: function (authToken, id, locale) {
// confirm parameters
if (!authToken || !id || !locale) {
return Promise.reject('connectorApi.getTranslation(): required parameters: authToken, id, locale');
}
return new Promise(function (resolve, reject) {
var options = {
url: urls.DOCUMENT + '/' + id + '/content?locale_code=' + locale + '&use_source=true',
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var translation = JSON.parse(body);
return resolve(translation);
} else {
return reject(error);
}
});
});
},
getBinaryTranslation: function (authToken, id, locale) {
// confirm parameters
if (!authToken || !id || !locale) {
return Promise.reject('connectorApi.getTranslation(): required parameters: authToken, id, locale');
}
return new Promise(function (resolve, reject) {
var options = {
url: urls.DOCUMENT + '/' + id + '/content?locale_code=' + locale + '&use_source=true',
auth: {
bearer: authToken
},
encoding: 'binary'
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
return resolve(body);
} else {
return reject(error);
}
});
});
},
deleteTranslation: function (authToken, id, locale) {
return Promise.reject('connectorApi.deleteTranslation(): not implemented');
},
getCommunities: function (authToken) {
return Promise.reject('connectorApi.getCommunities(): not implemented');
},
/**
* Create a new project in the Language Service Provider.
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} communityId Community identifier that should contain the project.
* @param {string} workflowId Identifier of the Language Service Provider workflow to use to translate any documents.
* @param {string} projectName Name of project to create.
* @returns {Promise.<string>} The Language Service Provider string id for the created project.
*/
addProject: function (authToken, communityId, workflowId, projectName) {
// confirm parameters
if (!authToken || !communityId || !workflowId || !projectName) {
return Promise.reject('connectorApi.addProject(): required parameters: authToken, communityId, workflowId, projectName)');
}
var getWorkflowId = workflowId ? Promise.resolve(workflowId) : new Promise(function (resolve, reject) {
// Get workflow_id
var options = {
url: urls.WORKFLOW + '?community_id=' + communityId,
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var workflows = JSON.parse(body);
// use this workflow instead of the one passed in
workflowId = workflows.entities[0].properties.id;
resolve(workflowId);
} else {
reject('unable to locate workflowId');
}
});
});
return new Promise(function (resolve, reject) {
getWorkflowId.then(function (workflowId) {
// create the project
options = {
url: urls.PROJECT,
auth: {
bearer: authToken
},
form: {
title: projectName,
community_id: communityId,
workflow_id: workflowId
}
};
addProxy(options, options.url);
request.post(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var project = JSON.parse(body);
return resolve(project);
} else {
return reject(error);
}
});
});
});
},
/**
* Get the percent complete of the project.<br/>
* This represents the percentage complete of the translations for all the documents in the project.
*
* @param {string} authToken Authorization header token to include in the request.
* @param {string} id Language Service Provider identifier for the document.
* @returns {Promise.<string>} The Language Service Provider GET "/:id/status" response.
*/
getProjectStatus: function (authToken, id) {
if (!authToken || !id) {
return Promise.reject('connectorApi.getProjectStatus(): required parameters: authToken, id)');
}
return new Promise(function (resolve, reject) {
var options = {
url: urls.PROJECT + '/' + id + '/status',
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var projectStatus = JSON.parse(body);
return resolve(projectStatus);
} else {
return reject(error);
}
});
});
},
getProjects: function (authToken, community_id) {
return Promise.reject('connectorApi.getProjects(): not implemented');
},
getVaults: function (authToken, community_id) {
return Promise.reject('connectorApi.getVaults(): not implemented');
},
getFilters: function (authToken) {
return Promise.reject('connectorApi.getFilters(): not implemented');
},
getFilter: function (authToken, id) {
return Promise.reject('connectorApi.getFilter(): not implemented');
},
getFilterContent: function (authToken, id) {
return Promise.reject('connectorApi.getFilterContent(): not implemented');
},
addFilter: function (authToken, name, type, content) {
return Promise.reject('connectorApi.addFilter(): not implemented');
},
getWorkflowsImpl: function (authToken, communityId) {
return new Promise(function (resolve, reject) {
var options = {
url: urls.WORKFLOW + '?community_id=' + communityId,
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var workflows = JSON.parse(body);
return resolve(workflows);
} else {
return reject(error);
}
});
});
},
/**
* Get the details about the community for this user.
*
* @param {string} authToken Authorization header token to include in the request.
* @returns {Promise.<object>} The Language Service Provider GET "/community" response.
*/
getCommunity: function (authToken) {
if (!authToken) {
return Promise.reject('connectorApi.getCommunity(): required parameters: authToken)');
}
return new Promise(function (resolve, reject) {
var options = {
url: urls.COMMUNITY,
auth: {
bearer: authToken
}
};
addProxy(options, options.url);
request.get(options, function (error, httpResponse, body) {
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
var communityResponse = JSON.parse(body),
community = communityResponse.entities[0],
communityDetails = community.properties;
return resolve(communityDetails.id);
} else {
return reject(error);
}
});
});
}
};
module.exports = new ConnectorInterface();