Joal has uploaded a new change for review.
https://gerrit.wikimedia.org/r/277784
Change subject: Add unique_devices endpoint to AQS
......................................................................
Add unique_devices endpoint to AQS
Change pageviews endpoint to facilitate other endpoint integration.
Add unique devices front and sys specifications and js code.
Bug: T129518
Change-Id: Ife9547679bfdbda8ccc1de47aede30290180ebc9
---
M config.test.yaml
M projects/aqs_default.yaml
A sys/unique-devices.js
A sys/unique-devices.yaml
M test/aqs_test_module.yaml
A test/features/unique-devices/unique-devices.js
M v1/pageviews.yaml
A v1/unique-devices.yaml
8 files changed, 353 insertions(+), 8 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/analytics/aqs
refs/changes/84/277784/1
diff --git a/config.test.yaml b/config.test.yaml
index 5e50bf2..f89f281 100644
--- a/config.test.yaml
+++ b/config.test.yaml
@@ -1,4 +1,4 @@
-# A synthetic domain to test pageviews module data
+# A synthetic domain to test aqs modules data
aqs.wikimedia.org: &analytics.wikimedia.org
x-modules:
/:
@@ -28,7 +28,7 @@
services:
- name: aqs
module: hyperswitch
- conf:
+ conf:
port: 7231
spec: *spec_root
salt: secret
diff --git a/projects/aqs_default.yaml b/projects/aqs_default.yaml
index 95c9b44..540b267 100644
--- a/projects/aqs_default.yaml
+++ b/projects/aqs_default.yaml
@@ -13,8 +13,10 @@
x-host-basePath: /api/rest_v1
x-modules:
- /:
+ /pageviews:
- path: v1/pageviews.yaml
+ /unique-devices:
+ - path: v1/unique-devices.yaml
/{api:sys}:
swagger: 2.0
@@ -28,3 +30,5 @@
conf: '{{options.table}}'
/pageviews:
- path: sys/pageviews.js
+ /unique-devices:
+ - path: sys/unique-devices.js
diff --git a/sys/unique-devices.js b/sys/unique-devices.js
new file mode 100644
index 0000000..b70fa5a
--- /dev/null
+++ b/sys/unique-devices.js
@@ -0,0 +1,108 @@
+'use strict';
+
+/**
+ * Unique devices API module
+ *
+ * This API serves pre-aggregated unique devices statistics from Cassandra
+ */
+
+var HyperSwitch = require('hyperswitch');
+var path = require('path');
+var HTTPError = HyperSwitch.HTTPError;
+var URI = HyperSwitch.URI;
+
+var aqsUtil = require('../lib/aqsUtil');
+
+var spec = HyperSwitch.utils.loadSpec(path.join(__dirname,
'unique-devices.yaml'));
+
+// Unique devices Service
+function UDVS(options) {
+ this.options = options;
+}
+
+
+var tables = {
+ project: 'unique-devices.per.project',
+};
+var tableURI = function(domain, tableName) {
+ return new URI([domain, 'sys', 'table', tableName, '']);
+};
+var tableSchemas = {
+ project: {
+ table: tables.project,
+ version: 2,
+ attributes: {
+ project: 'string',
+ site: 'string',
+ granularity: 'string',
+ // the hourly timestamp will be stored as YYYYMMDDHH
+ timestamp: 'string',
+ v: 'long'
+ },
+ index: [
+ { attribute: 'project', type: 'hash' },
+ { attribute: 'site', type: 'hash' },
+ { attribute: 'granularity', type: 'hash' },
+ { attribute: 'timestamp', type: 'range', order: 'asc' },
+ ]
+ }
+};
+
+
+UDVS.prototype.uniqueDevicesForProjects = function(hyper, req) {
+ var rp = req.params;
+
+ // dates are passed in as YYYYMMDD but we need the HH to match the loaded
data
+ // which was originally planned at hourly resolution, so we pass "fakeHour"
+ aqsUtil.validateStartAndEnd(rp, { fakeHour: true });
+
+ var dataRequest = hyper.get({
+ uri: tableURI(rp.domain, tables.project),
+ body: {
+ table: tables.project,
+ attributes: {
+ project: rp.project,
+ site: rp.site,
+ granularity: rp.granularity,
+ timestamp: { between: [rp.start, rp.end] },
+ }
+ }
+
+ }).catch(aqsUtil.notFoundCatcher);
+
+ return dataRequest.then(aqsUtil.normalizeResponse).then(function(res) {
+ if (res.body.items) {
+ res.body.items.forEach(function(item) {
+ // transform site to site-version
+ item['site-version'] = item.site;
+ delete item.site;
+ // transform v to unique-devices int
+ try {
+ item['unique-devices'] = parseInt(item.v, 10);
+ } catch (e) {
+ item['unique-devices'] = null;
+ }
+ delete item.v;
+ });
+ }
+ return res;
+ });
+};
+
+module.exports = function(options) {
+ var udvs = new UDVS(options);
+
+ return {
+ spec: spec,
+ operations: {
+ uniqueDevicesForProjects: udvs.uniqueDevicesForProjects.bind(udvs)
+ },
+ resources: [
+ {
+ // unique devices per project table
+ uri: '/{domain}/sys/table/' + tables.project,
+ body: tableSchemas.project,
+ }
+ ]
+ };
+};
diff --git a/sys/unique-devices.yaml b/sys/unique-devices.yaml
new file mode 100644
index 0000000..f296078
--- /dev/null
+++ b/sys/unique-devices.yaml
@@ -0,0 +1,5 @@
+paths:
+ /per-project/{project}/{site}/{granularity}/{start}/{end}:
+ get:
+ summary: query unique devices per project
+ operationId: uniqueDevicesForProjects
diff --git a/test/aqs_test_module.yaml b/test/aqs_test_module.yaml
index 557f010..46782a7 100644
--- a/test/aqs_test_module.yaml
+++ b/test/aqs_test_module.yaml
@@ -6,7 +6,7 @@
swagger: '2.0'
info:
version: 1.0.0-beta
- title: Wikimedia testing APIs
+ title: AQS testing APIs
x-is-api-root: true
paths:
/pageviews/insert-per-article-flat/{project}/{article}/{granularity}/{timestamp}/{views}:
@@ -94,3 +94,20 @@
day: '{{request.params.day}}'
articlesJSON: '{{request.body.articles}}'
x-monitor: false
+
+
/unique-devices/insert-per-project/{project}/{site}/{granularity}/{timestamp}/{v}:
+ post:
+ x-request-handler:
+ - put_to_storage:
+ request:
+ method: 'put'
+ uri: '/{domain}/sys/table/unique-devices.per.project/'
+ body:
+ table: 'unique-devices.per.project'
+ attributes:
+ project: '{{request.params.project}}'
+ site: '{{request.params.site}}'
+ granularity: '{{request.params.granularity}}'
+ timestamp: '{{request.params.timestamp}}'
+ v: '{{request.params.v}}'
+ x-monitor: false
diff --git a/test/features/unique-devices/unique-devices.js
b/test/features/unique-devices/unique-devices.js
new file mode 100644
index 0000000..3c1e5a9
--- /dev/null
+++ b/test/features/unique-devices/unique-devices.js
@@ -0,0 +1,82 @@
+'use strict';
+
+// mocha defines to avoid JSHint breakage
+/* global describe, it, before, beforeEach, afterEach */
+
+var assert = require('../../utils/assert.js');
+var preq = require('preq');
+var server = require('../../utils/server.js');
+
+
+describe('unique-devices endpoints', function () {
+ this.timeout(20000);
+
+ //Start server before running tests
+ before(function () { return server.start(); });
+
+ // NOTE: this tests using the projects/aqs_default.yaml config, so
+ // it doesn't know about the /metrics root like the prod config does
+ var endpoint =
'/unique-devices/en.wikipedia/all-sites/daily/19690101/19710101';
+
+ // Fake data insertion endpoints
+ var insertEndpoint =
'/unique-devices/insert-per-project/en.wikipedia/all-sites/daily/1970010100';
+
+ function fix(b, s, u) { return b.replace(s, s + u); }
+
+ // Test Endpoint
+
+ it('should return 400 when parameters are wrong', function () {
+ return preq.get({
+ uri: server.config.aqsURL + endpoint.replace('19710101',
'20150701000000')
+ }).catch(function(res) {
+ assert.deepEqual(res.status, 400);
+ });
+ });
+
+ it('should return 400 when start is before end', function () {
+ return preq.get({
+ uri: server.config.aqsURL + endpoint.replace('19690101',
'2016070100')
+ }).catch(function(res) {
+ assert.deepEqual(res.status, 400);
+ });
+ });
+
+ it('should return 400 when timestamp is invalid', function () {
+ return preq.get({
+ uri: server.config.aqsURL + endpoint.replace('19710101',
'2015022900')
+ }).catch(function(res) {
+ assert.deepEqual(res.status, 400);
+ });
+ });
+
+ // WARNING: the data created in this test is used exactly as created
+ // by the monitoring tests.
+ it('should return the expected aggregate data after insertion', function
() {
+ return preq.post({
+ uri: server.config.aqsURL + insertEndpoint + '/0'
+ }).then(function() {
+ return preq.get({
+ uri: server.config.aqsURL + endpoint
+ });
+ }).then(function(res) {
+ assert.deepEqual(res.body.items.length, 1);
+ assert.deepEqual(res.body.items[0]['unique-devices'], 0);
+ });
+ });
+
+ it('should parse the v column string into an int', function () {
+ return preq.post({
+ uri: server.config.aqsURL +
+ fix(insertEndpoint, 'en.wikipedia', '3') +
+ '/9007199254740991'
+ }).then(function() {
+ return preq.get({
+ uri: server.config.aqsURL +
+ fix(endpoint, 'en.wikipedia', '3')
+ });
+ }).then(function(res) {
+ assert.deepEqual(res.body.items.length, 1);
+ assert.deepEqual(res.body.items[0]['unique-devices'],
9007199254740991);
+ });
+ });
+});
diff --git a/v1/pageviews.yaml b/v1/pageviews.yaml
index 7fd3730..fd97016 100644
--- a/v1/pageviews.yaml
+++ b/v1/pageviews.yaml
@@ -11,7 +11,7 @@
name: Apache licence, v2
url: https://www.apache.org/licenses/LICENSE-2.0
paths:
- /pageviews/:
+ /:
get:
tags:
- Pageviews data
@@ -32,7 +32,7 @@
$ref: '#/definitions/problem'
x-monitor: false
-
/pageviews/per-article/{project}/{access}/{agent}/{article}/{granularity}/{start}/{end}:
+
/per-article/{project}/{access}/{agent}/{article}/{granularity}/{start}/{end}:
get:
tags:
- Pageviews data
@@ -95,7 +95,7 @@
uri:
/{domain}/sys/pageviews/per-article/{project}/{access}/{agent}/{article}/{granularity}/{start}/{end}
x-monitor: false
- /pageviews/aggregate/{project}/{access}/{agent}/{granularity}/{start}/{end}:
+ /aggregate/{project}/{access}/{agent}/{granularity}/{start}/{end}:
get:
tags:
- Pageviews data
@@ -176,7 +176,7 @@
timestamp: 1970010100
views: 0
- /pageviews/top/{project}/{access}/{year}/{month}/{day}:
+ /top/{project}/{access}/{year}/{month}/{day}:
get:
tags:
- Pageviews data
diff --git a/v1/unique-devices.yaml b/v1/unique-devices.yaml
new file mode 100644
index 0000000..ac885b7
--- /dev/null
+++ b/v1/unique-devices.yaml
@@ -0,0 +1,129 @@
+swagger: '2.0'
+info:
+ version: '1.0.0-beta'
+ title: Analytics Unique Devices API
+ description: Analytics Unique Devicess API
+ contact:
+ name: Analytics
+ email: [email protected]
+ url: https://www.mediawiki.org/wiki/Analytics
+ license:
+ name: Apache licence, v2
+ url: https://www.apache.org/licenses/LICENSE-2.0
+paths:
+ /{project}/{site-version}/{granularity}/{start}/{end}:
+ get:
+ tags:
+ - Unique devices data
+ description: |
+ Given a project and a date range, returns a timeseries of unique
devices counts. You need to specify a project, and can filter by site-version.
You can choose between daily and hourly granularity as well
+ Stability:
[experimental](https://www.mediawiki.org/wiki/API_versioning#Experimental)
+ produces:
+ - application/json
+ parameters:
+ - name: project
+ in: path
+ description: The name of any Wikimedia project formatted like
{language code}.{project name}, for example en.wikipedia. You may pass
en.wikipedia.org and the .org will be stripped off. For projects like commons
without language codes, use commons.wikimedia
+ type: string
+ required: true
+ - name: site-version
+ in: path
+ description: If you want to filter by site version, use one of
desktop-site, mobile-site. If you are interested in unique devices regardless
of site version, use or all-sites
+ type: string
+ enum: ['all-sites', 'desktop-site', 'mobile-site']
+ required: true
+ - name: granularity
+ in: path
+ description: The time unit for the response data. As of today, the
supported granularities for this endpoint are daily and monthly
+ type: string
+ enum: ['daily', 'monthly']
+ required: true
+ - name: start
+ in: path
+ description: The timestamp of the first day/month to include, in
YYYYMMDD format
+ type: string
+ required: true
+ - name: end
+ in: path
+ description: The timestamp of the last day/month to include, in
YYYYMMDD format
+ type: string
+ required: true
+ responses:
+ '200':
+ description: The list of values
+ schema:
+ $ref: '#/definitions/project'
+ default:
+ description: Error
+ schema:
+ $ref: '#/definitions/problem'
+ x-request-handler:
+ - get_from_backend:
+ request:
+ uri:
/{domain}/sys/unique-devices/per-project/{project}/{site-version}/{granularity}/{start}/{end}
+ x-monitor: true
+ x-amples:
+ - title: Get unique devices
+ request:
+ params:
+ domain: wikimedia.org
+ project: en.wikipedia
+ site-version: all-sites
+ granularity: daily
+ start: 19700101
+ end: 19700101
+ response:
+ status: 200
+ headers:
+ content-type: application/json
+ body:
+ items:
+ - project: en.wikipedia
+ site-version: all-sites
+ granularity: daily
+ timestamp: 19700101
+ views: 0
+
+definitions:
+ # A https://tools.ietf.org/html/draft-nottingham-http-problem
+ problem:
+ required:
+ - type
+ properties:
+ type:
+ type: string
+ title:
+ type: string
+ detail:
+ type: string
+ instance:
+ type: string
+
+ listing:
+ description: The result format for listings
+ required:
+ - items
+ properties:
+ items:
+ type: array
+ items:
+ type: string
+
+ project:
+ properties:
+ items:
+ type: array
+ items:
+ properties:
+ project:
+ type : string
+ site-version:
+ type : string
+ granularity:
+ type: string
+ timestamp:
+ # the hourly timestamp will be stored as YYYYMMDDHH
+ type: string
+ unique-devices:
+ type: integer
+ format: int64
--
To view, visit https://gerrit.wikimedia.org/r/277784
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ife9547679bfdbda8ccc1de47aede30290180ebc9
Gerrit-PatchSet: 1
Gerrit-Project: analytics/aqs
Gerrit-Branch: master
Gerrit-Owner: Joal <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits