Fix getTranslatorCode bugs
- /connector/getTranslatorCode used to respond with empty content - CodeGetter froze connectors by issuing hundreds of simultanious XHR requests
This commit is contained in:
parent
38df733a37
commit
e83bbbad4e
|
@ -24,8 +24,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const NUM_CONCURRENT_TESTS = 6;
|
const NUM_CONCURRENT_TESTS = 6;
|
||||||
const TRANSLATOR_TYPES = ["Web", "Import", "Export", "Search"];
|
|
||||||
const TABLE_COLUMNS = ["Translator", "Supported", "Status", "Pending", "Succeeded", "Failed", "Mismatch", "Issues"];
|
const TABLE_COLUMNS = ["Translator", "Supported", "Status", "Pending", "Succeeded", "Failed", "Mismatch", "Issues"];
|
||||||
|
// Not using const to prevent const collisions in connectors
|
||||||
|
var TRANSLATOR_TYPES = ["Web", "Import", "Export", "Search"];
|
||||||
var translatorTables = {},
|
var translatorTables = {},
|
||||||
translatorTestViews = {},
|
translatorTestViews = {},
|
||||||
translatorTestViewsToRun = {},
|
translatorTestViewsToRun = {},
|
||||||
|
|
|
@ -56,7 +56,7 @@ Zotero.Repo = new function() {
|
||||||
* Get translator code from repository
|
* Get translator code from repository
|
||||||
* @param {String} translatorID ID of the translator to retrieve code for
|
* @param {String} translatorID ID of the translator to retrieve code for
|
||||||
*/
|
*/
|
||||||
this.getTranslatorCode = Zotero.Promise.method(function (translatorID) {
|
this.getTranslatorCode = Zotero.Promise.method(function (translatorID, debugMode) {
|
||||||
var deferred = Zotero.Promise.defer();
|
var deferred = Zotero.Promise.defer();
|
||||||
|
|
||||||
// try standalone
|
// try standalone
|
||||||
|
@ -72,6 +72,11 @@ Zotero.Repo = new function() {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Don't fetch from repo in debug mode
|
||||||
|
if (debugMode) {
|
||||||
|
deferred.resolve([false, Zotero.Repo.SOURCE_ZOTERO_STANDALONE]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// then try repo
|
// then try repo
|
||||||
|
|
|
@ -130,7 +130,9 @@ Zotero.Translators = new function() {
|
||||||
if(!_initialized) Zotero.Translators.init()
|
if(!_initialized) Zotero.Translators.init()
|
||||||
var translators = _cache[type].slice(0);
|
var translators = _cache[type].slice(0);
|
||||||
var codeGetter = new Zotero.Translators.CodeGetter(translators, debugMode);
|
var codeGetter = new Zotero.Translators.CodeGetter(translators, debugMode);
|
||||||
return codeGetter.getAll();
|
return codeGetter.getAll().then(function() {
|
||||||
|
return translators;
|
||||||
|
});;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -326,24 +328,36 @@ Zotero.Translators = new function() {
|
||||||
Zotero.Translators.CodeGetter = function(translators, debugMode) {
|
Zotero.Translators.CodeGetter = function(translators, debugMode) {
|
||||||
this._translators = translators;
|
this._translators = translators;
|
||||||
this._debugMode = debugMode;
|
this._debugMode = debugMode;
|
||||||
}
|
this._concurrency = 1;
|
||||||
|
};
|
||||||
|
|
||||||
Zotero.Translators.CodeGetter.prototype.getAll = Zotero.Promise.method(function () {
|
Zotero.Translators.CodeGetter.prototype.getCodeFor = Zotero.Promise.method(function(i) {
|
||||||
var translators = [];
|
let translator = this._translators[i];
|
||||||
for (let translator of this._translators) {
|
// retrieve code if no code and translator is supported locally
|
||||||
// retrieve code if no code and translator is supported locally
|
if((translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER && !translator.hasOwnProperty("code"))
|
||||||
if((translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER && !translator.hasOwnProperty("code"))
|
// or if debug mode is enabled (even if unsupported locally)
|
||||||
// or if debug mode is enabled (even if unsupported locally)
|
|| (this._debugMode && (!translator.hasOwnProperty("code")
|
||||||
|| (this._debugMode && (!translator.hasOwnProperty("code")
|
// or if in debug mode and the code we have came from the repo (which doesn't
|
||||||
// or if in debug mode and the code we have came from the repo (which doesn't
|
// include test cases)
|
||||||
// include test cases)
|
|| (Zotero.Repo && translator.codeSource === Zotero.Repo.SOURCE_REPO)))) {
|
||||||
|| (Zotero.Repo && translator.codeSource === Zotero.Repo.SOURCE_REPO)))) {
|
// get code
|
||||||
// get next translator
|
return translator.getCode();
|
||||||
translators.push(translator.getCode());
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.Translators.CodeGetter.prototype.getAll = function () {
|
||||||
|
var codes = [];
|
||||||
|
// Chain promises with some level of concurrency. If unchained, fires
|
||||||
|
// off hundreds of xhttprequests on connectors and crashes the extension
|
||||||
|
for (let i = 0; i < this._translators.length; i++) {
|
||||||
|
if (i < this._concurrency) {
|
||||||
|
codes.push(this.getCodeFor(i));
|
||||||
|
} else {
|
||||||
|
codes.push(codes[i-this._concurrency].then(() => this.getCodeFor(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Zotero.Promise.all(translators);
|
return Promise.all(codes);
|
||||||
});
|
};
|
||||||
|
|
||||||
var TRANSLATOR_REQUIRED_PROPERTIES = ["translatorID", "translatorType", "label", "creator", "target",
|
var TRANSLATOR_REQUIRED_PROPERTIES = ["translatorID", "translatorType", "label", "creator", "target",
|
||||||
"priority", "lastUpdated"];
|
"priority", "lastUpdated"];
|
||||||
|
@ -428,8 +442,8 @@ Zotero.Translator.prototype.init = function(info) {
|
||||||
*
|
*
|
||||||
* @return {Promise<String|false>} - Promise for translator code or false if none
|
* @return {Promise<String|false>} - Promise for translator code or false if none
|
||||||
*/
|
*/
|
||||||
Zotero.Translator.prototype.getCode = function () {
|
Zotero.Translator.prototype.getCode = function (debugMode) {
|
||||||
return Zotero.Repo.getTranslatorCode(this.translatorID)
|
return Zotero.Repo.getTranslatorCode(this.translatorID, debugMode)
|
||||||
.then(function (args) {
|
.then(function (args) {
|
||||||
var code = args[0];
|
var code = args[0];
|
||||||
var source = args[1];
|
var source = args[1];
|
||||||
|
|
|
@ -618,7 +618,9 @@ Zotero.Server.Connector.GetTranslatorCode.prototype = {
|
||||||
*/
|
*/
|
||||||
"init":function(postData, sendResponseCallback) {
|
"init":function(postData, sendResponseCallback) {
|
||||||
var translator = Zotero.Translators.get(postData.translatorID);
|
var translator = Zotero.Translators.get(postData.translatorID);
|
||||||
sendResponseCallback(200, "application/javascript", translator.code);
|
translator.getCode().then(function(code) {
|
||||||
|
sendResponseCallback(200, "application/javascript", code);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1264,7 +1264,7 @@ Zotero.Translate.Base.prototype = {
|
||||||
|
|
||||||
// Zotero.Translators.get() returns a promise in the connectors, but we don't expect it to
|
// Zotero.Translators.get() returns a promise in the connectors, but we don't expect it to
|
||||||
// otherwise
|
// otherwise
|
||||||
if (!this.isConnector && this.translator[0].then) {
|
if (!Zotero.isConnector && this.translator[0].then) {
|
||||||
throw new Error("Translator should not be a promise in non-connector mode");
|
throw new Error("Translator should not be a promise in non-connector mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Enumeration of types of translators
|
// Enumeration of types of translators
|
||||||
const TRANSLATOR_TYPES = {"import":1, "export":2, "web":4, "search":8};
|
var TRANSLATOR_TYPES = {"import":1, "export":2, "web":4, "search":8};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton to handle loading and caching of translators
|
* Singleton to handle loading and caching of translators
|
||||||
|
|
|
@ -753,6 +753,28 @@ var generateTranslatorExportData = Zotero.Promise.coroutine(function* generateTr
|
||||||
return translatorExportData;
|
return translatorExportData;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a dummy translator that can be passed to Zotero.Translate
|
||||||
|
*/
|
||||||
|
function buildDummyTranslator(translatorType, code) {
|
||||||
|
let info = {
|
||||||
|
"translatorID":"dummy-translator",
|
||||||
|
"translatorType":1, // import
|
||||||
|
"label":"Dummy Translator",
|
||||||
|
"creator":"Simon Kornblith",
|
||||||
|
"target":"",
|
||||||
|
"priority":100,
|
||||||
|
"browserSupport":"g",
|
||||||
|
"inRepository":false,
|
||||||
|
"lastUpdated":"0000-00-00 00:00:00",
|
||||||
|
};
|
||||||
|
let translator = new Zotero.Translator(info);
|
||||||
|
translator.code = code;
|
||||||
|
return translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports an attachment from a test file.
|
* Imports an attachment from a test file.
|
||||||
* @param {string} filename - The filename to import (in data directory)
|
* @param {string} filename - The filename to import (in data directory)
|
||||||
|
|
|
@ -33,6 +33,34 @@ describe("Connector Server", function () {
|
||||||
after(function () {
|
after(function () {
|
||||||
win.close();
|
win.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('/connector/getTranslatorCode', function() {
|
||||||
|
it('should respond with translator code', function* () {
|
||||||
|
var code = 'function detectWeb() {}\nfunction doImport() {}';
|
||||||
|
var translator = buildDummyTranslator(4, code);
|
||||||
|
sinon.stub(Zotero.Translators, 'get').returns(translator);
|
||||||
|
|
||||||
|
var response = yield Zotero.HTTP.request(
|
||||||
|
'POST',
|
||||||
|
connectorServerPath + "/connector/getTranslatorCode",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
translatorID: "dummy-translator",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.isTrue(Zotero.Translators.get.calledWith('dummy-translator'));
|
||||||
|
assert.equal(response.response, code);
|
||||||
|
|
||||||
|
Zotero.Translators.get.restore();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe("/connector/saveItems", function () {
|
describe("/connector/saveItems", function () {
|
||||||
// TODO: Test cookies
|
// TODO: Test cookies
|
||||||
|
|
|
@ -1,26 +1,6 @@
|
||||||
new function() {
|
new function() {
|
||||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a dummy translator that can be passed to Zotero.Translate
|
|
||||||
*/
|
|
||||||
function buildDummyTranslator(translatorType, code) {
|
|
||||||
let info = {
|
|
||||||
"translatorID":"dummy-translator",
|
|
||||||
"translatorType":1, // import
|
|
||||||
"label":"Dummy Translator",
|
|
||||||
"creator":"Simon Kornblith",
|
|
||||||
"target":"",
|
|
||||||
"priority":100,
|
|
||||||
"browserSupport":"g",
|
|
||||||
"inRepository":false,
|
|
||||||
"lastUpdated":"0000-00-00 00:00:00",
|
|
||||||
};
|
|
||||||
let translator = new Zotero.Translator(info);
|
|
||||||
translator.code = code;
|
|
||||||
return translator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new translator that saves the specified items
|
* Create a new translator that saves the specified items
|
||||||
* @param {String} translatorType - "import" or "web"
|
* @param {String} translatorType - "import" or "web"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user