Move REST search client out of WVUI into Vector
Bug: T288686 Depends-on: I4afc8c38dc9c51d55b46b766a1417b1266963482 Change-Id: Iac6023bb6edca5c8dddc3bfd362db727b2534946
This commit is contained in:
parent
7084f9a9df
commit
3e92800bd6
|
@ -2,3 +2,4 @@
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
var mockMediaWiki = require( '@wikimedia/mw-node-qunit/src/mockMediaWiki.js' );
|
var mockMediaWiki = require( '@wikimedia/mw-node-qunit/src/mockMediaWiki.js' );
|
||||||
global.mw = mockMediaWiki();
|
global.mw = mockMediaWiki();
|
||||||
|
global.$ = require('jquery');
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"linkMap": {
|
"linkMap": {
|
||||||
"\"addEventListener\"": "https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener",
|
"\"addEventListener\"": "https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener",
|
||||||
"jQuery": "https://api.jquery.com",
|
"jQuery": "https://api.jquery.com",
|
||||||
|
"AbortSignal": "https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal",
|
||||||
"Document": "https://developer.mozilla.org/docs/Web/API/Document",
|
"Document": "https://developer.mozilla.org/docs/Web/API/Document",
|
||||||
"Element": "https://developer.mozilla.org/docs/Web/API/Element",
|
"Element": "https://developer.mozilla.org/docs/Web/API/Element",
|
||||||
"Event": "https://developer.mozilla.org/docs/Web/API/Event",
|
"Event": "https://developer.mozilla.org/docs/Web/API/Event",
|
||||||
|
@ -31,11 +32,11 @@
|
||||||
"HTMLInputElement": "https://developer.mozilla.org/docs/Web/API/HTMLInputElement",
|
"HTMLInputElement": "https://developer.mozilla.org/docs/Web/API/HTMLInputElement",
|
||||||
"\"removeEventListener\"": "https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener",
|
"\"removeEventListener\"": "https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener",
|
||||||
"Window": "https://developer.mozilla.org/docs/Web/API/Window",
|
"Window": "https://developer.mozilla.org/docs/Web/API/Window",
|
||||||
|
|
||||||
"CheckboxHack": "https://doc.wikimedia.org/mediawiki-core/master/js",
|
"CheckboxHack": "https://doc.wikimedia.org/mediawiki-core/master/js",
|
||||||
|
|
||||||
"MW": "https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw",
|
|
||||||
"MediaWikiPageReadyModule": "https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.plugin.page.ready",
|
"MediaWikiPageReadyModule": "https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.plugin.page.ready",
|
||||||
|
"MW": "https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw",
|
||||||
|
"MwMap": "https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Map",
|
||||||
|
"RequestInit": "https://developer.mozilla.org/en-US/docs/Web/API/Request/Request",
|
||||||
"JQueryStatic": "https://api.jquery.com",
|
"JQueryStatic": "https://api.jquery.com",
|
||||||
"VectorResourceLoaderVirtualConfig": "#",
|
"VectorResourceLoaderVirtualConfig": "#",
|
||||||
"void": "#",
|
"void": "#",
|
||||||
|
|
|
@ -19,8 +19,9 @@
|
||||||
"eslint-config-wikimedia": "0.20.0",
|
"eslint-config-wikimedia": "0.20.0",
|
||||||
"grunt-banana-checker": "0.9.0",
|
"grunt-banana-checker": "0.9.0",
|
||||||
"jest": "26.4.2",
|
"jest": "26.4.2",
|
||||||
|
"jest-fetch-mock": "3.0.3",
|
||||||
"jsdoc": "3.6.7",
|
"jsdoc": "3.6.7",
|
||||||
"jsdoc-wmf-theme": "0.0.3",
|
"jsdoc-wmf-theme": "0.0.5",
|
||||||
"less": "3.8.1",
|
"less": "3.8.1",
|
||||||
"less-loader": "4.1.0",
|
"less-loader": "4.1.0",
|
||||||
"mustache": "3.0.1",
|
"mustache": "3.0.1",
|
||||||
|
@ -7607,6 +7608,57 @@
|
||||||
"react": "^0.14.0 || ^15.0.0 || ^16.0.0"
|
"react": "^0.14.0 || ^15.0.0 || ^16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-fetch": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "2.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-fetch/node_modules/node-fetch": {
|
||||||
|
"version": "2.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||||
|
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-fetch/node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/cross-fetch/node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/cross-fetch/node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
@ -14172,6 +14224,16 @@
|
||||||
"node": ">= 10.14.2"
|
"node": ">= 10.14.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-fetch-mock": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cross-fetch": "^3.0.4",
|
||||||
|
"promise-polyfill": "^8.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-get-type": {
|
"node_modules/jest-get-type": {
|
||||||
"version": "26.3.0",
|
"version": "26.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
|
||||||
|
@ -15793,9 +15855,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsdoc-wmf-theme": {
|
"node_modules/jsdoc-wmf-theme": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/jsdoc-wmf-theme/-/jsdoc-wmf-theme-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/jsdoc-wmf-theme/-/jsdoc-wmf-theme-0.0.5.tgz",
|
||||||
"integrity": "sha512-jpszk0hcjY7bD1sCd8JrBdtcoudG0h9FbJTjdq8WOSEtUBNWgtIc7s1ccDoYnK/bp4OEuA7xH0xtpqe0SVutsw==",
|
"integrity": "sha512-YRVucO3yiKF6a54oIR+gQLDynO60o2m0lOiCBCws0vIORJOn9T++tGJrOCVy5TSaSAmJTX1cnTbUCH7L+c1JCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domino": "^2.0.1",
|
"domino": "^2.0.1",
|
||||||
|
@ -18861,6 +18923,12 @@
|
||||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/promise-polyfill": {
|
||||||
|
"version": "8.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.1.tgz",
|
||||||
|
"integrity": "sha512-3p9zj0cEHbp7NVUxEYUWjQlffXqnXaZIMPkAO7HhFh8u5636xLRDHOUo2vpWSK0T2mqm6fKLXYn1KP6PAZ2gKg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/promise.allsettled": {
|
"node_modules/promise.allsettled": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.4.tgz",
|
||||||
|
@ -30772,6 +30840,48 @@
|
||||||
"warning": "^4.0.3"
|
"warning": "^4.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cross-fetch": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"node-fetch": "2.6.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "2.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||||
|
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
@ -35892,6 +36002,16 @@
|
||||||
"jest-util": "^26.6.2"
|
"jest-util": "^26.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jest-fetch-mock": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cross-fetch": "^3.0.4",
|
||||||
|
"promise-polyfill": "^8.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jest-get-type": {
|
"jest-get-type": {
|
||||||
"version": "26.3.0",
|
"version": "26.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
|
||||||
|
@ -37144,9 +37264,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsdoc-wmf-theme": {
|
"jsdoc-wmf-theme": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/jsdoc-wmf-theme/-/jsdoc-wmf-theme-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/jsdoc-wmf-theme/-/jsdoc-wmf-theme-0.0.5.tgz",
|
||||||
"integrity": "sha512-jpszk0hcjY7bD1sCd8JrBdtcoudG0h9FbJTjdq8WOSEtUBNWgtIc7s1ccDoYnK/bp4OEuA7xH0xtpqe0SVutsw==",
|
"integrity": "sha512-YRVucO3yiKF6a54oIR+gQLDynO60o2m0lOiCBCws0vIORJOn9T++tGJrOCVy5TSaSAmJTX1cnTbUCH7L+c1JCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"domino": "^2.0.1",
|
"domino": "^2.0.1",
|
||||||
|
@ -39569,6 +39689,12 @@
|
||||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"promise-polyfill": {
|
||||||
|
"version": "8.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.1.tgz",
|
||||||
|
"integrity": "sha512-3p9zj0cEHbp7NVUxEYUWjQlffXqnXaZIMPkAO7HhFh8u5636xLRDHOUo2vpWSK0T2mqm6fKLXYn1KP6PAZ2gKg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"promise.allsettled": {
|
"promise.allsettled": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.4.tgz",
|
||||||
|
|
|
@ -31,8 +31,9 @@
|
||||||
"eslint-config-wikimedia": "0.20.0",
|
"eslint-config-wikimedia": "0.20.0",
|
||||||
"grunt-banana-checker": "0.9.0",
|
"grunt-banana-checker": "0.9.0",
|
||||||
"jest": "26.4.2",
|
"jest": "26.4.2",
|
||||||
|
"jest-fetch-mock": "3.0.3",
|
||||||
"jsdoc": "3.6.7",
|
"jsdoc": "3.6.7",
|
||||||
"jsdoc-wmf-theme": "0.0.3",
|
"jsdoc-wmf-theme": "0.0.5",
|
||||||
"less": "3.8.1",
|
"less": "3.8.1",
|
||||||
"less-loader": "4.1.0",
|
"less-loader": "4.1.0",
|
||||||
"mustache": "3.0.1",
|
"mustache": "3.0.1",
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
{
|
{
|
||||||
"extends": [
|
"extends": [
|
||||||
"../../.eslintrcEs6.json"
|
"../../.eslintrcEs6.json"
|
||||||
]
|
],
|
||||||
|
"rules": {
|
||||||
|
"jsdoc/no-undefined-types": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"definedTypes": [
|
||||||
|
"RequestInit",
|
||||||
|
"MwMap"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
<script>
|
<script>
|
||||||
/* global SubmitEvent */
|
/* global SubmitEvent */
|
||||||
const wvui = require( 'wvui-search' ),
|
const wvui = require( 'wvui-search' ),
|
||||||
|
client = require( './restSearchClient.js' ),
|
||||||
instrumentation = require( './instrumentation.js' );
|
instrumentation = require( './instrumentation.js' );
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -62,10 +63,10 @@ module.exports = {
|
||||||
/**
|
/**
|
||||||
* Allow wikis eg. Hebrew Wikipedia to replace the default search API client
|
* Allow wikis eg. Hebrew Wikipedia to replace the default search API client
|
||||||
*
|
*
|
||||||
* @return {void|Object}
|
* @return {module:restSearchClient~SearchClient}
|
||||||
*/
|
*/
|
||||||
getClient: () => {
|
getClient: () => {
|
||||||
return mw.config.get( 'wgVectorSearchClient', undefined );
|
return client( mw.config );
|
||||||
},
|
},
|
||||||
language: () => {
|
language: () => {
|
||||||
return mw.config.get( 'wgUserLanguage' );
|
return mw.config.get( 'wgUserLanguage' );
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/** @module restSearchClient */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AbortableFetch
|
||||||
|
* @property {Promise<any>} fetch
|
||||||
|
* @property {Function} abort
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} NullableAbortController
|
||||||
|
* @property {AbortSignal | undefined} signal
|
||||||
|
* @property {Function} abort
|
||||||
|
*/
|
||||||
|
const nullAbortController = {
|
||||||
|
signal: undefined,
|
||||||
|
abort: () => {} // Do nothing (no-op)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper which combines native fetch() in browsers and the following json() call.
|
||||||
|
*
|
||||||
|
* @param {string} resource
|
||||||
|
* @param {RequestInit} [init]
|
||||||
|
* @return {AbortableFetch}
|
||||||
|
*/
|
||||||
|
function fetchJson( resource, init ) {
|
||||||
|
// As of 2020, browser support for AbortController is limited:
|
||||||
|
// https://caniuse.com/abortcontroller
|
||||||
|
// so replacing it with no-op if it doesn't exist.
|
||||||
|
/* eslint-disable compat/compat */
|
||||||
|
const controller = window.AbortController ?
|
||||||
|
new AbortController() :
|
||||||
|
nullAbortController;
|
||||||
|
/* eslint-enable compat/compat */
|
||||||
|
|
||||||
|
const getJson = fetch( resource, $.extend( init, {
|
||||||
|
signal: controller.signal
|
||||||
|
} ) ).then( ( response ) => {
|
||||||
|
if ( !response.ok ) {
|
||||||
|
return Promise.reject(
|
||||||
|
'Network request failed with HTTP code ' + response.status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
} );
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetch: getJson,
|
||||||
|
abort: () => {
|
||||||
|
controller.abort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} RestResponse
|
||||||
|
* @property {RestResult[]} pages
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} RestResult
|
||||||
|
* @property {number} id
|
||||||
|
* @property {string} key
|
||||||
|
* @property {string} title
|
||||||
|
* @property {string} [description]
|
||||||
|
* @property {RestThumbnail | null} [thumbnail]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} RestThumbnail
|
||||||
|
* @property {string} url
|
||||||
|
* @property {number | null} [width]
|
||||||
|
* @property {number | null} [height]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SearchResponse
|
||||||
|
* @property {string} query
|
||||||
|
* @property {SearchResult[]} results
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SearchResult
|
||||||
|
* @property {number} id
|
||||||
|
* @property {string} key
|
||||||
|
* @property {string} title
|
||||||
|
* @property {string} [description]
|
||||||
|
* @property {SearchResultThumbnail} [thumbnail]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SearchResultThumbnail
|
||||||
|
* @property {string} url
|
||||||
|
* @property {number} [width]
|
||||||
|
* @property {number} [height]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nullish coalescing operator (??) helper
|
||||||
|
*
|
||||||
|
* @param {any} a
|
||||||
|
* @param {any} b
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
function nullish( a, b ) {
|
||||||
|
return ( a !== null && a !== undefined ) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} query
|
||||||
|
* @param {RestResponse} restResponse
|
||||||
|
* @return {SearchResponse}
|
||||||
|
*/
|
||||||
|
function adaptApiResponse( query, restResponse ) {
|
||||||
|
return {
|
||||||
|
query,
|
||||||
|
results: restResponse.pages.map( ( page ) => {
|
||||||
|
const thumbnail = page.thumbnail;
|
||||||
|
return {
|
||||||
|
id: page.id,
|
||||||
|
key: page.key,
|
||||||
|
title: page.title,
|
||||||
|
description: page.description,
|
||||||
|
thumbnail: thumbnail ? {
|
||||||
|
url: thumbnail.url,
|
||||||
|
width: nullish( thumbnail.width, undefined ),
|
||||||
|
height: nullish( thumbnail.height, undefined )
|
||||||
|
} : undefined
|
||||||
|
};
|
||||||
|
} )
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AbortableSearchFetch
|
||||||
|
* @property {Promise<SearchResponse>} fetch
|
||||||
|
* @property {Function} abort
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback fetchByTitle
|
||||||
|
* @param {string} query The search term.
|
||||||
|
* @param {string} domain The base URL for the wiki without protocol. Example: 'sr.wikipedia.org'.
|
||||||
|
* @param {number} [limit] Maximum number of results.
|
||||||
|
* @return {AbortableSearchFetch}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SearchClient
|
||||||
|
* @property {fetchByTitle} fetchByTitle
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MwMap} config
|
||||||
|
* @return {SearchClient}
|
||||||
|
*/
|
||||||
|
function restSearchClient( config ) {
|
||||||
|
const customClient = config.get( 'wgVectorSearchClient' );
|
||||||
|
return customClient || {
|
||||||
|
/**
|
||||||
|
* @type {fetchByTitle}
|
||||||
|
*/
|
||||||
|
fetchByTitle: ( q, domain, limit = 10 ) => {
|
||||||
|
const params = { q, limit };
|
||||||
|
const url = '//' + domain + config.get( 'wgScriptPath' ) + '/rest.php/v1/search/title?' + $.param( params );
|
||||||
|
const result = fetchJson( url, {
|
||||||
|
headers: {
|
||||||
|
accept: 'application/json'
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
const searchResponsePromise = result.fetch
|
||||||
|
.then( ( /** @type {RestResponse} */ res ) => {
|
||||||
|
return adaptApiResponse( q, res );
|
||||||
|
} );
|
||||||
|
return {
|
||||||
|
abort: result.abort,
|
||||||
|
fetch: searchResponsePromise
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = restSearchClient;
|
|
@ -145,6 +145,7 @@
|
||||||
"packageFiles": [
|
"packageFiles": [
|
||||||
"resources/skins.vector.search/skins.vector.search.js",
|
"resources/skins.vector.search/skins.vector.search.js",
|
||||||
"resources/skins.vector.search/instrumentation.js",
|
"resources/skins.vector.search/instrumentation.js",
|
||||||
|
"resources/skins.vector.search/restSearchClient.js",
|
||||||
"resources/skins.vector.search/App.vue",
|
"resources/skins.vector.search/App.vue",
|
||||||
{
|
{
|
||||||
"name": "resources/skins.vector.search/config.json",
|
"name": "resources/skins.vector.search/config.json",
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* global fetchMock */
|
||||||
|
const restSearchClient = require( '../../resources/skins.vector.search/restSearchClient.js' );
|
||||||
|
const jestFetchMock = require( 'jest-fetch-mock' );
|
||||||
|
|
||||||
|
const mockedRequests = !process.env.TEST_LIVE_REQUESTS;
|
||||||
|
const configMock = {
|
||||||
|
get: jest.fn().mockImplementation( key => {
|
||||||
|
if ( key === 'wgScriptPath' ) {
|
||||||
|
return '/w';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} ),
|
||||||
|
set: jest.fn()
|
||||||
|
};
|
||||||
|
|
||||||
|
describe( 'restApiSearchClient', () => {
|
||||||
|
beforeAll( () => {
|
||||||
|
jestFetchMock.enableFetchMocks();
|
||||||
|
} );
|
||||||
|
|
||||||
|
afterAll( () => {
|
||||||
|
jestFetchMock.disableFetchMocks();
|
||||||
|
} );
|
||||||
|
|
||||||
|
beforeEach( () => {
|
||||||
|
fetchMock.resetMocks();
|
||||||
|
if ( !mockedRequests ) {
|
||||||
|
fetchMock.disableMocks();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( '2 results', async () => {
|
||||||
|
const thumbUrl = '//upload.wikimedia.org/wikipedia/commons/0/01/MediaWiki-smaller-logo.png';
|
||||||
|
const restResponse = {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
id: 37298,
|
||||||
|
key: 'Media',
|
||||||
|
title: 'Media',
|
||||||
|
description: 'Wikimedia disambiguation page',
|
||||||
|
thumbnail: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 323710,
|
||||||
|
key: 'MediaWiki',
|
||||||
|
title: 'MediaWiki',
|
||||||
|
description: 'wiki software',
|
||||||
|
thumbnail: {
|
||||||
|
width: 200,
|
||||||
|
height: 189,
|
||||||
|
url: thumbUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
fetchMock.mockOnce( JSON.stringify( restResponse ) );
|
||||||
|
|
||||||
|
const searchResult = await restSearchClient( configMock ).fetchByTitle(
|
||||||
|
'media',
|
||||||
|
'en.wikipedia.org',
|
||||||
|
2
|
||||||
|
).fetch;
|
||||||
|
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
expect( searchResult.query ).toStrictEqual( 'media' );
|
||||||
|
expect( searchResult.results ).toBeTruthy();
|
||||||
|
expect( searchResult.results.length ).toBe( 2 );
|
||||||
|
|
||||||
|
expect( searchResult.results[ 0 ] ).toStrictEqual(
|
||||||
|
Object.assign( {}, restResponse.pages[ 0 ], {
|
||||||
|
// thumbnail: null -> thumbnail: undefined
|
||||||
|
thumbnail: undefined
|
||||||
|
} ) );
|
||||||
|
expect( searchResult.results[ 1 ] ).toStrictEqual( restResponse.pages[ 1 ] );
|
||||||
|
|
||||||
|
if ( mockedRequests ) {
|
||||||
|
expect( fetchMock ).toHaveBeenCalledTimes( 1 );
|
||||||
|
expect( fetchMock ).toHaveBeenCalledWith(
|
||||||
|
'//en.wikipedia.org/w/rest.php/v1/search/title?q=media&limit=2',
|
||||||
|
{ headers: { accept: 'application/json' }, signal: controller.signal }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( '0 results', async () => {
|
||||||
|
const restResponse = { pages: [] };
|
||||||
|
fetchMock.mockOnce( JSON.stringify( restResponse ) );
|
||||||
|
|
||||||
|
const searchResult = await restSearchClient( configMock ).fetchByTitle(
|
||||||
|
'thereIsNothingLikeThis',
|
||||||
|
'en.wikipedia.org'
|
||||||
|
).fetch;
|
||||||
|
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
|
const controller = new AbortController();
|
||||||
|
expect( searchResult.query ).toStrictEqual( 'thereIsNothingLikeThis' );
|
||||||
|
expect( searchResult.results ).toBeTruthy();
|
||||||
|
expect( searchResult.results.length ).toBe( 0 );
|
||||||
|
|
||||||
|
if ( mockedRequests ) {
|
||||||
|
expect( fetchMock ).toHaveBeenCalledTimes( 1 );
|
||||||
|
expect( fetchMock ).toHaveBeenCalledWith(
|
||||||
|
'//en.wikipedia.org/w/rest.php/v1/search/title?q=thereIsNothingLikeThis&limit=10',
|
||||||
|
{ headers: { accept: 'application/json' }, signal: controller.signal }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
if ( mockedRequests ) {
|
||||||
|
test( 'network error', async () => {
|
||||||
|
fetchMock.mockRejectOnce( new Error( 'failed' ) );
|
||||||
|
|
||||||
|
await expect( restSearchClient( configMock ).fetchByTitle(
|
||||||
|
'anything',
|
||||||
|
'en.wikipedia.org'
|
||||||
|
).fetch ).rejects.toThrow( 'failed' );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
Loading…
Reference in New Issue