Skip to content

Commit

Permalink
Extend API configuration options. zotero/web-library#558
Browse files Browse the repository at this point in the history
  • Loading branch information
tnajdek committed Aug 30, 2024
1 parent 785f9fe commit 21b673f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 5 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,9 @@ Executes request and returns a response. Not meant to be called directly, instea

| Param | Type | Description |
| --- | --- | --- |
| options.apiScheme | <code>String</code> | Scheme part of the API URL |
| options.apiAuthorityPart | <code>String</code> | Authority part of the API URL |
| options.apiPath | <code>String</code> | Path part of the API URL |
| options.authorization | <code>String</code> | 'Authorization' header |
| options.zoteroWriteToken | <code>String</code> | 'Zotero-Write-Token' header |
| options.ifModifiedSinceVersion | <code>String</code> | 'If-Modified-Since-Version' header |
Expand Down
26 changes: 24 additions & 2 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const api = function() {
* @chainable
*/
const api = function(key = '', opts = {}) {
let props = { ...opts };
let props = { ...processOpts(opts) };

if(!('executors' in props) && (!this || !('executors' in this))) {
props.executors = [request];
Expand Down Expand Up @@ -648,13 +648,35 @@ const api = function() {
return ef.bind(this)(opts);
}

const processOpts = function (opts) {
let newOpts = { ...opts };
if ('apiScheme' in newOpts) {
// only alphanumeric characters and the +, -, and . allowed
if (newOpts.apiScheme.endsWith('://')) {
newOpts.apiScheme = newOpts.apiScheme.substring(0, newOpts.apiScheme.length - 3);
}
if (!newOpts.apiScheme.match(/^[a-zA-Z0-9+\-.]+$/)) {
throw new Error('apiScheme can only contain alphanumeric characters, plus (+), minus (-), and dot (.)');
}
}
if ('apiPath' in newOpts) {
if (newOpts.apiPath.startsWith('/')) {
newOpts.apiPath = newOpts.apiPath.substring(1);
}
if (newOpts.apiPath.length > 0 && !newOpts.apiPath.endsWith('/')) {
newOpts.apiPath += '/';
}
}
return newOpts;
}

const prepareRequest = function(config, verb, opts, body) {
var method = verb.toLowerCase();
var relevantSearchKey;
var requestConfig, keysToDelete;
switch(method) {
case 'get':
requestConfig = { ...config, ...opts, method };
requestConfig = { ...config, ...processOpts(opts), method };
if('version' in requestConfig) {
requestConfig['ifModifiedSinceVersion'] = requestConfig['version'];
delete requestConfig['version'];
Expand Down
9 changes: 7 additions & 2 deletions src/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ const resourcesSpecs = [

const defaults = {
apiAuthorityPart: 'api.zotero.org',
apiPath: '',
apiScheme: 'https',
cache: 'default',
credentials: 'omit',
format: 'json',
Expand Down Expand Up @@ -171,6 +173,9 @@ const sleep = seconds => {
/**
* Executes request and returns a response. Not meant to be called directly, instead use {@link
module:zotero-api-client~api}.
* @param {String} options.apiScheme - Scheme part of the API URL
* @param {String} options.apiAuthorityPart - Authority part of the API URL
* @param {String} options.apiPath - Path part of the API URL
* @param {String} options.authorization - 'Authorization' header
* @param {String} options.zoteroWriteToken - 'Zotero-Write-Token' header
* @param {String} options.ifModifiedSinceVersion - 'If-Modified-Since-Version' header
Expand Down Expand Up @@ -257,7 +262,7 @@ const request = async config => {
const headers = makeHeaders(options, acceptedHeaderNames);
const path = makeUrlPath(options.resource);
const query = makeUrlQuery(options, queryParamNames);
const url = `https://${options.apiAuthorityPart}/${path}${query}`;
const url = `${options.apiScheme}://${options.apiAuthorityPart}/${options.apiPath}${path}${query}`;
const fetchConfig = {};

for(let param of fetchParamNames) {
Expand Down Expand Up @@ -330,7 +335,7 @@ const request = async config => {
let uploadResponse, isUploadSuccessful, registerResponse;
if(hasDefinedKey(options, 'filePatch')) {
const uploadQuery = makeUrlQuery({ ...options, upload: authData.uploadKey }, filePatchQueryParamNames);
const uploadUrl = `https://${options.apiAuthorityPart}/${path}${uploadQuery}`;
const uploadUrl = `${options.apiScheme}://${options.apiAuthorityPart}/${options.apiPath}${path}${uploadQuery}`;

delete fetchConfig.headers['Content-Type'];
// upload file patch request
Expand Down
34 changes: 33 additions & 1 deletion test/api.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,42 @@ describe('Zotero Api Client', () => {

it('accepts api key and optional config', () => {
const request = api(KEY, {
apiAuthorityPart: 'some-other-api.zotero.org'
apiAuthorityPart: 'some-other-api.zotero.org',
apiPath: 'some-other-path/',
apiScheme: 'app',
}).getConfig();
assert.equal(request.zoteroApiKey, KEY);
assert.equal(request.apiAuthorityPart, 'some-other-api.zotero.org');
assert.equal(request.apiPath, 'some-other-path/');
assert.equal(request.apiScheme, 'app');
});

it('should correct apiPath missing a trailing slash', () => {
const request = api(KEY, {
apiPath: 'some-other-path'
}).getConfig();
assert.equal(request.apiPath, 'some-other-path/');
});

it('should correct apiPath begining with a slash', () => {
const request = api(KEY, {
apiPath: '/some-other-path'
}).getConfig();
assert.equal(request.apiPath, 'some-other-path/');
});

it('should correct apiScheme ', () => {
const request = api(KEY, {
apiScheme: 'app://'
}).getConfig();
assert.equal(request.apiScheme, 'app');
});

it('rejects invalid apiScheme', () => {
assert.throws(() => {
api(KEY, { apiScheme: 'h$$p' });
}, 'apiScheme can only contain alphanumeric characters, plus (+), minus (-), and dot (.)');

});

it('allows configuration via multiple api() calls', () => {
Expand Down
18 changes: 18 additions & 0 deletions test/request.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1931,4 +1931,22 @@ describe('ZoteroJS request', () => {
});
});
})

describe('Configuration', () => {
it('should honor api configuration', () => {
fetchMock.mock('begin:app://some-other-api.zotero.org:123/prefix/users/475425/items/ABCD1111', 200);
return request({
method: 'get',
resource: {
library: 'u475425',
items: 'ABCD1111'
},
apiAuthorityPart: 'some-other-api.zotero.org:123',
apiPath: 'prefix/',
apiScheme: 'app'
}).then(() => {
assert.isOk(fetchMock.done());
});
});
});
});

0 comments on commit 21b673f

Please sign in to comment.