Merge pull request #319 from CatalystCode/add-extension-docs

adding documentation
This commit is contained in:
Mor Shemesh 2017-08-20 16:53:55 +03:00 коммит произвёл GitHub
Родитель 730232ce54 82e12865e6
Коммит eead308150
14 изменённых файлов: 413 добавлений и 17 удалений

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

@ -45,9 +45,8 @@ yarn start:dev
Open **http://localhost:3000**
For contribution and code documentation, [follow this link](/docs/README.md).
(For more information on development environment, see https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/)
For contribution and code documentation follow:
[DEVELOPMENT & CONTRIBUTION](/docs/README.md).
# Deploy To Azure

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

@ -30,6 +30,7 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* type: 'bars',
* args: {
* prefix: string - a prefix string for the exported variables (default to id).
* data: string - the state property holding the data (default is 'values').
* valueField: string - The field name holding the value/y value of the bar
* barsField: string - The field name holding the names for the bars
* seriesField: string - The field name holding the series name (aggregation in a specific field)
@ -63,7 +64,7 @@ export function bars(
const threshold = args.threshold || 0;
const othersValue = args.othersValue || 'Others';
let values: any[] = state.values || [];
let values: any[] = state[args.data || 'values'] || [];
// Concating values with '...'
if (values && values.length && valueMaxLength && (seriesField || barsField)) {

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

@ -25,6 +25,7 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* type: 'filter',
* args: {
* prefix: string - a prefix string for the exported variables (default to id).
* data: string - the state property holding the data (default is 'values').
* field: string - the field holding the filter values in the results (default = "value")
* }
* }
@ -40,14 +41,14 @@ export function filter (
plugin: IDataSourcePlugin,
prevState: any) {
const { values } = state;
if (!values) { return null; }
const args = typeof format !== 'string' ? format.args : {};
const prefix = getPrefix(format);
const field = args.field || 'value';
const unknown = args.unknown || 'unknown';
const values = state[args.data || 'values'];
if (!values) { return null; }
// This code is meant to fix the following scenario:
// When "Timespan" filter changes, to "channels-selected" variable
// is going to be reset into an empty set.

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

@ -11,6 +11,7 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* type: 'filtered_samples',
* args: {
* prefix: string - a prefix string for the filtered sample data (defaults to 'filtered').
* data: string - the state property holding the data (default is 'values').
* }
* }
* @param state Current received state from data source

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

@ -26,6 +26,7 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* type: 'filter',
* args: {
* prefix: string - a prefix string for the exported variables (default to id).
* data: string - the state property holding the data (default is 'values').
* }
* }
* @param state Current received state from data source
@ -41,17 +42,20 @@ export function flags(
prevState: any) {
const prefix = getPrefix(format);
const args = typeof format !== 'string' && format.args || {};
const params = plugin.getParams();
if (!params || !Array.isArray(params.values)) {
return formatWarn('A paramerter "values" is expected as an array on "params" in the data source', 'filter', plugin);
}
if (!state) { return null; }
let values = params[args.data || 'values'];
let flags = {};
params.values.forEach(key => { flags[key] = state.selectedValue === key; });
values.forEach(key => { flags[key] = state.selectedValue === key; });
flags[prefix + 'values-all'] = params.values;
flags[prefix + 'values-all'] = values;
flags[prefix + 'values-selected'] = state.selectedValue || [];
return flags;

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

@ -43,7 +43,13 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* ]
* }
*
* @param format Plugin format parameter
* @param format 'retention' | {
* type: 'retention',
* args: {
* prefix: string - a prefix string for the exported variables (default to id).
* data: string - the state property holding the data (default is 'values').
* }
* }
* @param state Current received state from data source
* @param dependencies Dependencies for the plugin
* should contain "selectedTimespan" equals to 'PT24H', 'P7D' etc...
@ -55,12 +61,15 @@ export function retention (
dependencies: IDictionary,
plugin: IDataSourcePlugin,
prevState: any) {
const { values } = state;
const { selectedTimespan } = dependencies;
if (!values || !values.length) { return null; }
const prefix = getPrefix(format);
const args = typeof format !== 'string' && format.args || {};
let values = state[args.data || 'values'];
if (!values || !values.length) { return null; }
const { selectedTimespan } = dependencies;
let result = {
totalUnique: 0,
totalUniqueUsersIn24hr: 0,

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

@ -25,6 +25,7 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* type: 'scorecard',
* args: {
* prefix: string - a prefix string for the exported variables (default to id).
* data: string - the state property holding the data (default is 'values').
* countField: 'count' - Field name with count value (default: 'count')
* postfix: '%' - String to add after the value (default: null)
* thresholds: [{ value: 0, heading: '', color: '#000', icon: 'done' }]
@ -43,11 +44,12 @@ export function scorecard (
dependencies: IDictionary,
plugin: IDataSourcePlugin,
prevState: any) {
let { values } = state;
const args = typeof format !== 'string' && format.args || { thresholds: null };
const countField = args.countField || 'count';
const postfix = args.postfix || null;
let values = state[args.data || 'values'];
let checkValue = (values && values[0] && values[0][countField]) || 0;
let createValue = (value: any, heading: string, color: string, icon: string, subvalue?: any, subheading?: string) => {

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

@ -35,6 +35,7 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* type: 'timeline',
* args: {
* prefix: string - a prefix string for the exported variables (default to id).
* data: string - the state property holding the data (default is 'values').
* timeField: 'timestamp' - The field containing timestamp
* lineField: 'channel' - A field to hold/group by different lines in the graph
* valueField: 'count' - holds the value/y value of the current point
@ -56,11 +57,12 @@ export function timeline(
return formatWarn('format should be an object with args', 'timeline', plugin);
}
const timeline = state.values;
const { timespan } = dependencies;
const args = format.args || {};
const { timeField, lineField, valueField } = args;
const prefix = getPrefix(format);
let values = state[args.data || 'values'];
const timeline = values;
let _timeline = {};
let _lines = {};

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

@ -26,6 +26,7 @@ import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugi
* type: 'timespan',
* args: {
* prefix: string - a prefix string for the exported variables (default to id).
* data: string - the state property holding the data (default is 'values').
* }
* }
* @param state Current received state from data source
@ -58,7 +59,11 @@ export function timespan(
queryTimespan,
granularity
};
result[prefix + 'values-all'] = params.values;
const args = typeof format !== 'string' && format.args || {};
let values = params[args.data || 'values'];
result[prefix + 'values-all'] = values;
result[prefix + 'values-selected'] = state.selectedValue;
return result;

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

@ -22,6 +22,8 @@ Connection plugins are connected to Data Source plugins. A Data Source can have
## Data Source Plugins
[How to create a Data Source Plugin](add-new-data-source.md)
* Constant
* Sample
* Application Insights
@ -32,6 +34,8 @@ Connection plugins are connected to Data Source plugins. A Data Source can have
## Elements Plugins
[How to create a Visual Plugin](add-new-element.md)
* [Area Chart](components/area.md)
* [Bar Chart](components/bar.md)
* [Detail View](components/detail.md)
@ -45,6 +49,8 @@ Connection plugins are connected to Data Source plugins. A Data Source can have
## data-formats plugins
[A short excerpt on data-formats](data-formats)
# Additional Features
* [Two Modes Elements](two-modes-element.md)

219
docs/add-new-data-source.md Normal file
Просмотреть файл

@ -0,0 +1,219 @@
# Adding a new Data Source to Ibex Dashboard
There are three types of data sources existing in Ibex today:
1. Unchanging data source - The source for the data remains unchanging (while the state is updatable)
2. Direct API calls - The data is received by querying a remote API
3. Server API calls - The data is received by querting the server (This approach is used for APIs which SDKs require Node.js)
Let's see how to create a general data source that:
* Uses an external API
* That API requires a `username` and `password`
* We can send a string query to that API
* We can add parameters to that query
## Connection
Connection types are used to store authentication information for the remote APIs.
While a Connection is not required for unchanging data sources, defining a connection once, enable different data sources to query the same API with the same credentials.
Under `/client/src/data-sources/connections` create a new file like this (for the sake of this article, we'll use the name 'new-connection'):
`new-connection.tsx`:
```tsx
import * as React from 'react';
import { IConnection, ConnectionEditor, IConnectionProps } from './Connection';
/**
* This class is used to define the parameters for the connection
* as well as the editor for the connection
*/
export default class NewConnection implements IConnection {
type = 'new-connection';
params = [ 'username', 'password' ];
editor = AzureConnectionEditor;
}
/**
* In case not all the parameters are filled up, or the user wants to edit the parameters
* they can use this class to edit the those parameters
*/
class NewConnectionEditor extends ConnectionEditor<IConnectionProps, any> {
render() {
return (
<div>...</div>
);
}
}
```
Then, do to `/client/src/data-sources/connections/index.ts` and add your new connection type:
```ts
import NewConnection from './new-connection';
var connectionTypes = [
...,
NewConnection ];
```
## Data Source Creation
After creating the connection type, we need to define the data source and its options.
Under `/client/src/data-sources/plugins` create a new file like this (for the sake of this article, we'll use the name 'new-data-source'):
`new-data-source.ts`:
```ts
import * as request from 'xhr-request';
import { DataSourcePlugin, IOptions } from './DataSourcePlugin';
import { DataSourceConnector } from '../DataSourceConnector';
import NewConnection from '../connections/new-connection';
let connectionType = new GraphQLConnection();
interface INewDataSourceParams {
query: string;
parameters: Object;
}
export default class NewDataSource extends DataSourcePlugin<INewDataSourceParams> {
type = 'NewDataSource'; // The name of the data source to be used in a template
defaultProperty = 'data'; // The property holding the default value when adding this data source as a dependency
connectionType = connectionType.type;
constructor(options: IOptions<INewDataSourceParams>, connections: IDict<IStringDictionary>) {
super(options, connections);
// this._props.params is of type INewDataSourceParams, and will hold the requested pararms
this.validateParams(this._props.params);
}
/**
* This method will be called each time the dependencies for this data source are updated
*/
dependenciesUpdated(dependencies: any) {
// Ensure dependencies exist
const isAnyDependencyMissing = Object.keys(this.getDependencies()).some(key => dependencies[key] == null);
if (isAnyDependencyMissing) {
return dispatch => dispatch();
}
// Validate connection
const connection = this.getConnection();
const { username, password } = connection;
if (!connection || !username || !password) {
return dispatch => dispatch();
}
const params = this.getParams() || ({} as INewDataSourceParams);
const query = params.query || '';
const parameters = dependencies['parameters'] || params.variables;
return dispatch => {
request('https://reqres.in/api/users', {
method: 'GET',
json: true,
/**
* Please notice, in this sample, this API required no username/password/query/params
*/
body: {
username: username,
password: password,
query: query,
parameters: parameters
}
}, (err, json) => {
if (err) {
return this.failure(err);
}
// Using https://reqres.in/api/users
// json contains {data: [...], total: 6, etc... }
return dispatch(json);
});
};
}
/**
* This method is used mainly by filters to support updating selected values by the user.
* Unless you have to change it, copy it as is.
*/
updateSelectedValues(dependencies: IDictionary, selectedValues: any) {
if (Array.isArray(selectedValues)) {
return Object.assign(dependencies, { 'selectedValues': selectedValues });
} else {
return Object.assign(dependencies, { ... selectedValues });
}
}
private validateParams(params: INewDataSourceParams): void {
if (true) {
return;
}
throw new Error('Params are bad');
}
}
```
Then, to declare this data source, edit the following file:
`/client/src/data-sources/plugins/index.ts`
```ts
import NewDataSource from './NewDataSource';
export default {
...,
'NewDataSource': NewDataSource
};
```
## Using in a template
To use the data source in a dashboard:
`example.private.js`:
```js
return {
id: "example",
...,
config: {
connections: {
/* The following parameters will be automatically requested if not supplied here */
/* "new-connection" is derived from the type property of the Connection Type class */
"new-connection": { username: "admin",password: "123456" }
},
layout: {...}
},
dataSources: [
{
id: "some_id",
type: "NewDataSource",
params: {
query: "SELECT * FROM USERS",
parameters: { a: 1, b: 2, c: 3 }
},
format: { type: "bars", valueField: "id", barsField: "first_name", seriesField: "lastName" }
}
],
elements: [
{
id: "bar_sample1",
type: "BarData",
title: "Some Bar Data",
subtitle: "Description of bar sample 1",
size: { w: 5, h: 8 },
source: "some_id",
}
],
...
}
```

132
docs/add-new-element.md Normal file
Просмотреть файл

@ -0,0 +1,132 @@
# Adding a Visual Element
The current project holds a collection of visual plugin is.
For the full list [click here](README.md#elements-plugins).
To create a new visual component we will need to things:
1. Create a new `GenericComponent` class that will display the component.
2. Create a new `data-format` to transform the data to fit this visual component.
To read more about data-formats [click here](data-formats).
This article explains how to create a generic component that:
* Displays an array of data in a `ul` list.
* Enables defining the color of the text in the `ul`.
## Creating a new Visual Component
Under `/client/src/components/generic` create a new class (for the sake of this article we will use NewComponent):
`NewComponent.tsx`:
```tsx
import * as React from 'react';
import * as _ from 'lodash';
import Card from '../../Card';
import { GenericComponent, IGenericProps, IGenericState } from '../GenericComponent';
interface INewProps extends IGenericProps {
props: {
textColor: string;
};
};
interface INewState extends IGenericState {
values: any[];
}
export default class BarData extends GenericComponent<INewProps, INewState> {
/**
* This method is used to translate data received from a data-format
* It means, it will look for 'values' property in a source of data.
*/
static fromSource(source: string) {
return {
values: GenericComponent.sourceFormat(source, 'values'),
};
}
constructor(props: any) {
super(props);
this.state = {
values: []
};
}
render() {
let { values } = this.state;
let { id, title, subtitle, props } = this.props;
let { textColor } = props;
let liElements = [];
if (!values || !values.length) {
liElements = [
(
<li><div style={{ padding: 20 }}>No data is available</div></li>
)
];
}
else {
liElements = values.map((value, idx) => {
return (
<li key={idx} style={{ color: textColor }} >
{JSON.stringify(value)}
</li>
);
});
}
// Todo: Receive the width of the SVG component from the container
return (
<Card id={id} title={title} subtitle={subtitle}>
<ul>
{liElements}
</ul>
</Card>
);
}
}
```
## Creating the data-format
To create the correlative data format, we need to create a data transformer the outputs a 'values' property (To correlate to the property in the `fromSource` static method).
We'll create a data format that receives an array and stores it in a format attirbute named `values` in correlation to the requested `values` in the `fromSource` static method.
Under `/client/src/utils/data-formats/formats` create the following file (we'll use 'new-format' for the sake of this article):
`new-format.ts`:
```ts
import { DataFormatTypes, IDataFormat, formatWarn, getPrefix } from '../common';
import { IDataSourcePlugin } from '../../../data-sources/plugins/DataSourcePlugin';
/**
* Please look at other data formats, to follow documentation formats
*/
export function filter (
format: string | IDataFormat,
state: any,
dependencies: IDictionary,
plugin: IDataSourcePlugin,
prevState: any) {
const args = typeof format !== 'string' ? format.args : {};
const prefix = getPrefix(format);
const data = args.data || 'values'; // This property is used to look for the data location
const field = args.field || 'value';
const unknown = args.unknown || 'unknown';
const values = state[data];
if (!values) { return null; }
let result = {};
result[prefix + 'values'] = values;
return result;
}
```

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

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

@ -0,0 +1,15 @@
# Data Formats
Data formats are data transformers that align the data received from a data source to the format required by a visual component.
The available data formats are:
* [bars](../../client/src/utils/data-formats/formats/bars.ts)
* [filter](../../client/src/utils/data-formats/formats/filter.ts)
* [filtered-samples](../../client/src/utils/data-formats/formats/filtered-samples.ts)
* [flags](../../client/src/utils/data-formats/formats/flags.ts)
* [retention](../../client/src/utils/data-formats/formats/retention.ts)
* [scorecard](../../client/src/utils/data-formats/formats/scorecard.ts)
* [timeline](../../client/src/utils/data-formats/formats/timeline.ts)
* [timespan](../../client/src/utils/data-formats/formats/timespan.ts)
To create a new Visual Element with a correlating data-format [click here](../add-new-element.md).