Support for connector-based save target selection
- Updates /saveItems and /saveSnapshot to take a sessionID - Provides a list of editable collections in the current library - Adds an /updateSession method that takes a sessionID and updates the collection and tags of any items saved via that operation (and changes the currently selected collection) Cross-library changes are not yet supported
This commit is contained in:
parent
4731b8f905
commit
c8cf9b9e6f
2 changed files with 435 additions and 7 deletions
|
@ -72,6 +72,136 @@ Zotero.Server.Connector = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Zotero.Server.Connector.Data = {};
|
Zotero.Server.Connector.Data = {};
|
||||||
|
|
||||||
|
Zotero.Server.Connector.SessionManager = {
|
||||||
|
_sessions: new Map(),
|
||||||
|
|
||||||
|
get: function (id) {
|
||||||
|
return this._sessions.get(id);
|
||||||
|
},
|
||||||
|
|
||||||
|
create: function (id) {
|
||||||
|
// Legacy client
|
||||||
|
if (!id) {
|
||||||
|
id = Zotero.Utilities.randomString();
|
||||||
|
}
|
||||||
|
if (this._sessions.has(id)) {
|
||||||
|
throw new Error(`Session ID ${id} exists`);
|
||||||
|
}
|
||||||
|
Zotero.debug("Creating connector save session " + id);
|
||||||
|
var session = new Zotero.Server.Connector.SaveSession(id);
|
||||||
|
this._sessions.set(id, session);
|
||||||
|
this.gc();
|
||||||
|
return session;
|
||||||
|
},
|
||||||
|
|
||||||
|
gc: function () {
|
||||||
|
// Delete sessions older than 10 minutes, or older than 1 minute if more than 10 sessions
|
||||||
|
var ttl = this._sessions.size >= 10 ? 60 : 600;
|
||||||
|
var deleteBefore = new Date() - ttl * 1000;
|
||||||
|
|
||||||
|
for (let session of this._sessions) {
|
||||||
|
if (session.created < deleteBefore) {
|
||||||
|
this._session.delete(session.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Server.Connector.SaveSession = function (id) {
|
||||||
|
this.id = id;
|
||||||
|
this.created = new Date();
|
||||||
|
this._objects = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.Server.Connector.SaveSession.prototype.addItem = async function (item) {
|
||||||
|
return this._addObjects('item', [item]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.Server.Connector.SaveSession.prototype.addItems = async function (items) {
|
||||||
|
return this._addObjects('item', items);
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.Server.Connector.SaveSession.prototype.update = async function (libraryID, collectionID, tags) {
|
||||||
|
this._currentLibraryID = libraryID;
|
||||||
|
this._currentCollectionID = collectionID;
|
||||||
|
this._currentTags = tags || "";
|
||||||
|
|
||||||
|
// Select new destination in collections pane
|
||||||
|
var win = Zotero.getActiveZoteroPane();
|
||||||
|
if (win && win.collectionsView) {
|
||||||
|
if (collectionID) {
|
||||||
|
var targetID = "C" + collectionID;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var targetID = "L" + libraryID;
|
||||||
|
}
|
||||||
|
await win.collectionsView.selectByID(targetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this._updateObjects(this._objects);
|
||||||
|
|
||||||
|
// TODO: Update active item saver
|
||||||
|
|
||||||
|
// If a single item was saved, select it
|
||||||
|
if (win && win.collectionsView) {
|
||||||
|
if (this._objects && this._objects.item) {
|
||||||
|
let items = Array.from(this._objects.item).filter(item => item.isTopLevelItem());
|
||||||
|
if (items.length == 1) {
|
||||||
|
await win.selectItem(items[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.Server.Connector.SaveSession.prototype._addObjects = async function (objectType, objects) {
|
||||||
|
if (!this._objects[objectType]) {
|
||||||
|
this._objects[objectType] = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If target has changed since the save began, update the objects
|
||||||
|
await this._updateObjects({
|
||||||
|
[objectType]: objects
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let object of objects) {
|
||||||
|
this._objects[objectType].add(object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.Server.Connector.SaveSession.prototype._updateObjects = async function (objects) {
|
||||||
|
if (Object.keys(objects).every(type => objects[type].length == 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var libraryID = this._currentLibraryID;
|
||||||
|
var collectionID = this._currentCollectionID;
|
||||||
|
var tags = this._currentTags.trim();
|
||||||
|
tags = tags ? tags.split(/\s*,\s*/) : [];
|
||||||
|
|
||||||
|
Zotero.debug("Updating objects for connector save session " + this.id);
|
||||||
|
|
||||||
|
return Zotero.DB.executeTransaction(async function () {
|
||||||
|
for (let objectType in objects) {
|
||||||
|
for (let object of objects[objectType]) {
|
||||||
|
Zotero.debug(object.libraryID);
|
||||||
|
Zotero.debug(libraryID);
|
||||||
|
if (object.libraryID != libraryID) {
|
||||||
|
throw new Error("Can't move objects between libraries");
|
||||||
|
}
|
||||||
|
// Assign tags and collections to top-level items
|
||||||
|
if (objectType == 'item' && object.isTopLevelItem()) {
|
||||||
|
object.setTags(tags);
|
||||||
|
object.setCollections(collectionID ? [collectionID] : []);
|
||||||
|
await object.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Zotero.Server.Connector.AttachmentProgressManager = new function() {
|
Zotero.Server.Connector.AttachmentProgressManager = new function() {
|
||||||
var attachmentsInProgress = new WeakMap(),
|
var attachmentsInProgress = new WeakMap(),
|
||||||
attachmentProgress = {},
|
attachmentProgress = {},
|
||||||
|
@ -172,7 +302,6 @@ Zotero.Server.Connector.GetTranslators.prototype = {
|
||||||
*/
|
*/
|
||||||
Zotero.Server.Connector.Detect = function() {};
|
Zotero.Server.Connector.Detect = function() {};
|
||||||
Zotero.Server.Endpoints["/connector/detect"] = Zotero.Server.Connector.Detect;
|
Zotero.Server.Endpoints["/connector/detect"] = Zotero.Server.Connector.Detect;
|
||||||
Zotero.Server.Connector.Data = {};
|
|
||||||
Zotero.Server.Connector.Detect.prototype = {
|
Zotero.Server.Connector.Detect.prototype = {
|
||||||
supportedMethods: ["POST"],
|
supportedMethods: ["POST"],
|
||||||
supportedDataTypes: ["application/json"],
|
supportedDataTypes: ["application/json"],
|
||||||
|
@ -373,6 +502,15 @@ Zotero.Server.Connector.SaveItem.prototype = {
|
||||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||||
var libraryID = library.libraryID;
|
var libraryID = library.libraryID;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var session = Zotero.Server.Connector.SessionManager.create(data.sessionID);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||||
|
}
|
||||||
|
yield session.update(libraryID, collection ? collection.id : false);
|
||||||
|
|
||||||
|
// TODO: Default to My Library root, since it's changeable
|
||||||
if (!library.editable) {
|
if (!library.editable) {
|
||||||
Zotero.logError("Can't add item to read-only library " + library.name);
|
Zotero.logError("Can't add item to read-only library " + library.name);
|
||||||
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
||||||
|
@ -420,9 +558,12 @@ Zotero.Server.Connector.SaveItem.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deferred.resolve([201, "application/json", JSON.stringify({items: data.items})]);
|
deferred.resolve([201, "application/json", JSON.stringify({items: data.items})]);
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
.then(function (items) {
|
||||||
|
session.addItems(items);
|
||||||
|
});
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
@ -460,6 +601,15 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||||
var libraryID = library.libraryID;
|
var libraryID = library.libraryID;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var session = Zotero.Server.Connector.SessionManager.create(data.sessionID);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||||
|
}
|
||||||
|
yield session.update(libraryID, collection ? collection.id : false);
|
||||||
|
|
||||||
|
// TODO: Default to My Library root, since it's changeable
|
||||||
if (!library.editable) {
|
if (!library.editable) {
|
||||||
Zotero.logError("Can't add item to read-only library " + library.name);
|
Zotero.logError("Can't add item to read-only library " + library.name);
|
||||||
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
||||||
|
@ -491,13 +641,14 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
delete Zotero.Server.Connector.Data[data.url];
|
delete Zotero.Server.Connector.Data[data.url];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
yield Zotero.Attachments.importFromURL({
|
let item = yield Zotero.Attachments.importFromURL({
|
||||||
libraryID,
|
libraryID,
|
||||||
url: data.url,
|
url: data.url,
|
||||||
collections: collection ? [collection.id] : undefined,
|
collections: collection ? [collection.id] : undefined,
|
||||||
contentType: "application/pdf",
|
contentType: "application/pdf",
|
||||||
cookieSandbox
|
cookieSandbox
|
||||||
});
|
});
|
||||||
|
yield session.addItem(item);
|
||||||
return 201;
|
return 201;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
@ -523,6 +674,7 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
item.setCollections([collection.id]);
|
item.setCollections([collection.id]);
|
||||||
}
|
}
|
||||||
var itemID = yield item.saveTx();
|
var itemID = yield item.saveTx();
|
||||||
|
yield session.addItem(item);
|
||||||
|
|
||||||
// save snapshot
|
// save snapshot
|
||||||
if (filesEditable && !data.skipSnapshot) {
|
if (filesEditable && !data.skipSnapshot) {
|
||||||
|
@ -580,6 +732,60 @@ Zotero.Server.Connector.SelectItems.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Accepts:
|
||||||
|
* sessionID - A session ID previously passed to /saveItems
|
||||||
|
* target - A treeViewID (L1, C23, etc.) for the library or collection to save to
|
||||||
|
* tags - A string of tags separated by commas
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 200 response on successful change
|
||||||
|
* 400 on error with 'error' property in JSON
|
||||||
|
*/
|
||||||
|
Zotero.Server.Connector.UpdateSession = function() {};
|
||||||
|
Zotero.Server.Endpoints["/connector/updateSession"] = Zotero.Server.Connector.UpdateSession;
|
||||||
|
Zotero.Server.Connector.UpdateSession.prototype = {
|
||||||
|
supportedMethods: ["POST"],
|
||||||
|
supportedDataTypes: ["application/json"],
|
||||||
|
permitBookmarklet: true,
|
||||||
|
|
||||||
|
init: async function (options) {
|
||||||
|
var data = options.data
|
||||||
|
|
||||||
|
if (!data.sessionID) {
|
||||||
|
return [400, "application/json", JSON.stringify({ error: "SESSION_ID_NOT_PROVIDED" })];
|
||||||
|
}
|
||||||
|
|
||||||
|
var session = Zotero.Server.Connector.SessionManager.get(data.sessionID);
|
||||||
|
if (!session) {
|
||||||
|
return [400, "application/json", JSON.stringify({ error: "SESSION_NOT_FOUND" })];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse treeViewID
|
||||||
|
var [type, id] = [data.target[0], parseInt(data.target.substr(1))];
|
||||||
|
var tags = data.tags;
|
||||||
|
|
||||||
|
if (type == 'L') {
|
||||||
|
let library = Zotero.Libraries.get(id);
|
||||||
|
await session.update(library.libraryID, null, tags);
|
||||||
|
}
|
||||||
|
else if (type == 'C') {
|
||||||
|
let collection = await Zotero.Collections.getAsync(id);
|
||||||
|
if (!collection) {
|
||||||
|
return [400, "application/json", JSON.stringify({ error: "COLLECTION_NOT_FOUND" })];
|
||||||
|
}
|
||||||
|
await session.update(collection.libraryID, collection.id, tags);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error(`Invalid identifier '${data.target}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [200, "application/json", JSON.stringify({})];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets progress for an attachment that is currently being saved
|
* Gets progress for an attachment that is currently being saved
|
||||||
*
|
*
|
||||||
|
@ -725,9 +931,6 @@ Zotero.Server.Connector.GetSelectedCollection.prototype = {
|
||||||
editable
|
editable
|
||||||
};
|
};
|
||||||
|
|
||||||
response.libraryName = library.name;
|
|
||||||
response.libraryEditable = library.editable;
|
|
||||||
|
|
||||||
if(collection && collection.id) {
|
if(collection && collection.id) {
|
||||||
response.id = collection.id;
|
response.id = collection.id;
|
||||||
response.name = collection.name;
|
response.name = collection.name;
|
||||||
|
@ -736,6 +939,31 @@ Zotero.Server.Connector.GetSelectedCollection.prototype = {
|
||||||
response.name = response.libraryName;
|
response.name = response.libraryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get list of editable libraries and collections
|
||||||
|
var collections = [];
|
||||||
|
var originalLibraryID = library.libraryID;
|
||||||
|
for (let library of Zotero.Libraries.getAll()) {
|
||||||
|
if (!library.editable) continue;
|
||||||
|
// TEMP: For now, don't allow library changing
|
||||||
|
if (library.libraryID != originalLibraryID) continue;
|
||||||
|
|
||||||
|
// Add recent: true for recent targets
|
||||||
|
|
||||||
|
collections.push(
|
||||||
|
{
|
||||||
|
id: library.treeViewID,
|
||||||
|
name: library.name
|
||||||
|
},
|
||||||
|
...Zotero.Collections.getByLibrary(library.libraryID, true).map(c => ({
|
||||||
|
id: c.treeViewID,
|
||||||
|
name: c.name,
|
||||||
|
level: c.level + 1 || 1 // Added by Zotero.Collections._getByContainer()
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
response.targets = collections;
|
||||||
|
|
||||||
|
// TODO: Limit debug size
|
||||||
sendResponseCallback(200, "application/json", JSON.stringify(response));
|
sendResponseCallback(200, "application/json", JSON.stringify(response));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -427,6 +427,206 @@ describe("Connector Server", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("/connector/updateSession", function () {
|
||||||
|
it("should update collections and tags of item saved via /saveItems", async function () {
|
||||||
|
var collection1 = await createDataObject('collection');
|
||||||
|
var collection2 = await createDataObject('collection');
|
||||||
|
await waitForItemsLoad(win);
|
||||||
|
|
||||||
|
var sessionID = Zotero.Utilities.randomString();
|
||||||
|
var body = {
|
||||||
|
sessionID,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
itemType: "newspaperArticle",
|
||||||
|
title: "Title",
|
||||||
|
creators: [
|
||||||
|
{
|
||||||
|
firstName: "First",
|
||||||
|
lastName: "Last",
|
||||||
|
creatorType: "author"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
title: "Attachment",
|
||||||
|
url: `${testServerPath}/attachment`,
|
||||||
|
mimeType: "text/html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
uri: "http://example.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
httpd.registerPathHandler(
|
||||||
|
"/attachment",
|
||||||
|
{
|
||||||
|
handle: function (request, response) {
|
||||||
|
response.setStatusLine(null, 200, "OK");
|
||||||
|
response.write("<html><head><title>Title</title><body>Body</body></html>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var reqPromise = Zotero.HTTP.request(
|
||||||
|
'POST',
|
||||||
|
connectorServerPath + "/connector/saveItems",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var ids = await waitForItemEvent('add');
|
||||||
|
var item = Zotero.Items.get(ids[0]);
|
||||||
|
assert.isTrue(collection2.hasItem(item.id));
|
||||||
|
await waitForItemEvent('add');
|
||||||
|
// Wait until indexing is done
|
||||||
|
await waitForItemEvent('refresh');
|
||||||
|
|
||||||
|
var req = await reqPromise;
|
||||||
|
assert.equal(req.status, 201);
|
||||||
|
|
||||||
|
// Update saved item
|
||||||
|
var req = await Zotero.HTTP.request(
|
||||||
|
'POST',
|
||||||
|
connectorServerPath + "/connector/updateSession",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionID,
|
||||||
|
target: collection1.treeViewID,
|
||||||
|
tags: "A, B"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(req.status, 200);
|
||||||
|
assert.isTrue(collection1.hasItem(item.id));
|
||||||
|
assert.isTrue(item.hasTag("A"));
|
||||||
|
assert.isTrue(item.hasTag("B"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should update collections and tags of PDF saved via /saveSnapshot", async function () {
|
||||||
|
var sessionID = Zotero.Utilities.randomString();
|
||||||
|
|
||||||
|
var collection1 = await createDataObject('collection');
|
||||||
|
var collection2 = await createDataObject('collection');
|
||||||
|
await waitForItemsLoad(win);
|
||||||
|
|
||||||
|
var file = getTestDataDirectory();
|
||||||
|
file.append('test.pdf');
|
||||||
|
httpd.registerFile("/test.pdf", file);
|
||||||
|
|
||||||
|
var ids;
|
||||||
|
var promise = waitForItemEvent('add');
|
||||||
|
var reqPromise = Zotero.HTTP.request(
|
||||||
|
'POST',
|
||||||
|
connectorServerPath + "/connector/saveSnapshot",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionID,
|
||||||
|
url: testServerPath + "/test.pdf",
|
||||||
|
pdf: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var ids = await promise;
|
||||||
|
var item = Zotero.Items.get(ids[0]);
|
||||||
|
assert.isTrue(collection2.hasItem(item.id));
|
||||||
|
// Wait until indexing is done
|
||||||
|
await waitForItemEvent('refresh');
|
||||||
|
var req = await reqPromise;
|
||||||
|
assert.equal(req.status, 201);
|
||||||
|
|
||||||
|
// Update saved item
|
||||||
|
var req = await Zotero.HTTP.request(
|
||||||
|
'POST',
|
||||||
|
connectorServerPath + "/connector/updateSession",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionID,
|
||||||
|
target: collection1.treeViewID,
|
||||||
|
tags: "A, B"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(req.status, 200);
|
||||||
|
assert.isTrue(collection1.hasItem(item.id));
|
||||||
|
assert.isTrue(item.hasTag("A"));
|
||||||
|
assert.isTrue(item.hasTag("B"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should update collections and tags of webpage saved via /saveSnapshot", async function () {
|
||||||
|
var sessionID = Zotero.Utilities.randomString();
|
||||||
|
|
||||||
|
var collection1 = await createDataObject('collection');
|
||||||
|
var collection2 = await createDataObject('collection');
|
||||||
|
await waitForItemsLoad(win);
|
||||||
|
|
||||||
|
// saveSnapshot saves parent and child before returning
|
||||||
|
var ids1, ids2;
|
||||||
|
var promise = waitForItemEvent('add').then(function (ids) {
|
||||||
|
ids1 = ids;
|
||||||
|
return waitForItemEvent('add').then(function (ids) {
|
||||||
|
ids2 = ids;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await Zotero.HTTP.request(
|
||||||
|
'POST',
|
||||||
|
connectorServerPath + "/connector/saveSnapshot",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionID,
|
||||||
|
url: "http://example.com",
|
||||||
|
html: "<html><head><title>Title</title><body>Body</body></html>"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.isTrue(promise.isFulfilled());
|
||||||
|
|
||||||
|
var item = Zotero.Items.get(ids1[0]);
|
||||||
|
|
||||||
|
// Update saved item
|
||||||
|
var req = await Zotero.HTTP.request(
|
||||||
|
'POST',
|
||||||
|
connectorServerPath + "/connector/updateSession",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionID,
|
||||||
|
target: collection1.treeViewID,
|
||||||
|
tags: "A, B"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(req.status, 200);
|
||||||
|
assert.isTrue(collection1.hasItem(item.id));
|
||||||
|
assert.isTrue(item.hasTag("A"));
|
||||||
|
assert.isTrue(item.hasTag("B"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('/connector/installStyle', function() {
|
describe('/connector/installStyle', function() {
|
||||||
var endpoint;
|
var endpoint;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue