96 lines
2.8 KiB
JavaScript
96 lines
2.8 KiB
JavaScript
import _ from 'lodash';
|
|
import debug from 'debug';
|
|
import dedent from 'dedent';
|
|
import fs from 'fs';
|
|
import goog from 'googleapis';
|
|
import { Observable } from 'rx';
|
|
|
|
import { timeCache, observeMethod } from './rx';
|
|
|
|
// one million!
|
|
const upperBound = 1000 * 1000;
|
|
const scope = 'https://www.googleapis.com/auth/analytics.readonly';
|
|
const pathToCred = process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
|
|
const log = debug('fcc:server:utils:about');
|
|
const analytics = goog.analytics('v3');
|
|
const makeRequest = observeMethod(analytics.data.realtime, 'get');
|
|
export const toBoundInt = _.flow(
|
|
// first convert string to integer
|
|
_.toInteger,
|
|
// then we bound the integer to prevent weird things like Infinity
|
|
// and negative numbers
|
|
// can't wait to the day we need to update this!
|
|
_.partialRight(_.clamp, 0, upperBound)
|
|
);
|
|
|
|
export function createActiveUsers() {
|
|
const zero = Observable.of(0);
|
|
let credentials;
|
|
if (!pathToCred) {
|
|
// if no path to credentials set to zero;
|
|
log(dedent`
|
|
no google applications credentials environmental variable found
|
|
'GOOGLE_APPLICATION_CREDENTIALS'
|
|
'activeUser' api will always return 0
|
|
this can safely be ignored during development
|
|
`);
|
|
return zero;
|
|
}
|
|
try {
|
|
credentials = require(fs.realpathSync(pathToCred));
|
|
} catch (err) {
|
|
log('google applications credentials file failed to require');
|
|
console.error(err);
|
|
// if we can't require credentials set to zero;
|
|
return zero;
|
|
}
|
|
if (
|
|
!credentials.private_key ||
|
|
!credentials.client_email ||
|
|
!credentials.viewId
|
|
) {
|
|
log(dedent`
|
|
google applications credentials json should have a
|
|
* private_key
|
|
* client_email
|
|
* viewId
|
|
but none were found
|
|
`);
|
|
return zero;
|
|
}
|
|
|
|
const client = new goog.auth.JWT(
|
|
credentials['client_email'],
|
|
null,
|
|
credentials['private_key'],
|
|
[scope],
|
|
);
|
|
const authorize = observeMethod(client, 'authorize');
|
|
const options = {
|
|
ids: `ga:${credentials.viewId}`,
|
|
auth: client,
|
|
metrics: 'rt:activeUsers'
|
|
};
|
|
return Observable.defer(
|
|
// we wait for authorize to complete before attempting to make request
|
|
// this ensures our token is initialized and valid
|
|
// we defer here to make sure the actual request is done per subscription
|
|
// instead of once at startup
|
|
() => authorize().flatMap(() => makeRequest(options))
|
|
)
|
|
// data: Array[body|Object, request: Request]
|
|
.map(data => data[0])
|
|
.map(
|
|
({ totalsForAllResults } = {}) => totalsForAllResults['rt:activeUsers']
|
|
)
|
|
.map(toBoundInt)
|
|
// print errors to error log for logging, duh
|
|
.do(null, err => console.error(err))
|
|
// always send a number down
|
|
.catch(() => Observable.of(0))
|
|
// cache for 2 seconds to prevent hitting our daily request limit
|
|
::timeCache(2, 'seconds');
|
|
}
|
|
|