BearND has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/296266

Change subject: Picture of the day
......................................................................

Picture of the day

http://localhost:6927/en.wikipedia.org/v1/media/image/featured/2016/04/15

Bug: T132765
Change-Id: I45148c7915abab9cc1a1d84c8aa5e178c668c169
---
M lib/dateUtil.js
A lib/feed/featured-image.js
M lib/mobile-util.js
A routes/featured-image.js
M spec.yaml
A test/features/featured-image/pagecontent.js
6 files changed, 287 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/mobileapps 
refs/changes/66/296266/1

diff --git a/lib/dateUtil.js b/lib/dateUtil.js
index fc8d7d0..2ac33ac 100644
--- a/lib/dateUtil.js
+++ b/lib/dateUtil.js
@@ -51,6 +51,21 @@
     return number;
 }
 
+/**
+ * Returns a String formatted in ISO date format -- just the date. Timezone is 
UTC.
+ * This is similar to Date.toISOString() but without the time and time zone 
portions.
+ *
+ * Example: "2016-05-16"
+ *
+ * @param {Date} date date to be used
+ * @return {String} formatted date string
+ */
+function formatISODate(date) {
+    return date.getUTCFullYear() +
+    '-' + pad(date.getUTCMonth() + 1) +
+    '-' + pad(date.getUTCDate());
+}
+
 function yesterday(req) {
     var date = getRequestedDate(req),
         yesterday = new Date(date - ONE_DAY);
@@ -65,6 +80,7 @@
     getRequestedDate: getRequestedDate,
     dateStringFrom: dateStringFrom,
     iso8601DateFrom: iso8601DateFrom,
+    formatISODate: formatISODate,
     pad: pad,
     yesterday: yesterday,
     ONE_DAY: ONE_DAY
diff --git a/lib/feed/featured-image.js b/lib/feed/featured-image.js
new file mode 100644
index 0000000..6b6367f
--- /dev/null
+++ b/lib/feed/featured-image.js
@@ -0,0 +1,103 @@
+/**
+ * To retrieve the picture of the day for a given date.
+ */
+
+'use strict';
+
+var preq = require('preq');
+var api = require('../api-util');
+var dateUtil = require('../dateUtil');
+var mwapi = require('../mwapi');
+var mUtil = require('../mobile-util');
+var sUtil = require('../util');
+var HTTPError = sUtil.HTTPError;
+
+
+/**
+ * Builds the request to get the Picture of the day of a given date.
+ *
+ * @param {Object} app the application object
+ * @param {String} domain the requested domain, e.g. 'de.wikipedia.org'
+ * @param {Date} date for which day the featured image of theday is requested
+ * @return {Promise} a promise resolving as an JSON object containing the 
response
+ */
+function requestPictureOfTheDay(app, domain, date) {
+    var isoDate = dateUtil.formatISODate(date);
+    var lang = mUtil.getLanguageFromDomain(domain);
+    return api.mwApiGet(app, 'commons.wikimedia.org', {
+        action: 'query',
+        format: 'json',
+        formatversion: 2,
+        generator: 'images',
+        prop: 'imageinfo|revisions',
+        iiextmetadatafilter: 'ImageDescription',
+        iiextmetadatalanguage: lang,
+        iiprop: 'url|extmetadata|dimensions',
+        iiurlwidth: 640,
+        rawcontinue: '',
+        titles: `Template:Potd/${isoDate}_(${lang})`
+    });
+}
+
+// -- functions dealing with responses:
+
+function getPageObject(response, dontThrow) {
+    if (response.body.query && response.body.query.pages[0]) {
+        var page = response.body.query.pages[0];
+        if (!page.pageid || page.missing === true) {
+            throw new HTTPError({
+                status: 404,
+                type: 'not_found',
+                title: 'No picture of the day for this date',
+                detail: 'There is no picture of the day for this date.'
+            });
+        }
+        return page;
+    } else {
+        if (!dontThrow) {
+            throw new HTTPError({
+                status: 500,
+                type: 'unknown_backend_response',
+                title: 'Unexpected backend response',
+                detail: 'The backend responded with gibberish.'
+            });
+        }
+    }
+}
+
+function buildPotdResponse(page) {
+    let imageinfo = page.imageinfo && page.imageinfo[0];
+    return {
+        title: page.title,
+        thumbnail: {
+            source: imageinfo && imageinfo.thumburl,
+            width: imageinfo && imageinfo.thumbwidth,
+            height: imageinfo && imageinfo.thumbheight
+        },
+        // the full res image:
+        image: {
+            source: imageinfo.url,
+            width: imageinfo.width,
+            height: imageinfo.height
+        },
+        description: imageinfo.extmetadata && 
imageinfo.extmetadata.ImageDescription.value
+    };
+}
+
+function promise(app, req) {
+    return requestPictureOfTheDay(app, req.params.domain, 
dateUtil.getRequestedDate(req))
+    .then(function (response) {
+        mwapi.checkForQueryPagesInResponse(req, response);
+        var page = getPageObject(response);
+        return {
+            payload: buildPotdResponse(page),
+            meta: {
+                etag: page.pageid + '/' + mwapi.getRevisionFromExtract(page)
+            }
+        };
+    });
+}
+
+module.exports = {
+    promise: promise
+};
diff --git a/lib/mobile-util.js b/lib/mobile-util.js
index 5f079ed..105081c 100644
--- a/lib/mobile-util.js
+++ b/lib/mobile-util.js
@@ -87,6 +87,14 @@
     return domain.split('.').slice(0,2).join('.');
 }
 
+/**
+ * Get the language of the wiki base don the domain name.
+ * Example: 'en.wikipedia.org' -> 'en'.
+ */
+function getLanguageFromDomain(domain) {
+    return domain.split('.')[0];
+}
+
 // Merge two arrays of objects by the specified property.
 // Stolen from https://jsfiddle.net/guya/eAWKR/.
 function mergeByProp(arr1, arr2, prop, pushIfKeyNotFound) {
@@ -177,6 +185,7 @@
     getDateStringEtag: getDateStringEtag,
     mobileToCanonical: mobileToCanonical,
     removeTLD: removeTLD,
+    getLanguageFromDomain: getLanguageFromDomain,
     mergeByProp: mergeByProp,
     adjustMemberKeys: adjustMemberKeys,
     fillInMemberKeys: fillInMemberKeys,
diff --git a/routes/featured-image.js b/routes/featured-image.js
new file mode 100644
index 0000000..60b1263
--- /dev/null
+++ b/routes/featured-image.js
@@ -0,0 +1,42 @@
+/**
+ * Picture of the day
+ */
+
+'use strict';
+
+var mUtil = require('../lib/mobile-util');
+var sUtil = require('../lib/util');
+var featured = require('../lib/feed/featured-image');
+
+/**
+ * The main router object
+ */
+var router = sUtil.router();
+
+/**
+ * The main application object reported when this module is require()d
+ */
+var app;
+
+/**
+ * GET {domain}/v1/media/image/featured/{year}/{month}/{day}
+ * Gets the title and other metadata for the picture of the day of a given 
date.
+ * ETag is set to the pageid and the revision.
+ */
+router.get('/image/featured/:yyyy/:mm/:dd', function (req, res) {
+    return featured.promise(app, req)
+        .then(function (response) {
+            res.status(200);
+            mUtil.setETagToValue(res, response.meta.etag);
+            res.json(response.payload).end();
+        });
+});
+
+module.exports = function (appObj) {
+    app = appObj;
+    return {
+        path: '/media',
+        api_version: 1,
+        router: router
+    };
+};
diff --git a/spec.yaml b/spec.yaml
index 308112a..19ce2a4 100644
--- a/spec.yaml
+++ b/spec.yaml
@@ -173,6 +173,59 @@
                 source: /.+/
                 width: /.+/
                 height: /.+/
+  # from routes/featured-image.js
+  /{domain}/v1/media/image/featured/{yyyy}/{mm}/{dd}:
+    get:
+      tags:
+        - Featured image for a given date (aka Picture of the day)
+      description: Provides thumbnail and full res image URLs and a localized 
description based on the domain used.
+      produces:
+        - application/json
+      parameters:
+        - name: yyyy
+          in: path
+          description: "Year the featured image is requested for"
+          type: integer
+          required: true
+          minimum: 2016
+          maximum: 2999
+        - name: mm
+          in: path
+          description: "Month the featured image is requested for"
+          type: integer
+          required: true
+          minimum: 1
+          maximum: 12
+        - name: dd
+          in: path
+          description: "Day of the month the featured image is requested for"
+          type: integer
+          required: true
+          minimum: 1
+          maximum: 31
+      x-amples:
+        - title: retrieve info of the featured image for April 29, 2016
+          request:
+            params:
+              yyyy: 2016
+              mm: 4
+              dd: 29
+          response:
+            status: 200
+            headers:
+              content-type: application/json
+            body:
+              title: /.+/
+              description: /.+/
+              image:
+                source: /.+/
+                width: /.+/
+                height: /.+/
+              thumbnail:
+                source: /.+/
+                width: /.+/
+                height: /.+/
+
   # from routes/most-read.js
   /{domain}/v1/page/most-read/{yyyy}/{mm}/{dd}:
     get:
diff --git a/test/features/featured-image/pagecontent.js 
b/test/features/featured-image/pagecontent.js
new file mode 100644
index 0000000..c24d156
--- /dev/null
+++ b/test/features/featured-image/pagecontent.js
@@ -0,0 +1,64 @@
+'use strict';
+
+var assert = require('../../utils/assert.js');
+var preq   = require('preq');
+var server = require('../../utils/server.js');
+var headers = require('../../utils/headers.js');
+
+describe('featured-image', function() {
+    this.timeout(20000);
+
+    before(function () { return server.start(); });
+
+    it('featured image of a specific date should respond to GET request with 
expected headers, incl. CORS and CSP headers', function() {
+        return headers.checkHeaders(server.config.uri + 
'en.wikipedia.org/v1/media/image/featured/2016/04/15',
+            'application/json');
+    });
+
+    it('featured image of 4/15/2016 should have expected properties', 
function() {
+        return preq.get({ uri: server.config.uri + 
'en.wikipedia.org/v1/media/image/featured/2016/04/15' })
+            .then(function(res) {
+                assert.status(res, 200);
+                // the page id should be stable but not the revision:
+                assert.ok(res.headers.etag.indexOf('42184395/') == 0);
+                assert.equal(res.body.title, 'File:Iglesia de La Compañía, 
Quito, Ecuador, 2015-07-22, DD 116-118 HDR.JPG');
+                assert.equal(res.body.thumbnail.source, 
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Iglesia_de_La_Compa%C3%B1%C3%ADa%2C_Quito%2C_Ecuador%2C_2015-07-22%2C_DD_116-118_HDR.JPG/640px-Iglesia_de_La_Compa%C3%B1%C3%ADa%2C_Quito%2C_Ecuador%2C_2015-07-22%2C_DD_116-118_HDR.JPG');
+                assert.equal(res.body.image.source, 
'https://upload.wikimedia.org/wikipedia/commons/e/e3/Iglesia_de_La_Compa%C3%B1%C3%ADa%2C_Quito%2C_Ecuador%2C_2015-07-22%2C_DD_116-118_HDR.JPG');
+                assert.ok(res.body.description.indexOf('Main altar') >= 0);
+            });
+    });
+
+    it('incomplete date should return 404', function() {
+        return preq.get({ uri: server.config.uri + 
'en.wikipedia.org/v1/media/image/featured/2016/04' })
+            .then(function(res) {
+            }, function(err) {
+                assert.status(err, 404);
+            });
+    });
+
+    it('extra uri path parameter after date should return 404', function() {
+        return preq.get({ uri: server.config.uri + 
'en.wikipedia.org/v1/media/image/featured/2016/04/15/11' })
+            .then(function(res) {
+            }, function(err) {
+                assert.status(err, 404);
+            });
+    });
+
+    it('unsupported language', function() {
+        return preq.get({ uri: server.config.uri + 
'fr.wikipedia.org/v1/media/image/featured/2016/04/15' })
+            .then(function(res) {
+            }, function(err) {
+                assert.status(err, 501);
+                assert.equal(err.body.type, 'unsupported_language');
+            });
+    });
+
+    it('featured image of an old date should return 404', function() {
+        return preq.get({ uri: server.config.uri + 
'en.wikipedia.org/v1/media/image/featured/1970/12/31' })
+            .then(function(res) {
+            }, function(err) {
+                assert.status(err, 404);
+                assert.equal(err.body.type, 'not_found');
+            });
+    });
+});

-- 
To view, visit https://gerrit.wikimedia.org/r/296266
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I45148c7915abab9cc1a1d84c8aa5e178c668c169
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/services/mobileapps
Gerrit-Branch: master
Gerrit-Owner: BearND <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to