Move meta-information to MediaWiki object

This commit is contained in:
Florine W. Dekker 2020-04-15 15:04:39 +02:00
parent 9138636c03
commit 57a0913f6f
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
3 changed files with 57 additions and 59 deletions

View File

@ -1,6 +1,6 @@
{
"name": "interlanguage-checker",
"version": "1.6.0",
"version": "1.7.0",
"description": "Check the consistency of MediaWiki interlanguage links in a simple overview.",
"author": "Felix W. Dekker",
"browser": "bundle.js",

View File

@ -52,7 +52,7 @@ doAfterLoad(async () => {
messageHandler.handle("progress", `Initializing <code>${urlInput.getValue()}</code>`);
try {
const mw = new MediaWiki(urlInput.getValue());
const mw = await new MediaWiki(urlInput.getValue()).init();
mwm = await new MediaWikiManager().init(mw);
} catch (error) {
messageHandler.handle("error", error);

View File

@ -76,37 +76,38 @@ export class Redirect {
/**
* A map of interwiki links.
*
* @property map {Object.<string, string>} maps interwiki abbreviations to URLs
* @property map {Array<{prefix: string, url: string}>} maps interwiki abbreviations to URLs
*/
export class InterwikiMap {
/**
* Constructs a new interwiki map.
*
* @param map {Object.<string, string>} the mapping from interwiki abbreviations to URLs to store in this map
* @param map {Array<{prefix: string, url: string}>} the mapping from interwiki abbreviations to URLs to store in
* this map
*/
constructor(map) {
this.map = Object.assign({}, map);
this.map = map;
}
/**
* Returns the URL for the given abbreviation, or `undefined` if the abbreviation could not be found.
* Returns the URL for the given prefix, or `undefined` if the prefix could not be found.
*
* @param abbr {string} the abbreviation to return the URL of
* @returns {string} the URL for the given abbreviation, or `undefined` if the abbreviation could not be found
* @param prefix {string} the prefix to return the URL of
* @returns {string} the URL for the given prefix, or `undefined` if the prefix could not be found
*/
getUrl(abbr) {
return this.map[abbr];
getUrl(prefix) {
return this.map.find(it => it.prefix === prefix).url;
}
/**
* Returns `true` if and only if this map has a URL for the given abbreviation.
* Returns `true` if and only if this map has a URL for the given prefix.
*
* @param abbr {string} the abbreviation to check for
* @returns {boolean} `true` if and only if this map has a URL for the given abbreviation
* @param prefix {string} the prefix to check for
* @returns {boolean} `true` if and only if this map has a URL for the given prefix
*/
hasUrl(abbr) {
return this.map[abbr] !== undefined;
hasUrl(prefix) {
return this.map.find(it => it.prefix === prefix) !== undefined;
}
/**
@ -116,13 +117,13 @@ export class InterwikiMap {
* @return a new `InterwikiMap` with all entries from both this map and the given map
*/
mergeWith(other) {
const conflicts = Object.keys(this.map)
.filter(key => other.map.hasOwnProperty(key))
.filter(key => this.map[key] !== other.map[key]);
if (conflicts.length !== 0)
const conflicts = this.map
.filter(({prefix}) => other.hasUrl(prefix))
.filter(({prefix, url}) => url !== other.getUrl(prefix));
if (conflicts.length > 0)
console.warn("iw map merge conflict(s)", conflicts);
return new InterwikiMap(Object.assign({}, this.map, other.map));
return new InterwikiMap(this.map.concat(other.map));
}
}
@ -267,7 +268,7 @@ export class InterlangNetwork {
return mergeStates(state, newStatus);
}, "present");
switch(state) {
switch (state) {
case "present":
return "complete";
case "absent":
@ -286,6 +287,9 @@ export class InterlangNetwork {
*
* @property baseUrl {string} the origin of the wiki's API
* @property apiPath {string} the path relative to the wiki's API; starts with a `/`
* @property general {Object} the general information, retrieved from the API
* @property interwikiMap {InterwikiMap} the interwiki map of this wiki
* @property namespaces {Object[]} the namespaces on this wiki
*/
export class MediaWiki {
/**
@ -299,6 +303,20 @@ export class MediaWiki {
this.apiPath = urlObj.pathname;
}
/**
* Initializes this `MediaWiki` object with the necessary information from the API.
*
* @returns {MediaWiki} this `MediaWiki` object
*/
async init() {
const query = await this.getSiteInfo("general", "interwikimap", "namespaces");
this.general = query.general;
this.interwikiMap = new InterwikiMap(query.interwikimap);
this.namespaces = query.namespaces;
return this;
}
/**
* Sends a request to the MediaWiki API and runs the given callback on the response.
@ -358,30 +376,14 @@ export class MediaWiki {
}
/**
* Returns this wiki's general information.
* Returns this wiki's site information.
*
* @return {Object} this wiki's general information
* @param props {...string} the site information properties to retrieve, such as "general" or "interwikimap"
* @returns {Object} the wiki's site information, with each property corresponding to an argument to this method
*/
getGeneralInfo() {
return this.request({action: "query", meta: "siteinfo", siprop: "general"})
.then(response => response.query.general);
}
/**
* Requests this wiki's interwiki map.
*
* @return {Promise<InterwikiMap>} this wiki's interwiki map
*/
getIwMap() {
return this
.request({action: "query", meta: "siteinfo", siprop: "interwikimap"})
.then(response =>
response.query.interwikimap.reduce((map, mapping) => {
map[mapping["prefix"]] = mapping["url"];
return map;
}, {})
)
.then(map => new InterwikiMap(map));
getSiteInfo(...props) {
return this.request({action: "query", meta: "siteinfo", siprop: props.join("|")})
.then(response => response.query);
}
}
@ -401,7 +403,7 @@ export class MediaWikiManager {
*/
constructor() {
this.mws = {};
this._iwMap = new InterwikiMap({});
this._iwMap = new InterwikiMap([]);
}
/**
@ -411,18 +413,16 @@ export class MediaWikiManager {
* @return {MediaWikiManager} this `MediaWikiManager`
*/
async init(baseMw) {
const general = await baseMw.getGeneralInfo();
this.basePath = [...(baseMw.apiPath)]
.map((it, i) => it === general.articlepath[i] ? it : "")
.map((it, i) => it === baseMw.general.articlepath[i] ? it : "")
.join("")
.slice(0, -1);
this.articlePath = general.articlepath.slice(this.basePath.length);
this.articlePath = baseMw.general.articlepath.slice(this.basePath.length);
this.apiPath = baseMw.apiPath.slice(this.basePath.length);
this.baseLang = general.lang;
this.baseLang = baseMw.general.lang;
this.mws[general.lang] = baseMw;
await this._importIwMap(baseMw);
this.mws[baseMw.general.lang] = baseMw;
this._updateIwMap();
return this;
}
@ -443,8 +443,8 @@ export class MediaWikiManager {
return undefined;
const url = this._iwMap.getUrl(lang);
this.mws[lang] = new MediaWiki(url.slice(0, -this.articlePath.length) + this.apiPath);
await this._importIwMap(this.mws[lang]);
this.mws[lang] = await new MediaWiki(url.slice(0, -this.articlePath.length) + this.apiPath).init();
this._updateIwMap();
return this.mws[lang];
}
@ -471,13 +471,11 @@ export class MediaWikiManager {
/**
* Imports the interwiki map from the given wiki, fetching it from the server and merging it into this manager's
* main interwiki map.
*
* @param mw {MediaWiki} the `MediaWiki` to import the interwiki map of
* Updates the `_iwMap` property with the entries in `MediaWiki` instances in this manager.
*/
async _importIwMap(mw) {
this._iwMap = this._iwMap.mergeWith(await mw.getIwMap());
_updateIwMap() {
const maps = Object.keys(this.mws).map(key => this.mws[key].interwikiMap.map);
this._iwMap = new InterwikiMap([].concat(...maps));
}
}