Skip to content

Commit

Permalink
Issue mozilla#39 - Add Nimbledroid endpoint
Browse files Browse the repository at this point in the history
* Add documentation to enable Nimbledroid fetching
* Heroku review app configuration changes to copy Nimbledroid env variables
* Add /api/android/nimbledroid endpoint
* Add central configuration file
* Add Nimbledroid tests
  • Loading branch information
armenzg committed Jul 10, 2018
1 parent 9f5a9aa commit d06327e
Show file tree
Hide file tree
Showing 7 changed files with 1,094 additions and 0 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ GOOGLE_API_KEY=<created API key> yarn start
```
* Visit http://localhost:3000/api/perf/notes to verify it works

### Providing a Nimbledroid API key
Nimbledroid provides us with performance data for various sites on Android.
If you want to make changes to the Nimbledroid APIs on the backend you will need
to have access to our corporate Nimbledroid account.

Once you have access you can fetch your personal key (keep private) under your
[account](https://nimbledroid.com/account). You can re-generate it there if it ever gets leaked.

Once you have it you can start the backend like this:

```
export NIMBLEDROID_API_KEY=<API key>
export NIMBLEDROID_EMAIL=<your email address>
yarn start
```

Load http://localhost:3000/api/nimbledroid to verify it works.

## Attributions

- heartbeat icon by Creative Stall from the Noun Project
6 changes: 6 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
"GOOGLE_API_KEY": {
"required": true
},
"NIMBLEDROID_API_KEY": {
"required": true
},
"NIMBLEDROID_EMAIL": {
"required": true
},
"NPM_CONFIG_PRODUCTION": {
"required": true
},
Expand Down
22 changes: 22 additions & 0 deletions src/android/routes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Router from 'koa-router';
import NimbledroidClient from '../utils/NimbledroidClient';
import { getSpreadsheetValues } from '../utils/google';
import config from '../configuration';

Expand Down Expand Up @@ -26,4 +27,25 @@ router
entry.klar = parseFloat(entry.klar);
});
ctx.body = list;
})
.get('/nimbledroid', async (ctx) => {
if (!process.env.NIMBLEDROID_API_KEY || !process.env.NIMBLEDROID_EMAIL) {
ctx.throw(
400,
'You need to set Nimbledroid authentication for this endpoint to work. More info in ' +
'https://github.com/mozilla/firefox-health-backend/blob/master/README.md',
);
}
const { product } = ctx.request.query;
if (!product) {
ctx.throw(
400,
'You need to call this endpoint with ?product=<klar|focus>.',
);
}
const handler = new NimbledroidClient(
process.env.NIMBLEDROID_EMAIL,
process.env.NIMBLEDROID_API_KEY,
);
ctx.body = await handler.getNimbledroidData(product);
});
1 change: 1 addition & 0 deletions src/configuration.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const config = {
quantumSpreadsheetId: '1UMsy_sZkdgtElr2buwRtABuyA3GY6wNK_pfF01c890A',
androidSpreadsheetId: '1vE0b3tawWY9vVNq9Ds6CiA9XZ6LStkcXZl3F8dwXid8',
nimbledroidApiUrl: 'https://nimbledroid.com/api/v2/users/npark@mozilla.com/apps/org.mozilla',
repoUrl: 'https://github.com/mozilla/firefox-health-backend',
};

Expand Down
45 changes: 45 additions & 0 deletions src/utils/NimbledroidClient/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import fetchJson from '../../fetch/json';
import config from '../../configuration';

const apiUrl = product => `${config.nimbledroidApiUrl}.${product}/apks`;

const generateAuthKey = (email, apiKey) => (
Buffer.from(`${email}:${apiKey}`).toString('base64')
);

// It makes the data smaller (from 1.66mb to 1.23mb)
const removeConsoleMessage = nimbledroidData => (
nimbledroidData.map((run) => {
delete run.console_message;
return run;
})
);

class NimbledroidHandler {
constructor(apiKey, email) {
if (!apiKey || !email) {
throw Error('You need to instantiate with an apiKey and email address.');
}
this.apiKey = apiKey;
this.email = email;
}

async getNimbledroidData(product) {
return removeConsoleMessage(await this.fetchData(product));
}

async fetchData(product, buildId) {
return fetchJson(
apiUrl(product, buildId),
{ method: 'GET', headers: this.generateAuthHeaders() },
);
}

generateAuthHeaders() {
return ({
Authorization: `Basic ${generateAuthKey(this.email, this.apiKey)}`,
});
}
}

export default NimbledroidHandler;
40 changes: 40 additions & 0 deletions test/android/nimbledroid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* global afterEach describe, it */
import fetchMock from 'fetch-mock';
import superagent from 'supertest';
import app from '../../src/app';
import config from '../../src/configuration';

const KLAR_DATA = require('../mocks/nimbledroidKlar');

const request = () => superagent(app.listen());

describe('/android', () => {
const product = 'klar';
fetchMock.get(`${config.nimbledroidApiUrl}.${product}/apks`, KLAR_DATA);

describe('GET /api/android/nimbledroid/', () => {
it('should return 400', (done) => {
delete process.env.NIMBLEDROID_EMAIL;
request()
.get('/api/android/nimbledroid/')
.expect(400, done);
});

it('should return 400', (done) => {
request()
.get('/api/android/nimbledroid/')
.expect(400, done);
});

it('should return 200', (done) => {
request()
.get(`/api/android/nimbledroid/?product=${product}`)
.expect(200, done);
});

afterEach(() => {
process.env.NIMBLEDROID_EMAIL = 'nobody@moz.com';
process.env.NIMBLEDROID_API_KEY = 'foo_bar';
});
});
});

0 comments on commit d06327e

Please sign in to comment.