Niedzielski has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/387234 )
Change subject: Chore: split out summary unmarshaller
......................................................................
Chore: split out summary unmarshaller
Split out page summary unmarshalling responsibilities from page
unmarshalling.
Change-Id: I771a4891297ca2edccfc3057bdef4500311a2f42
---
M src/common/http/page-summary-http-client.ts
A src/common/marshallers/page-base-unmarshaller.test.ts
A src/common/marshallers/page-base-unmarshaller.ts
A src/common/marshallers/page-summary-unmarshaller.test.ts
A src/common/marshallers/page-summary-unmarshaller.ts
M src/common/marshallers/page-unmarshaller.test.ts
M src/common/marshallers/page-unmarshaller.ts
A src/common/marshallers/utils.test.ts
8 files changed, 308 insertions(+), 270 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/marvin refs/changes/34/387234/1
diff --git a/src/common/http/page-summary-http-client.ts
b/src/common/http/page-summary-http-client.ts
index a223344..e18f0a7 100644
--- a/src/common/http/page-summary-http-client.ts
+++ b/src/common/http/page-summary-http-client.ts
@@ -2,7 +2,7 @@
import { PageSummary } from "../models/page/summary";
import { PageTitlePath } from "../models/page/title";
import { RESTBase } from "../marshallers/restbase";
-import { unmarshalPageSummary } from "../marshallers/page-unmarshaller";
+import { unmarshalPageSummary } from
"../marshallers/page-summary-unmarshaller";
import HttpResponse from "./http-response";
import { PageRedirect } from "./page-redirect";
import reencodeRESTBaseTitlePath from "./restbase-title-encoder";
diff --git a/src/common/marshallers/page-base-unmarshaller.test.ts
b/src/common/marshallers/page-base-unmarshaller.test.ts
new file mode 100644
index 0000000..9a7ac64
--- /dev/null
+++ b/src/common/marshallers/page-base-unmarshaller.test.ts
@@ -0,0 +1,28 @@
+import * as assert from "assert";
+import { PageGeolocation } from "../models/page/geolocation";
+import {
+ unmarshalETag,
+ unmarshalPageGeolocation
+} from "./page-base-unmarshaller";
+import { RESTBase } from "./restbase";
+import { EXPECTED_ETAG, HEADERS } from "./utils.test";
+
+describe("page-base-unmarshaller", () => {
+ it(".unmarshalPageGeolocation() unmarshals", () => {
+ const json: RESTBase.PageSummary.Geolocation = {
+ lat: 1,
+ lon: 2
+ };
+ const expected: PageGeolocation = {
+ latitude: 1,
+ longitude: 2
+ };
+ const result = unmarshalPageGeolocation(json as {});
+ assert.deepStrictEqual(result, expected);
+ });
+
+ it(".unmarshalETag() unmarshals", () => {
+ const result = unmarshalETag(HEADERS);
+ assert.deepStrictEqual(result, EXPECTED_ETAG);
+ });
+});
diff --git a/src/common/marshallers/page-base-unmarshaller.ts
b/src/common/marshallers/page-base-unmarshaller.ts
new file mode 100644
index 0000000..b87c104
--- /dev/null
+++ b/src/common/marshallers/page-base-unmarshaller.ts
@@ -0,0 +1,33 @@
+import { ETag } from "../models/etag";
+import { PageGeolocation } from "../models/page/geolocation";
+import { PageTitleID } from "../models/page/title";
+import { IsomorphicHeaders } from "../types/isomorphic-unfetch-extras";
+import { JSONObject } from "../types/json";
+import { RESTBase } from "./restbase";
+
+export function unmarshalPageTitleID(url: string): PageTitleID {
+ // Titles themselves may contain slashes, however, RESTBase only understands
+ // titles with encoded slashes so when this unmarshaller encounters a slash
it
+ // may safely consider this a path segment and not part of the title.
+ const titlePath = url.split("/").pop();
+ if (titlePath === undefined) {
+ throw new Error("titlePath should be known at response time.");
+ }
+ return decodeURI(titlePath);
+}
+
+export const unmarshalPageGeolocation = (json: JSONObject): PageGeolocation =>
{
+ if (json.latitude) {
+ const type: RESTBase.PageSections.Geolocation = json as any;
+ return { latitude: type.latitude, longitude: type.longitude };
+ } else {
+ const type: RESTBase.PageSummary.Geolocation = json as any;
+ return { latitude: type.lat, longitude: type.lon };
+ }
+};
+
+export const unmarshalETag = (headers: IsomorphicHeaders): ETag => {
+ const etag = headers.get("ETag") as string;
+ const [revision, timeID] = etag.split("/");
+ return { revision: parseInt(revision, 10), timeID };
+};
diff --git a/src/common/marshallers/page-summary-unmarshaller.test.ts
b/src/common/marshallers/page-summary-unmarshaller.test.ts
new file mode 100644
index 0000000..c0abf50
--- /dev/null
+++ b/src/common/marshallers/page-summary-unmarshaller.test.ts
@@ -0,0 +1,111 @@
+import * as assert from "assert";
+import { PageSummary, pageSummaryReviver } from "../models/page/summary";
+import { PageImage, PageThumbnail } from "../models/page/image";
+import {
+ unmarshalPageImage,
+ unmarshalPageThumbnail,
+ unmarshalPageSummary
+} from "./page-summary-unmarshaller";
+import { RESTBase } from "./restbase";
+import { EXPECTED_ETAG, HEADERS, reviveFile } from "./utils.test";
+
+const NOW = new Date(Date.now()).toString();
+
+describe("page-summary-unmarshaller", () => {
+ describe(".unmarshalPageThumbnail()", () => {
+ [false, true].forEach(landscape => {
+ it(`unmarshals ${landscape ? "landscape" : "portrait"}`, () => {
+ const width = landscape ? 2 : 1;
+ const height = landscape ? 1 : 2;
+ const json: RESTBase.PageSummary.Thumbnail = {
+ source: "source",
+ original: "original",
+ width,
+ height
+ };
+ const expected: PageThumbnail = {
+ url: "source",
+ width,
+ height,
+ landscape,
+ originalURL: "original"
+ };
+ const result = unmarshalPageThumbnail(json as {});
+ assert.deepStrictEqual(result, expected);
+ });
+ });
+ });
+
+ describe(".unmarshalPageImage()", () => {
+ [false, true].forEach(landscape => {
+ it(`unmarshals ${landscape ? "landscape" : "portrait"}`, () => {
+ const width = landscape ? 2 : 1;
+ const height = landscape ? 1 : 2;
+ const json: RESTBase.PageSummary.Image = {
+ source: "source",
+ width,
+ height
+ };
+ const expected: PageImage = {
+ url: "source",
+ width,
+ height,
+ landscape
+ };
+ const result = unmarshalPageImage(json as {});
+ assert.deepStrictEqual(result, expected);
+ });
+ });
+ });
+
+ describe(".unmarshalPageSummary()", () => {
+ // eslint-disable-next-line max-len
+ it("unmarshals omitting undefined properties and returns extract even when
there are no paragraphs", () => {
+ const json: RESTBase.PageSummary.PageSummary = {
+ title: "title",
+ displaytitle: "displaytitle",
+ pageid: 1,
+ extract: "extract",
+ extract_html: "extract_html",
+ lang: "en",
+ dir: "ltr",
+ timestamp: NOW,
+ description: "description"
+ };
+ const expected: PageSummary = {
+ pageID: 1,
+ titleID: "titleID",
+ titleText: "title",
+ titleHTML: "displaytitle",
+ descriptionText: "description",
+ lastModified: new Date(Date.parse(NOW)),
+ etag: EXPECTED_ETAG,
+ wikiLanguageCode: "en",
+ localeDirection: "ltr",
+ extractText: "extract",
+ extractHTML: ["extract_html"]
+ };
+ const result = unmarshalPageSummary({
+ url: "titleID",
+ headers: HEADERS,
+ json: json as {}
+ });
+ assert.deepStrictEqual(result, expected);
+ });
+
+ it("unmarshals a server response", () => {
+ const json = require("./page-summary-restbase.test.json");
+ const result = unmarshalPageSummary({
+ requestTitleID: "mount_everest",
+ url: "https://en.wikipedia.org/api/rest_v1/page/summary/Mount_Everest",
+ headers: HEADERS,
+ json
+ });
+ const expected = reviveFile(
+ "./page-summary-expected.test.json",
+ pageSummaryReviver
+ );
+ assert.deepStrictEqual(result, expected);
+ });
+ });
+});
diff --git a/src/common/marshallers/page-summary-unmarshaller.ts
b/src/common/marshallers/page-summary-unmarshaller.ts
new file mode 100644
index 0000000..2b2b206
--- /dev/null
+++ b/src/common/marshallers/page-summary-unmarshaller.ts
@@ -0,0 +1,110 @@
+import { PageImage, PageThumbnail } from "../models/page/image";
+import { PageSummary } from "../models/page/summary";
+import { PageTitleID } from "../models/page/title";
+import { IsomorphicHeaders } from "../types/isomorphic-unfetch-extras";
+import { JSONObject } from "../types/json";
+import {
+ unmarshalPageTitleID,
+ unmarshalPageGeolocation,
+ unmarshalETag
+} from "./page-base-unmarshaller";
+import { RESTBase } from "./restbase";
+
+export const unmarshalPageThumbnail = (json: JSONObject): PageThumbnail => {
+ const type: RESTBase.PageSummary.Thumbnail = json as any;
+ return {
+ url: type.source,
+ originalURL: type.original,
+ width: type.width,
+ height: type.height,
+ landscape: type.width > type.height
+ };
+};
+
+export const unmarshalPageImage = (json: JSONObject): PageImage => {
+ const type: RESTBase.PageSummary.Image = json as any;
+ return {
+ url: type.source,
+ width: type.width,
+ height: type.height,
+ landscape: type.width > type.height
+ };
+};
+
+// todo: remove this function and Domino package from package*.json, the
+// corresponding Webpack IgnorePlugin, and the Domino typing, when
+// isomorphic HTML parsing is unneeded. Some other options explored:
+// - undom advertises itself as "DOM but not a parser". Parsing, serialization,
+// and selectors are all unsupported. There is a recipe for rudimentary
+// serialization but the real issue is the lack of a parser which is
essential
+// to Marvin's use case. The GitHub issues have several discussions of an
+// extensible plugin system that may help fill in these gaps but to date
these
+// features are unavailable.
+// - This parsing logic could live in the Preact components render cycle but:
+// - Rendering in a component will still require a DOM on the server.
+// - The expected place to receive this data is at unmarshalling time.
+// - Only Domino was considered. jsdom and other alternatives were not
explored.
+// - A new Marvin service endpoint gives the project a great deal of
flexibility
+// in how it handles server responses. However, it comes at the great expense
+// of increased complexity and reduced performance:
+// - Marvin already offers client and server code. Adding a service API is a
+// refactor that decreases code clarity.
+// - Requests from the server and the client must be issued to Marvin then
+// reissued to the real data endpoints.
+// - Example implementation: https://gerrit.wikimedia.org/r/#/c/379698/4.
+// Domino was chosen for familiarity and because it's already used on the MCS
+// backend. This is a service-only dependency so the client filesize is not
+// affected at the expense of two different code paths.
+const parseExtractHTML = (extractHTML: string) => {
+ const element =
+ typeof document === "undefined"
+ ? require("domino").createDocument().body
+ : document.implementation.createHTMLDocument("").body;
+ element.innerHTML = extractHTML;
+
+ const paragraphs = Array.from(element.querySelectorAll("p"));
+ return paragraphs.length
+ ? paragraphs.map((paragraph: HTMLParagraphElement) => paragraph.outerHTML)
+ : [extractHTML];
+};
+
+export const unmarshalPageSummary = ({
+ url,
+ requestTitleID,
+ headers,
+ json
+}: {
+ url: string;
+ requestTitleID?: PageTitleID | string;
+ headers: IsomorphicHeaders;
+ json: JSONObject;
+}): PageSummary => {
+ const type: RESTBase.PageSummary.PageSummary = json as any;
+ const result: PageSummary = {
+ pageID: type.pageid,
+ titleID: unmarshalPageTitleID(url),
+ titleText: type.title,
+ titleHTML: type.displaytitle,
+ lastModified: new Date(type.timestamp),
+ descriptionText: type.description,
+ etag: unmarshalETag(headers),
+
+ wikiLanguageCode: type.lang,
+ localeDirection: type.dir,
+ extractText: type.extract,
+ extractHTML: parseExtractHTML(type.extract_html)
+ };
+ if (requestTitleID !== undefined) {
+ result.requestTitleID = requestTitleID;
+ }
+ if (type.coordinates) {
+ result.geolocation = unmarshalPageGeolocation(type.coordinates as {});
+ }
+ if (type.thumbnail) {
+ result.thumbnail = unmarshalPageThumbnail(type.thumbnail as {});
+ }
+ if (type.originalimage) {
+ result.image = unmarshalPageImage(type.originalimage as {});
+ }
+ return result;
+};
diff --git a/src/common/marshallers/page-unmarshaller.test.ts
b/src/common/marshallers/page-unmarshaller.test.ts
index 9904460..2e54284 100644
--- a/src/common/marshallers/page-unmarshaller.test.ts
+++ b/src/common/marshallers/page-unmarshaller.test.ts
@@ -1,9 +1,6 @@
import * as assert from "assert";
-import * as fetch from "node-fetch";
-import { JSONValue } from "../types/json";
-import { PageSummary, pageSummaryReviver } from "../models/page/summary";
-import { PageImage, PageThumbnail } from "../models/page/image";
-import { PageGeolocation } from "../models/page/geolocation";
+
+import { PageImage } from "../models/page/image";
import { PageNamespace } from "../models/page/namespace";
import {
Page,
@@ -15,82 +12,21 @@
} from "../models/page/page";
import { PageUser, PageUserGender } from "../models/page/user";
import {
- unmarshalETag,
unmarshalPage,
unmarshalPageLead,
unmarshalPageBody,
- unmarshalPageGeolocation,
- unmarshalPageImage,
unmarshalPageImageMap,
- unmarshalPageThumbnail,
unmarshalPageSection,
unmarshalPageSections,
- unmarshalPageSummary,
unmarshalPageUser,
unmarshalPageUserGender
} from "./page-unmarshaller";
import { RESTBase } from "./restbase";
-
-const revive = (
- filename: string,
- reviver?: (key: any, value: JSONValue) => any
-) => JSON.parse(JSON.stringify(require(filename)), reviver);
+import { EXPECTED_ETAG, HEADERS, reviveFile } from "./utils.test";
const NOW = new Date(Date.now()).toString();
-const ETAG_REVISION = 802006980;
-const ETAG_TIME_ID = "4f754377-a235-11e7-a776-efb84f18649a";
-const HEADERS = new fetch.Headers();
-HEADERS.append("etag", `${ETAG_REVISION}/${ETAG_TIME_ID}`);
-const EXPECTED_ETAG = { revision: ETAG_REVISION, timeID: ETAG_TIME_ID };
-
describe("page-unmarshaller", () => {
- describe(".unmarshalPageThumbnail()", () => {
- [false, true].forEach(landscape => {
- it(`unmarshals ${landscape ? "landscape" : "portrait"}`, () => {
- const width = landscape ? 2 : 1;
- const height = landscape ? 1 : 2;
- const json: RESTBase.PageSummary.Thumbnail = {
- source: "source",
- original: "original",
- width,
- height
- };
- const expected: PageThumbnail = {
- url: "source",
- width,
- height,
- landscape,
- originalURL: "original"
- };
- const result = unmarshalPageThumbnail(json as {});
- assert.deepStrictEqual(result, expected);
- });
- });
- });
-
- describe(".unmarshalPageImage()", () => {
- [false, true].forEach(landscape => {
- it(`unmarshals ${landscape ? "landscape" : "portrait"}`, () => {
- const width = landscape ? 2 : 1;
- const height = landscape ? 1 : 2;
- const json: RESTBase.PageSummary.Image = {
- source: "source",
- width,
- height
- };
- const expected: PageImage = {
- url: "source",
- width,
- height,
- landscape
- };
- const result = unmarshalPageImage(json as {});
- assert.deepStrictEqual(result, expected);
- });
- });
- });
-
describe(".unmarshalPageImageMap()", () => {
it("unmarshals an empty map", () => {
const json: RESTBase.PageSections.ThumbnailMap = {
@@ -112,75 +48,6 @@
{ url: "2", width: 2 }
];
const result = unmarshalPageImageMap(json as {});
- assert.deepStrictEqual(result, expected);
- });
- });
-
- it(".unmarshalPageGeolocation() unmarshals", () => {
- const json: RESTBase.PageSummary.Geolocation = {
- lat: 1,
- lon: 2
- };
- const expected: PageGeolocation = {
- latitude: 1,
- longitude: 2
- };
- const result = unmarshalPageGeolocation(json as {});
- assert.deepStrictEqual(result, expected);
- });
-
- it(".unmarshalETag() unmarshals", () => {
- const result = unmarshalETag(HEADERS);
- assert.deepStrictEqual(result, EXPECTED_ETAG);
- });
-
- describe(".unmarshalPageSummary()", () => {
- // eslint-disable-next-line max-len
- it("unmarshals omitting undefined properties and returns extract even when
there are no paragraphs", () => {
- const json: RESTBase.PageSummary.PageSummary = {
- title: "title",
- displaytitle: "displaytitle",
- pageid: 1,
- extract: "extract",
- extract_html: "extract_html",
- lang: "en",
- dir: "ltr",
- timestamp: NOW,
- description: "description"
- };
- const expected: PageSummary = {
- pageID: 1,
- titleID: "titleID",
- titleText: "title",
- titleHTML: "displaytitle",
- descriptionText: "description",
- lastModified: new Date(Date.parse(NOW)),
- etag: EXPECTED_ETAG,
- wikiLanguageCode: "en",
- localeDirection: "ltr",
- extractText: "extract",
- extractHTML: ["extract_html"]
- };
- const result = unmarshalPageSummary({
- url: "titleID",
- headers: HEADERS,
- json: json as {}
- });
- assert.deepStrictEqual(result, expected);
- });
-
- it("unmarshals a server response", () => {
- const json = require("./page-summary-restbase.test.json");
- const result = unmarshalPageSummary({
- requestTitleID: "mount_everest",
- url: "https://en.wikipedia.org/api/rest_v1/page/summary/Mount_Everest",
- headers: HEADERS,
- json
- });
- const expected = revive(
- "./page-summary-expected.test.json",
- pageSummaryReviver
- );
assert.deepStrictEqual(result, expected);
});
});
@@ -353,7 +220,7 @@
headers: HEADERS,
json
});
- const expected = revive(
+ const expected = reviveFile(
"./page-lead-expected.test.json",
pageLeadReviver
);
@@ -393,7 +260,7 @@
it("unmarshals a server response", () => {
const json = require("./page-body-restbase.test.json");
const result = unmarshalPageBody(json);
- const expected = revive("./page-body-expected.test.json");
+ const expected = reviveFile("./page-body-expected.test.json");
assert.deepStrictEqual(result, expected);
});
});
@@ -455,7 +322,7 @@
headers: HEADERS,
json
});
- const expected = revive("./page-expected.test.json", pageReviver);
+ const expected = reviveFile("./page-expected.test.json", pageReviver);
assert.deepStrictEqual(result, expected);
});
});
diff --git a/src/common/marshallers/page-unmarshaller.ts
b/src/common/marshallers/page-unmarshaller.ts
index f088562..f19e859 100644
--- a/src/common/marshallers/page-unmarshaller.ts
+++ b/src/common/marshallers/page-unmarshaller.ts
@@ -1,34 +1,15 @@
-import { PageImage, PageThumbnail } from "../models/page/image";
-import { ETag } from "../models/etag";
-import { PageGeolocation } from "../models/page/geolocation";
-import { PageSummary } from "../models/page/summary";
+import { PageImage } from "../models/page/image";
import { PageTitleID } from "../models/page/title";
import { Page, PageLead, PageBody, PageSection } from "../models/page/page";
import { PageUser, PageUserGender } from "../models/page/user";
import { IsomorphicHeaders } from "../types/isomorphic-unfetch-extras";
import { JSONArray, JSONObject } from "../types/json";
+import {
+ unmarshalPageTitleID,
+ unmarshalPageGeolocation,
+ unmarshalETag
+} from "./page-base-unmarshaller";
import { RESTBase } from "./restbase";
-
-export const unmarshalPageThumbnail = (json: JSONObject): PageThumbnail => {
- const type: RESTBase.PageSummary.Thumbnail = json as any;
- return {
- url: type.source,
- originalURL: type.original,
- width: type.width,
- height: type.height,
- landscape: type.width > type.height
- };
-};
-
-export const unmarshalPageImage = (json: JSONObject): PageImage => {
- const type: RESTBase.PageSummary.Image = json as any;
- return {
- url: type.source,
- width: type.width,
- height: type.height,
- landscape: type.width > type.height
- };
-};
export const unmarshalPageImageMap = (json: JSONObject): PageImage[] => {
const type: RESTBase.PageSections.ThumbnailMap = json as any;
@@ -38,111 +19,6 @@
url: type.urls[width],
width: parseInt(width, 10)
}));
-};
-
-export const unmarshalPageGeolocation = (json: JSONObject): PageGeolocation =>
{
- if (json.latitude) {
- const type: RESTBase.PageSections.Geolocation = json as any;
- return { latitude: type.latitude, longitude: type.longitude };
- } else {
- const type: RESTBase.PageSummary.Geolocation = json as any;
- return { latitude: type.lat, longitude: type.lon };
- }
-};
-
-// todo: remove this function and Domino package from package*.json, the
-// corresponding Webpack IgnorePlugin, and the Domino typing, when
-// isomorphic HTML parsing is unneeded. Some other options explored:
-// - undom advertises itself as "DOM but not a parser". Parsing, serialization,
-// and selectors are all unsupported. There is a recipe for rudimentary
-// serialization but the real issue is the lack of a parser which is
essential
-// to Marvin's use case. The GitHub issues have several discussions of an
-// extensible plugin system that may help fill in these gaps but to date
these
-// features are unavailable.
-// - This parsing logic could live in the Preact components render cycle but:
-// - Rendering in a component will still require a DOM on the server.
-// - The expected place to receive this data is at unmarshalling time.
-// - Only Domino was considered. jsdom and other alternatives were not
explored.
-// - A new Marvin service endpoint gives the project a great deal of
flexibility
-// in how it handles server responses. However, it comes at the great expense
-// of increased complexity and reduced performance:
-// - Marvin already offers client and server code. Adding a service API is a
-// refactor that decreases code clarity.
-// - Requests from the server and the client must be issued to Marvin then
-// reissued to the real data endpoints.
-// - Example implementation: https://gerrit.wikimedia.org/r/#/c/379698/4.
-// Domino was chosen for familiarity and because it's already used on the MCS
-// backend. This is a service-only dependency so the client filesize is not
-// affected at the expense of two different code paths.
-const parseExtractHTML = (extractHTML: string) => {
- const element =
- typeof document === "undefined"
- ? require("domino").createDocument().body
- : document.implementation.createHTMLDocument("").body;
- element.innerHTML = extractHTML;
-
- const paragraphs = Array.from(element.querySelectorAll("p"));
- return paragraphs.length
- ? paragraphs.map((paragraph: HTMLParagraphElement) => paragraph.outerHTML)
- : [extractHTML];
-};
-
-export const unmarshalETag = (headers: IsomorphicHeaders): ETag => {
- const etag = headers.get("ETag") as string;
- const [revision, timeID] = etag.split("/");
- return { revision: parseInt(revision, 10), timeID };
-};
-
-export function unmarshalPageTitleID(url: string): PageTitleID {
- // Titles themselves may contain slashes, however, RESTBase only understands
- // titles with encoded slashes so when this unmarshaller encounters a slash
it
- // may safely consider this a path segment and not part of the title.
- const titlePath = url.split("/").pop();
- if (titlePath === undefined) {
- throw new Error("titlePath should be known at response time.");
- }
- return decodeURI(titlePath);
-}
-
-export const unmarshalPageSummary = ({
- url,
- requestTitleID,
- headers,
- json
-}: {
- url: string;
- requestTitleID?: PageTitleID | string;
- headers: IsomorphicHeaders;
- json: JSONObject;
-}): PageSummary => {
- const type: RESTBase.PageSummary.PageSummary = json as any;
- const result: PageSummary = {
- pageID: type.pageid,
- titleID: unmarshalPageTitleID(url),
- titleText: type.title,
- titleHTML: type.displaytitle,
- lastModified: new Date(type.timestamp),
- descriptionText: type.description,
- etag: unmarshalETag(headers),
-
- wikiLanguageCode: type.lang,
- localeDirection: type.dir,
- extractText: type.extract,
- extractHTML: parseExtractHTML(type.extract_html)
- };
- if (requestTitleID !== undefined) {
- result.requestTitleID = requestTitleID;
- }
- if (type.coordinates) {
- result.geolocation = unmarshalPageGeolocation(type.coordinates as {});
- }
- if (type.thumbnail) {
- result.thumbnail = unmarshalPageThumbnail(type.thumbnail as {});
- }
- if (type.originalimage) {
- result.image = unmarshalPageImage(type.originalimage as {});
- }
- return result;
};
export const unmarshalPageUserGender = (json: string): PageUserGender => {
diff --git a/src/common/marshallers/utils.test.ts
b/src/common/marshallers/utils.test.ts
new file mode 100644
index 0000000..9b35ebe
--- /dev/null
+++ b/src/common/marshallers/utils.test.ts
@@ -0,0 +1,13 @@
+import * as fetch from "node-fetch";
+import { JSONValue } from "../types/json";
+
+export const reviveFile = (
+ filename: string,
+ reviver?: (key: any, value: JSONValue) => any
+) => JSON.parse(JSON.stringify(require(filename)), reviver);
+
+const ETAG_REVISION = 802006980;
+const ETAG_TIME_ID = "4f754377-a235-11e7-a776-efb84f18649a";
+export const HEADERS = new fetch.Headers();
+HEADERS.append("etag", `${ETAG_REVISION}/${ETAG_TIME_ID}`);
+export const EXPECTED_ETAG = { revision: ETAG_REVISION, timeID: ETAG_TIME_ID };
--
To view, visit https://gerrit.wikimedia.org/r/387234
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I771a4891297ca2edccfc3057bdef4500311a2f42
Gerrit-PatchSet: 1
Gerrit-Project: marvin
Gerrit-Branch: master
Gerrit-Owner: Niedzielski <[email protected]>
Gerrit-Reviewer: Sniedzielski <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits