This commit is contained in:
David Douglas 2017-05-17 22:37:26 -07:00
Родитель fbd383ec03
Коммит 7a02e8d031
5 изменённых файлов: 246 добавлений и 95 удалений

Просмотреть файл

@ -1,6 +1,8 @@
const fs = require('fs');
const path = require('path');
const express = require('express');
const crypto = require('crypto');
const request = require('xhr-request');
const privateSetupPath = path.join(__dirname, '..', 'config', 'setup.private.json');
const initialSetupPath = path.join(__dirname, '..', 'config', 'setup.initial.json');
@ -290,6 +292,76 @@ router.post('/setup', (req, res) => {
})
});
router.post('/cosmosdb/:id', (req, res) => {
const { id } = req.params;
const { privateDashboard } = paths();
const dashboardPath = path.join(privateDashboard, id + '.private.js');
const fileContents = getFileContents(dashboardPath);
if (!isValidFile(dashboardPath)) {
return res.send({ error: new Error('Failed to get dashboard') });
}
let docdb = getField(/("?cosmos-db"?:\s*)({.*?})/im, fileContents);
if (docdb.startsWith("{") && docdb.endsWith("}")) {
docdb = JSON.parse(docdb.replace(/(\s*?{\s*?|\s*?,\s*?)(['"])?([a-zA-Z0-9]+)(['"])?:/g, '$1"$3":'));
}
if (!docdb.key || !docdb.host) {
console.error('Missing CosmosDB host/key config');
return this.failure('CosmosDB host/key config required');
}
const date = new Date().toUTCString();
const verb = req.body.verb;
const resourceType = req.body.resourceType; //'docs';
const resourceLink = `dbs/${req.body.databaseId}/colls/${req.body.collectionId}`; //req.body.resourceLink;
const masterKey = docdb.key;
const auth = getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceLink, date, masterKey);
let cosmosQuery = {
query: req.body.query,
parameters: req.body.parameters,
};
const host = docdb.host;
const url = `https://${host}.documents.azure.com/${resourceLink}/docs`;
request(url, {
method: 'POST',
headers: {
'Content-Type': 'application/query+json',
'Accept': 'application/json',
'Authorization': auth,
'x-ms-date': date,
'x-ms-version': '2015-04-08',
'x-ms-documentdb-isquery': true,
},
body: cosmosQuery,
responseType: 'json',
json: true,
}, (err, doc) => {
if (err) {
console.log(err);
return this.failure(err);
}
res.send(doc);
});
});
function getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceLink, date, masterKey) {
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceLink || "") + "\n" +
date.toLowerCase() + "\n" +
"" + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
module.exports = {
router
}

Просмотреть файл

@ -0,0 +1,66 @@
import * as React from 'react';
import { IConnection, ConnectionEditor, IConnectionProps } from './Connection';
import InfoDrawer from '../../components/common/InfoDrawer';
import TextField from 'react-md/lib/TextFields';
import Checkbox from 'react-md/lib/SelectionControls/Checkbox';
export default class CosmosDBConnection implements IConnection {
type = 'cosmos-db';
params = ['host', 'key'];
editor = CosmosDBConnectionEditor;
}
class CosmosDBConnectionEditor extends ConnectionEditor<IConnectionProps, any> {
constructor(props: IConnectionProps) {
super(props);
this.onParamChange = this.onParamChange.bind(this);
}
onParamChange(value: string, event: any) {
if (typeof this.props.onParamChange === 'function') {
this.props.onParamChange('cosmos-db', event.target.id, value);
}
}
render() {
let { connection } = this.props;
// connection = connection || {'ssl':true };
return (
<div>
<h2 style={{ float: 'left', padding: 9 }}>CosmosDB</h2>
<InfoDrawer
width={300}
title="CosmosDB"
buttonIcon="help"
buttonTooltip="Click here to learn more about CosmosDB"
>
<div>
<a href="https://azure.microsoft.com/en-us/services/cosmos-db/" target="_blank">Create Cosmos DB</a>
<hr/>
<a href="https://www.documentdb.com/sql/demo" target="_blank">Try CosmosDB demo queries</a>
</div>
</InfoDrawer>
<TextField
id="host"
label={'Host'}
defaultValue={connection['host'] || ''}
lineDirection="center"
placeholder="Fill in hostname"
className="md-cell md-cell--bottom"
onChange={this.onParamChange}
/>
<TextField
id="key"
label={'Key'}
defaultValue={connection['key'] || ''}
lineDirection="center"
placeholder="Fill in Key"
className="md-cell md-cell--bottom"
onChange={this.onParamChange}
/>
</div>
);
}
}

Просмотреть файл

@ -1,10 +1,10 @@
import * as React from 'react';
import ApplicationInsightsConnection from './application-insights';
import MongoDBConnection from './mongodb';
import CosmosDBConnection from './cosmos-db';
import { IConnection } from './Connection';
var connectionTypes = [ ApplicationInsightsConnection, MongoDBConnection];
var connectionTypes = [ ApplicationInsightsConnection, CosmosDBConnection];
var connections: IDict<IConnection> = {};
connectionTypes.forEach(connectionType => {

Просмотреть файл

@ -1,93 +0,0 @@
import * as React from 'react';
import { IConnection, ConnectionEditor, IConnectionProps } from './Connection';
import InfoDrawer from '../../components/common/InfoDrawer';
import TextField from 'react-md/lib/TextFields';
import Checkbox from 'react-md/lib/SelectionControls/Checkbox';
export default class MongoDBConnection implements IConnection {
type = 'mongodb';
params = ['host', 'port', 'username', 'password', 'ssl'];
editor = MongoDBConnectionEditor;
}
class MongoDBConnectionEditor extends ConnectionEditor<IConnectionProps, any> {
constructor(props: IConnectionProps) {
super(props);
this.onParamChange = this.onParamChange.bind(this);
}
onParamChange(value: string, event: any) {
if (typeof this.props.onParamChange === 'function') {
this.props.onParamChange('mongodb', event.target.id, value);
}
}
render() {
let { connection } = this.props;
connection = connection || {'ssl':true };
return (
<div>
<h2 style={{ float: 'left', padding: 9 }}>MongoDB</h2>
<InfoDrawer
width={300}
title='Authentication'
buttonIcon='help'
buttonTooltip='Click here to learn more about authentications'
>
<div>
I lied. Click <a href='https://docs.mongodb.com/manual/core/authentication/' target='_blank'>here</a> to
learn more about MongoDB authentication.
<hr/>
Sorry.
</div>
</InfoDrawer>
<TextField
id="host"
label={'Host'}
defaultValue={connection['host'] || ''}
lineDirection="center"
placeholder="Fill in hostname"
className="md-cell md-cell--bottom"
onChange={this.onParamChange}
/>
<TextField
id="port"
label={'Port'}
defaultValue={connection['port'] || ''}
lineDirection="center"
placeholder="Fill in MongoDB port"
className="md-cell md-cell--bottom"
onChange={this.onParamChange}
/>
<TextField
id="username"
label={'Username'}
defaultValue={connection['username'] || ''}
lineDirection="center"
placeholder="Fill in username"
className="md-cell md-cell--bottom"
onChange={this.onParamChange}
/>
<TextField
id="password"
label={'Password'}
defaultValue={connection['password'] || ''}
lineDirection="center"
placeholder="Fill in password"
className="md-cell md-cell--bottom"
onChange={this.onParamChange}
/>
<Checkbox
id="ssl"
label={'Use SSL?'}
defaultChecked={connection['ssl']}
className="md-cell md-cell--bottom"
onChange={this.onParamChange}
/>
</div>
);
}
}

Просмотреть файл

@ -0,0 +1,106 @@
import * as request from 'xhr-request';
import { DataSourcePlugin, IOptions } from '../DataSourcePlugin';
import { DataSourceConnector } from '../../DataSourceConnector';
import CosmosDBConnection from '../../connections/cosmos-db';
let connectionType = new CosmosDBConnection();
interface IQueryParams {
query?: ((dependencies: any) => string) | string;
parameters?: (string | object)[];
mappings?: (string | object)[];
databaseId?: string;
collectionId?: string;
calculated?: (results: any) => object;
}
export default class CosmosDBQuery extends DataSourcePlugin<IQueryParams> {
type = 'CosmosDB-Query';
defaultProperty = 'doc';
connectionType = connectionType.type;
constructor(options: IOptions<IQueryParams>, connections: IDict<IStringDictionary>) {
super(options, connections);
this.validateTimespan(this._props);
this.validateParams(this._props.params);
}
/**
* update - called when dependencies are created
* @param {object} dependencies
* @param {function} callback
*/
updateDependencies(dependencies: any) {
let emptyDependency = false;
Object.keys(this._props.dependencies).forEach((key) => {
if (typeof dependencies[key] === 'undefined') { emptyDependency = true; }
});
// If one of the dependencies is not supplied, do not run the query
if (emptyDependency) {
return (dispatch) => {
return dispatch();
};
}
// Validate connection
let connection = this.getConnection();
let { host, key } = connection;
if (!connection || !host || !key) {
return (dispatch) => {
return dispatch();
};
}
// Get Dashboard Id
const paths = location.pathname.split('/');
if (paths.length !== 3) {
throw Error('Expected location pathname:' + paths);
}
const dashboardId = paths[paths.length - 1];
const params = this._props.params;
const query: string = this.compileQuery(params.query, dependencies);
const url = `/api/cosmosdb/${dashboardId}`;
const body = {
verb: 'POST',
databaseId: params.databaseId,
collectionId: params.collectionId,
resourceType: 'docs',
query: query,
parameters: params.parameters
};
return (dispatch) => {
request(url, {
method: 'POST',
json: true,
body: body,
}, (error, json) => {
if (error || !json.Documents) {
return this.failure(error);
}
return dispatch(json.Documents);
});
};
}
updateSelectedValues(dependencies: IDictionary, selectedValues: any) {
if (Array.isArray(selectedValues)) {
return Object.assign(dependencies, { 'selectedValues': selectedValues });
} else {
return Object.assign(dependencies, { ... selectedValues });
}
}
private compileQuery(query: any, dependencies: any): string {
return typeof query === 'function' ? query(dependencies) : query;
}
private validateTimespan(props: any) {
}
private validateParams(params: IQueryParams): void {
}
}