<commit_before>/**
 * @file TreeNode component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, Fragment} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import CircularLoading from '../CircularLoading';
import Theme from '../Theme';
import IconButton from '../IconButton';
import Radio from '../Radio';
import Checkbox from '../Checkbox';

import SelectMode from '../_statics/SelectMode';
import VirtualRoot from '../_statics/VirtualRoot';

import Util from '../_vendors/Util';
import Calculation from '../_vendors/Calculation';

class TreeNode extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            collapsed: props.data && props.data[VirtualRoot] ? false : props.collapsed
        };

    }

    toggleTreeNode = e => {

        e && e.stopPropagation();

        const {onNodeToggleStart} = this.props;
        onNodeToggleStart && onNodeToggleStart();

        this.setState({
            collapsed: !this.state.collapsed
        }, () => {
            const {onNodeToggleEnd} = this.props;
            onNodeToggleEnd && onNodeToggleEnd();
        });

    };

    checkboxChangeHandler = e => {

        const {data, path, value, onSelect, onDeselect} = this.props;

        if (!Calculation.isItemChecked(data, value, this.props)) {
            onSelect && onSelect(data, path, e);
        } else {
            onDeselect && onDeselect(data, path, e);
        }

    };

    radioChangeHandler = e => {
        const {data, path, onSelect} = this.props;
        onSelect && onSelect(data, path, e);
    };

    clickHandler = e => {

        const {data, path, disabled, isLoading, readOnly} = this.props;

        if (disabled || isLoading || readOnly || data.disabled || data.isLoading || data.readOnly) {
            return;
        }

        const {onClick} = this.props;
        onClick && onClick(data, path, e);

        const {selectMode} = this.props;

        switch (selectMode) {
            case SelectMode.MULTI_SELECT:
                this.checkboxChangeHandler(e);
                return;
            case SelectMode.SINGLE_SELECT:
                this.radioChangeHandler(e);
                return;
        }

    };

    renderChildren = () => {

        const {depth, path, data} = this.props,
            {collapsed} = this.state,
            isVirtual = VirtualRoot in data,

            childrenClassName = classNames('tree-node-children', {
                collapsed
            });

        return data.children && data.children.length > 0 ?

                {
                    data.children.map((item, index) =>
                        <TreeNode {...this.props}
                                  key={index}
                                  data={item}
                                  index={index}
                                  depth={depth + (isVirtual ? 0 : 1)}
                                  path={
                                      path ?
                                          [...path, {index, node: item}]
                                          :
                                          [{index, node: item}]
                                  }/>
                    )
                }

            :
            null;

    };

    render() {

        const {data} = this.props;

        if (VirtualRoot in data) {
            return this.renderChildren();
        }

        const {

                index, depth, theme, selectTheme, selectMode, value,
                disabled, isLoading, readOnly, allowCollapse, isSelectRecursive,
                valueField, displayField, descriptionField, filter,

                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                renderer, onMouseEnter, onMouseLeave

            } = this.props,
            {collapsed} = this.state,

            checked = Calculation.isItemChecked(data, value, this.props),
            indeterminate = Calculation.isNodeIndeterminate(data, value, this.props),

            isNodeLoading = data.isLoading || isLoading,
            isNodeDisabled = data.disabled || disabled || isNodeLoading,

            isNodeMatched = !filter || (data && displayField in data
                && data[displayField].toString().toUpperCase().includes(filter.toUpperCase())),

            nodeClassName = classNames('tree-node', {
                [`theme-${theme}`]: theme,
                [data.className]: data.className
            }),
            nodeStyle = {
                ...data.style,
                paddingLeft: (depth + 1) * 20
            },

            loadingIconPosition = (data.rightIconCls && !data.iconCls) ? 'right' : 'left';

        return (
            <Fragment>

                {
                    isNodeMatched ?

                                {
                                    allowCollapse && data.children && data.children.length > 0 ?
                                        <IconButton className="tree-node-collapse-icon"
                                                    iconCls={collapsed ?
                                                        data.collapsedIconCls || collapsedIconCls
                                                        :
                                                        data.expandedIconCls || expandedIconCls}
                                                    onClick={this.toggleTreeNode}/>
                                        :
                                        null
                                }

                                {
                                    selectMode === SelectMode.SINGLE_SELECT && (radioUncheckedIconCls || radioCheckedIconCls) ?
                                        <Radio className="tree-node-select"
                                               theme={selectTheme}
                                               checked={checked}
                                               disabled={isNodeDisabled}
                                               uncheckedIconCls={data.radioUncheckedIconCls || radioUncheckedIconCls}
                                               checkedIconCls={data.radioCheckedIconCls || radioCheckedIconCls}
                                               disableTouchRipple={true}/>
                                        :
                                        null
                                }

                                {
                                    selectMode === SelectMode.MULTI_SELECT ?
                                        <Checkbox className="tree-node-select"
                                                  theme={selectTheme}
                                                  checked={checked}
                                                  indeterminate={isSelectRecursive ? indeterminate : false}
                                                  disabled={isNodeDisabled}
                                                  uncheckedIconCls={data.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                                  checkedIconCls={data.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                                  indeterminateIconCls={data.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                                  disableTouchRipple={true}/>
                                        :
                                        null
                                }

                                {
                                    isNodeLoading && loadingIconPosition === 'left' ?

                                            <CircularLoading className="button-loading-icon"
                                                             size="small"/>

                                        :
                                        (
                                            data.iconCls ?
                                                <i className={`button-icon button-icon-left ${data.iconCls}`}
                                                   aria-hidden="true">
                                                :
                                                null
                                        )
                                }

                                {
                                    data.itemRenderer && typeof data.itemRenderer === 'function' ?
                                        data.itemRenderer(data, index)
                                        :
                                        (
                                            renderer && typeof renderer === 'function' ?
                                                renderer(data, index)
                                                :
                                                (
                                                    data[descriptionField] ?

                                                                {Util.getTextByDisplayField(data, displayField, valueField)}

                                                                {data[descriptionField]}

                                                        :
                                                        Util.getTextByDisplayField(data, displayField, valueField)
                                                )
                                        )
                                }

                                {
                                    isNodeLoading && loadingIconPosition === 'right' ?
                                        <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                                         size="small"/>
                                        :
                                        (
                                            data.rightIconCls ?
                                                <i className={`button-icon button-icon-right ${data.rightIconCls}`}
                                                   aria-hidden="true">
                                                :
                                                null
                                        )
                                }

                        :
                        null
                }

                {this.renderChildren()}

            </Fragment>
        );

    }
}

TreeNode.propTypes = {

    index: PropTypes.number,
    depth: PropTypes.number,
    path: PropTypes.array,

    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    data: PropTypes.object,
    value: PropTypes.any,
    filter: PropTypes.string,

    valueField: PropTypes.string,
    displayField: PropTypes.string,
    descriptionField: PropTypes.string,

    disabled: PropTypes.bool,
    isLoading: PropTypes.bool,
    readOnly: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsed: PropTypes.bool,
    isNodeToggling: PropTypes.bool,
    isSelectRecursive: PropTypes.bool,

    renderer: PropTypes.func,

    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    onClick: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    onNodeToggleStart: PropTypes.func,
    onNodeToggleEnd: PropTypes.func

};

TreeNode.defaultProps = {

    index: 0,
    depth: 0,

    theme: Theme.DEFAULT,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    disabled: false,
    isLoading: false,
    readOnly: false,
    allowCollapse: true,
    collapsed: false,
    isNodeToggling: false,
    isSelectRecursive: false,

    collapsedIconCls: 'fas fa-caret-right',
    expandedIconCls: 'fas fa-caret-down',
    checkboxUncheckedIconCls: 'far fa-square',
    checkboxCheckedIconCls: 'fas fa-check-square',
    checkboxIndeterminateIconCls: 'fas fa-minus-square'

};

export default TreeNode;
<commit_msg>update TreeNode<commit_after>/**
 * @file TreeNode component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, Fragment} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import CircularLoading from '../CircularLoading';
import IconButton from '../IconButton';
import Radio from '../Radio';
import Checkbox from '../Checkbox';

import Theme from '../Theme';
import SelectMode from '../_statics/SelectMode';
import VirtualRoot from '../_statics/VirtualRoot';

import Util from '../_vendors/Util';
import Calculation from '../_vendors/Calculation';

class TreeNode extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            collapsed: props.data && props.data[VirtualRoot] ? false : props.collapsed
        };

    }

    toggleTreeNode = e => {

        e && e.stopPropagation();

        const {onNodeToggleStart} = this.props;
        onNodeToggleStart && onNodeToggleStart();

        this.setState({
            collapsed: !this.state.collapsed
        }, () => {
            const {onNodeToggleEnd} = this.props;
            onNodeToggleEnd && onNodeToggleEnd();
        });

    };

    handleCheckboxChange = e => {

        const {data, path, value, onSelect, onDeselect} = this.props;

        if (!Calculation.isItemChecked(data, value, this.props)) {
            onSelect && onSelect(data, path, e);
        } else {
            onDeselect && onDeselect(data, path, e);
        }

    };

    handleRadioChange = e => {
        const {data, path, onSelect} = this.props;
        onSelect && onSelect(data, path, e);
    };

    handleClick = e => {

        const {data, path, disabled, isLoading, readOnly} = this.props;

        if (disabled || isLoading || readOnly || data.disabled || data.isLoading || data.readOnly) {
            return;
        }

        const {onClick} = this.props;
        onClick && onClick(data, path, e);

        const {selectMode} = this.props;

        switch (selectMode) {
            case SelectMode.MULTI_SELECT:
                this.handleCheckboxChange(e);
                return;
            case SelectMode.SINGLE_SELECT:
                this.handleRadioChange(e);
                return;
        }

    };

    renderChildren = () => {

        const {depth, path, data} = this.props,
            {collapsed} = this.state,
            isVirtual = VirtualRoot in data;

        return data.children && data.children.length > 0 ?

                {
                    data.children.map((item, index) =>
                        <TreeNode {...this.props}
                                  key={index}
                                  data={item}
                                  index={index}
                                  depth={depth + (isVirtual ? 0 : 1)}
                                  path={
                                      path ?
                                          [...path, {index, node: item}]
                                          :
                                          [{index, node: item}]
                                  }/>
                    )
                }

            :
            null;

    };

    /* eslint-disable complexity */
    render() {

        const {data} = this.props;

        if (VirtualRoot in data) {
            return this.renderChildren();
        }

        const {

                index, depth, theme, selectTheme, selectMode, value,
                disabled, isLoading, readOnly, allowCollapse, isSelectRecursive,
                valueField, displayField, descriptionField, filter,

                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                renderer, onMouseEnter, onMouseLeave

            } = this.props,
            {collapsed} = this.state,

            checked = Calculation.isItemChecked(data, value, this.props),
            indeterminate = Calculation.isNodeIndeterminate(data, value, this.props),

            isNodeLoading = data.isLoading || isLoading,
            isNodeDisabled = data.disabled || disabled || isNodeLoading,

            isNodeMatched = !filter || (data && displayField in data
                && data[displayField].toString().toUpperCase().includes(filter.toUpperCase())),

            loadingIconPosition = (data.rightIconCls && !data.iconCls) ? 'right' : 'left';

        return (
            <Fragment>

                {
                    isNodeMatched ?
                        <div className={classNames('tree-node', {
                            [`theme-${theme}`]: theme,
                            [data.className]: data.className
                        })}
                             style={{
                                 ...data.style,
                                 paddingLeft: (depth + 1) * 20
                             }}
                             title={data.title}
                             disabled={isNodeDisabled}
                             readOnly={readOnly}
                             onClick={this.handleClick}
                             onMouseEnter={onMouseEnter}
                             onMouseLeave={onMouseLeave}>

                                {
                                    allowCollapse && data.children && data.children.length > 0 ?
                                        <IconButton className="tree-node-collapse-icon"
                                                    iconCls={collapsed ?
                                                        data.collapsedIconCls || collapsedIconCls
                                                        :
                                                        data.expandedIconCls || expandedIconCls}
                                                    onClick={this.toggleTreeNode}/>
                                        :
                                        null
                                }

                                {
                                    selectMode === SelectMode.SINGLE_SELECT && (radioUncheckedIconCls || radioCheckedIconCls) ?
                                        <Radio className="tree-node-select"
                                               theme={selectTheme}
                                               checked={checked}
                                               disabled={isNodeDisabled}
                                               uncheckedIconCls={data.radioUncheckedIconCls || radioUncheckedIconCls}
                                               checkedIconCls={data.radioCheckedIconCls || radioCheckedIconCls}
                                               disableTouchRipple={true}/>
                                        :
                                        null
                                }

                                {
                                    selectMode === SelectMode.MULTI_SELECT ?
                                        <Checkbox className="tree-node-select"
                                                  theme={selectTheme}
                                                  checked={checked}
                                                  indeterminate={isSelectRecursive ? indeterminate : false}
                                                  disabled={isNodeDisabled}
                                                  uncheckedIconCls={data.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                                  checkedIconCls={data.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                                  indeterminateIconCls={data.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                                  disableTouchRipple={true}/>
                                        :
                                        null
                                }

                                {
                                    isNodeLoading && loadingIconPosition === 'left' ?

                                            <CircularLoading className="button-loading-icon"
                                                             size="small"/>

                                        :
                                        (
                                            data.iconCls ?
                                                <i className={`button-icon button-icon-left ${data.iconCls}`}
                                                   aria-hidden="true">
                                                :
                                                null
                                        )
                                }

                                {
                                    data.itemRenderer && typeof data.itemRenderer === 'function' ?
                                        data.itemRenderer(data, index)
                                        :
                                        (
                                            renderer && typeof renderer === 'function' ?
                                                renderer(data, index)
                                                :
                                                (
                                                    data[descriptionField] ?

                                                                {Util.getTextByDisplayField(data, displayField, valueField)}

                                                                {data[descriptionField]}

                                                        :
                                                        Util.getTextByDisplayField(data, displayField, valueField)
                                                )
                                        )
                                }

                                {
                                    isNodeLoading && loadingIconPosition === 'right' ?
                                        <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                                         size="small"/>
                                        :
                                        (
                                            data.rightIconCls ?
                                                <i className={`button-icon button-icon-right ${data.rightIconCls}`}
                                                   aria-hidden="true">
                                                :
                                                null
                                        )
                                }

                        :
                        null
                }

                {this.renderChildren()}

            </Fragment>
        );

    }
}

TreeNode.propTypes = {

    index: PropTypes.number,
    depth: PropTypes.number,
    path: PropTypes.array,

    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    data: PropTypes.object,
    value: PropTypes.any,
    filter: PropTypes.string,

    valueField: PropTypes.string,
    displayField: PropTypes.string,
    descriptionField: PropTypes.string,

    disabled: PropTypes.bool,
    isLoading: PropTypes.bool,
    readOnly: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsed: PropTypes.bool,
    isNodeToggling: PropTypes.bool,
    isSelectRecursive: PropTypes.bool,

    renderer: PropTypes.func,

    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    onClick: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    onNodeToggleStart: PropTypes.func,
    onNodeToggleEnd: PropTypes.func

};

TreeNode.defaultProps = {

    index: 0,
    depth: 0,

    theme: Theme.DEFAULT,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    disabled: false,
    isLoading: false,
    readOnly: false,
    allowCollapse: true,
    collapsed: false,
    isNodeToggling: false,
    isSelectRecursive: false,

    collapsedIconCls: 'fas fa-caret-right',
    expandedIconCls: 'fas fa-caret-down',
    checkboxUncheckedIconCls: 'far fa-square',
    checkboxCheckedIconCls: 'fas fa-check-square',
    checkboxIndeterminateIconCls: 'fas fa-minus-square'

};

export default TreeNode;
<|endoftext|>


<commit_before>import axios from 'axios';
import defaultConfig from '../config';
import Keycloak from 'keycloak-js';
import md5 from 'js-md5';
import baseEntities from '../config/baseEntities';
import attributes from '../config/attributes';
import asks from '../config/ask';

export function registerUser(email, password) {

	//for api call dispatching action so that we can show the user loading event
	return function (dispatch) {
		dispatch({ type: "CREATE_USER_PENDING", payload: null });
		axios({
			url: '',
			method: 'post',
			baseURL: defaultConfig.redirectURI,
			data: {
				email,
				password
			},
			headers: {}
		}).then((response) => {
			dispatch({ type: "CREATE_USER_FULFILLED", payload: response.data });
		}).catch((error) => {
			dispatch({ type: "CREATE_USER_REJECTED", payload: error });
		});
	}
}

export function fetchUser(email, password) {

	return function (dispatch) {
		dispatch({ type: "FETCH_USER_PENDING", payload: null });
		axios({
			url: 'user/login',
			method: 'post',
			baseURL: defaultConfig.baseURL,
			data: {
				email,
				password
			},
			headers: {}
		}).then((response) => {
			dispatch({ type: "FETCH_USER_FULFILLED", payload: response.data });
		}).catch((error) => {
			dispatch({ type: "FETCH_USER_REJECTED", payload: error });
		});
	}
}

export function init(config) {
	return function (dispatch) {
		const kc = Keycloak();
		kc.init({ onLoad: 'login-required' })
			.success((authenticated) => {
				if (authenticated) {
					const projectDetails = (project) => {
						const payload = {
							keycloak: kc,
							project
						}
						dispatch({ type: "INIT_FULLFILLED", payload: payload });
					}
					loadUserInfo(kc, config, projectDetails);
					// loadBaseEntities(kc);
				}
				else {
					console.log("user could not authenticated");
				}
			})
			.error(function (err) {
				console.log('failed to initialize');

			})
	}
}

export function config(config) {
	return function (dispatch) {
		axios
			.get("/genny.properties.json")
			.then((result) => {
				dispatch(({ type: 'CONFIG_FULLFILLED', payload: result.data }));
			})
			.catch((err) => {
				dispatch({ type: "CONFIG_REJECTED", payload: err })
			})
	}
}

export function loadUserInfo(keycloak, config, projectDetails) {
	keycloak.loadUserInfo()
		.success((user) => {
			md5(user.email);
			const hash = md5.create();
			hash.update(user.email);
			hash.hex();
			const imgUrl = 'https://www.gravatar.com/avatar/' + hash;
			let projectName;
			if (config.REACT_APP_PROJECT_NAME !== undefined) {
				projectName = keycloak.realm;
			} else {
				projectName = defaultConfig.REACT_APP_PROJECT_NAME;
			}
			const project = {
				logo: projectName,
				user: {
					image: imgUrl,
					name: user.given_name
				}
			}
			// console.log("loaduserInfo project", project);
			projectDetails(project);
		})
		.error((err) => {

		});

}

export function loadBaseEntities(kc) {
	console.log("laading base entities");
	console.log("kecloak from base ",kc.token);
	return function (dispatch) {
	axios({
		method:"get",
		headers: {
			'Authorization' : `Bearer ${kc.token}`
		},
		url: "/qwanda/baseentitys",
		baseURL: "http://qwanda-service.outcome-hub.com",
	})
	.then((response) => {
		// console.log(response.data);
		dispatch(({ type: 'BASEENTITIES_FULLFILLED', payload: response.data }));
	})
	.catch ((error) => {
		 console.log("error fetching baseEntities", error);
	})
	}
}

// export function loadBaseEntities() {
// 	return function (dispatch) {
// 		dispatch(({ type: 'BASEENTITIES_FULLFILLED', payload: baseEntities }));
// 	}
// }
export function getAttributes(id) {
	console.log(id);
	let element = {};
	const attribute = attributes.forEach((attribute) => {
		if( attribute.id === id) {
			element = attribute;
		}
	});
	return function (dispatch) {
		dispatch(({ type: 'ATTRIBUTES_FULLFILLED', payload: element}));
	}
}

export function getAsks() {
	let element = [];
	const asks = asks.forEach((ask) => {
		element = ask;
	})
	return function (dispatch) {
		dispatch(({ type: 'ASKS_FULLFILLED', payload: element }));
	}
}<commit_msg>changed to https for api url<commit_after>import axios from 'axios';
import defaultConfig from '../config';
import Keycloak from 'keycloak-js';
import md5 from 'js-md5';
import baseEntities from '../config/baseEntities';
import attributes from '../config/attributes';
import asks from '../config/ask';

export function registerUser(email, password) {

	//for api call dispatching action so that we can show the user loading event
	return function (dispatch) {
		dispatch({ type: "CREATE_USER_PENDING", payload: null });
		axios({
			url: '',
			method: 'post',
			baseURL: defaultConfig.redirectURI,
			data: {
				email,
				password
			},
			headers: {}
		}).then((response) => {
			dispatch({ type: "CREATE_USER_FULFILLED", payload: response.data });
		}).catch((error) => {
			dispatch({ type: "CREATE_USER_REJECTED", payload: error });
		});
	}
}

export function fetchUser(email, password) {

	return function (dispatch) {
		dispatch({ type: "FETCH_USER_PENDING", payload: null });
		axios({
			url: 'user/login',
			method: 'post',
			baseURL: defaultConfig.baseURL,
			data: {
				email,
				password
			},
			headers: {}
		}).then((response) => {
			dispatch({ type: "FETCH_USER_FULFILLED", payload: response.data });
		}).catch((error) => {
			dispatch({ type: "FETCH_USER_REJECTED", payload: error });
		});
	}
}

export function init(config) {
	return function (dispatch) {
		const kc = Keycloak();
		kc.init({ onLoad: 'login-required' })
			.success((authenticated) => {
				if (authenticated) {
					const projectDetails = (project) => {
						const payload = {
							keycloak: kc,
							project
						}
						dispatch({ type: "INIT_FULLFILLED", payload: payload });
					}
					loadUserInfo(kc, config, projectDetails);
					// loadBaseEntities(kc);
				}
				else {
					console.log("user could not authenticated");
				}
			})
			.error(function (err) {
				console.log('failed to initialize');

			})
	}
}

export function config(config) {
	return function (dispatch) {
		axios
			.get("/genny.properties.json")
			.then((result) => {
				dispatch(({ type: 'CONFIG_FULLFILLED', payload: result.data }));
			})
			.catch((err) => {
				dispatch({ type: "CONFIG_REJECTED", payload: err })
			})
	}
}

export function loadUserInfo(keycloak, config, projectDetails) {
	keycloak.loadUserInfo()
		.success((user) => {
			md5(user.email);
			const hash = md5.create();
			hash.update(user.email);
			hash.hex();
			const imgUrl = 'https://www.gravatar.com/avatar/' + hash;
			let projectName;
			if (config.REACT_APP_PROJECT_NAME !== undefined) {
				projectName = keycloak.realm;
			} else {
				projectName = defaultConfig.REACT_APP_PROJECT_NAME;
			}
			const project = {
				logo: projectName,
				user: {
					image: imgUrl,
					name: user.given_name
				}
			}
			// console.log("loaduserInfo project", project);
			projectDetails(project);
		})
		.error((err) => {

		});

}

export function loadBaseEntities(kc) {
	console.log("laading base entities");
	console.log("kecloak from base ",kc.token);
	return function (dispatch) {
	axios({
		method:"get",
		headers: {
			'Authorization' : `Bearer ${kc.token}`
		},
		url: "/qwanda/baseentitys",
		baseURL: "https://qwanda-service.outcome-hub.com",
	})
	.then((response) => {
		// console.log(response.data);
		dispatch(({ type: 'BASEENTITIES_FULLFILLED', payload: response.data }));
	})
	.catch ((error) => {
		 console.log("error fetching baseEntities", error);
	})
	}
}

// export function loadBaseEntities() {
// 	return function (dispatch) {
// 		dispatch(({ type: 'BASEENTITIES_FULLFILLED', payload: baseEntities }));
// 	}
// }
export function getAttributes(id) {
	console.log(id);
	let element = {};
	const attribute = attributes.forEach((attribute) => {
		if( attribute.id === id) {
			element = attribute;
		}
	});
	return function (dispatch) {
		dispatch(({ type: 'ATTRIBUTES_FULLFILLED', payload: element}));
	}
}

export function getAsks() {
	let element = [];
	const asks = asks.forEach((ask) => {
		element = ask;
	})
	return function (dispatch) {
		dispatch(({ type: 'ASKS_FULLFILLED', payload: element }));
	}
}
<|endoftext|>


<commit_before>import queryString from "query-string";
import parseParams from "../util/parseParams";
import { createStateFromQueryOrJSONs } from "./recomputeReduxState";
import { PAGE_CHANGE, URL_QUERY_CHANGE_WITH_COMPUTED_STATE } from "./types";

// make prefix for data files with fields joined by _ instead of / as in URL
const makeDataPathFromParsedParams = (parsedParams) => {
  const tmp_levels = Object.keys(parsedParams.dataset).map((d) => parsedParams.dataset[d]);
  tmp_levels.sort((x, y) => x[0] > y[0]);
  return tmp_levels.map((d) => d[1]).join("_");
};

/* match URL pathname to datasets (from manifest) */
export const getDatapath = (pathname, availableDatasets) => {
  if (!availableDatasets) {return undefined;}
  const parsedParams = parseParams(pathname, availableDatasets);
  if (parsedParams.valid) {
    return makeDataPathFromParsedParams(parsedParams);
  }
  return pathname.replace(/^\//, '').replace(/\/$/, '').replace('/', '_');
};

export const chooseDisplayComponentFromPathname = (pathname) => {
  if (pathname === "/" || pathname === "/all") return "splash";
  else if (pathname.startsWith("/status")) return "status";
  return "app"; // fallthrough
};

/* changes the state of the page and (perhaps) the dataset displayed.
This function is used throughout the app for all navigation to another page, (including braowserBackForward - see function below)
The exception is for navigation requests that specify only the query changes, or that have an identical pathname to that selected.
Note that this function is not pure, in that it may change the URL

ARGUMENTS:
(1) path - REQUIRED - the destination path - e.g. "zika" or "flu/..." (does not include query)
(2) query - OPTIONAL (default: undefined) - see below
(3) push - OPTIONAL (default: true) - signals that pushState should be used (has no effect on the reducers)

UNDERSTANDING QUERY (SLIGHTLY CONFUSING)
This function changes the pathname (stored in the datasets reducer) and modifies the URL pathname and query
accordingly in the middleware. But the URL query is not processed further.
Because the datasets reducer has changed, the <App> (or whichever display component we're on) will update.
In <App>, this causes a call to loadJSONs, which will, as part of it's dispatch, use the URL state of query.
In this way, the URL query is "used".
*/
export const changePage = ({path, query = undefined, push = true}) => (dispatch, getState) => {
  if (!path) {console.error("changePage called without a path"); return;}
  const { datasets } = getState();
  const d = {
    type: PAGE_CHANGE,
    displayComponent: chooseDisplayComponentFromPathname(path),
    errorMessage: undefined
  };
  d.datapath = d.displayComponent === "app" ? getDatapath(path, datasets.availableDatasets) : undefined;
  if (query !== undefined) { d.query = query; }
  if (push) { d.pushState = true; }
  /* check if this is "valid" - we can change it here before it is dispatched */
  dispatch(d);
};

/* a 404 uses the same machinery as changePage, but it's not a thunk */
export const goTo404 = (errorMessage) => ({
  type: PAGE_CHANGE,
  displayComponent: "splash",
  errorMessage,
  pushState: true
});

/* modify redux state and URL by specifying a new URL query string. Pathname is not considered, if you want to change that, use "changePage" instead.
Unlike "changePage" the query is processed both by the middleware (i.e. to update the URL) AND by the reducers, to update their state accordingly.
ARGUMENTS:
(1) query - REQUIRED - {object}
(2) push - OPTIONAL (default: true) - signals that pushState should be used (has no effect on the reducers)
*/
export const changePageQuery = ({queryToUse, queryToDisplay = false, push = true}) => (dispatch, getState) => {
  const newState = createStateFromQueryOrJSONs({oldState: getState(), query: queryToUse});
  dispatch({
    type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
    ...newState,
    pushState: push,
    query: queryToDisplay ? queryToDisplay : queryToUse
  });
};

export const browserBackForward = () => (dispatch, getState) => {
  const { datasets } = getState();
  /* if the pathname has changed, trigger the changePage action (will trigger new post to load, new dataset to load, etc) */
  // console.log("broswer back/forward detected. From: ", datasets.urlPath, datasets.urlSearch, "to:", window.location.pathname, window.location.search)
  if (datasets.urlPath !== window.location.pathname) {
    dispatch(changePage({path: window.location.pathname}));
  } else {
    dispatch(changePageQuery({queryToUse: queryString.parse(window.location.search)}));
  }
};
<commit_msg>Add global replacement of slashes with underscores to fix #538<commit_after>import queryString from "query-string";
import parseParams from "../util/parseParams";
import { createStateFromQueryOrJSONs } from "./recomputeReduxState";
import { PAGE_CHANGE, URL_QUERY_CHANGE_WITH_COMPUTED_STATE } from "./types";

// make prefix for data files with fields joined by _ instead of / as in URL
const makeDataPathFromParsedParams = (parsedParams) => {
  const tmp_levels = Object.keys(parsedParams.dataset).map((d) => parsedParams.dataset[d]);
  tmp_levels.sort((x, y) => x[0] > y[0]);
  return tmp_levels.map((d) => d[1]).join("_");
};

/* match URL pathname to datasets (from manifest) */
export const getDatapath = (pathname, availableDatasets) => {
  if (!availableDatasets) {return undefined;}
  const parsedParams = parseParams(pathname, availableDatasets);
  if (parsedParams.valid) {
    return makeDataPathFromParsedParams(parsedParams);
  }
  return pathname.replace(/^\//, '').replace(/\/$/, '').replace('/'/g, '_');
};

export const chooseDisplayComponentFromPathname = (pathname) => {
  if (pathname === "/" || pathname === "/all") return "splash";
  else if (pathname.startsWith("/status")) return "status";
  return "app"; // fallthrough
};

/* changes the state of the page and (perhaps) the dataset displayed.
This function is used throughout the app for all navigation to another page, (including braowserBackForward - see function below)
The exception is for navigation requests that specify only the query changes, or that have an identical pathname to that selected.
Note that this function is not pure, in that it may change the URL

ARGUMENTS:
(1) path - REQUIRED - the destination path - e.g. "zika" or "flu/..." (does not include query)
(2) query - OPTIONAL (default: undefined) - see below
(3) push - OPTIONAL (default: true) - signals that pushState should be used (has no effect on the reducers)

UNDERSTANDING QUERY (SLIGHTLY CONFUSING)
This function changes the pathname (stored in the datasets reducer) and modifies the URL pathname and query
accordingly in the middleware. But the URL query is not processed further.
Because the datasets reducer has changed, the <App> (or whichever display component we're on) will update.
In <App>, this causes a call to loadJSONs, which will, as part of it's dispatch, use the URL state of query.
In this way, the URL query is "used".
*/
export const changePage = ({path, query = undefined, push = true}) => (dispatch, getState) => {
  if (!path) {console.error("changePage called without a path"); return;}
  const { datasets } = getState();
  const d = {
    type: PAGE_CHANGE,
    displayComponent: chooseDisplayComponentFromPathname(path),
    errorMessage: undefined
  };
  d.datapath = d.displayComponent === "app" ? getDatapath(path, datasets.availableDatasets) : undefined;
  if (query !== undefined) { d.query = query; }
  if (push) { d.pushState = true; }
  /* check if this is "valid" - we can change it here before it is dispatched */
  dispatch(d);
};

/* a 404 uses the same machinery as changePage, but it's not a thunk */
export const goTo404 = (errorMessage) => ({
  type: PAGE_CHANGE,
  displayComponent: "splash",
  errorMessage,
  pushState: true
});

/* modify redux state and URL by specifying a new URL query string. Pathname is not considered, if you want to change that, use "changePage" instead.
Unlike "changePage" the query is processed both by the middleware (i.e. to update the URL) AND by the reducers, to update their state accordingly.
ARGUMENTS:
(1) query - REQUIRED - {object}
(2) push - OPTIONAL (default: true) - signals that pushState should be used (has no effect on the reducers)
*/
export const changePageQuery = ({queryToUse, queryToDisplay = false, push = true}) => (dispatch, getState) => {
  const newState = createStateFromQueryOrJSONs({oldState: getState(), query: queryToUse});
  dispatch({
    type: URL_QUERY_CHANGE_WITH_COMPUTED_STATE,
    ...newState,
    pushState: push,
    query: queryToDisplay ? queryToDisplay : queryToUse
  });
};

export const browserBackForward = () => (dispatch, getState) => {
  const { datasets } = getState();
  /* if the pathname has changed, trigger the changePage action (will trigger new post to load, new dataset to load, etc) */
  // console.log("broswer back/forward detected. From: ", datasets.urlPath, datasets.urlSearch, "to:", window.location.pathname, window.location.search)
  if (datasets.urlPath !== window.location.pathname) {
    dispatch(changePage({path: window.location.pathname}));
  } else {
    dispatch(changePageQuery({queryToUse: queryString.parse(window.location.search)}));
  }
};
<|endoftext|>


<commit_before>/**
 * AlMighty page object example module for work item list page
 * See: http://martinfowler.com/bliki/PageObject.html,
 * https://www.thoughtworks.com/insights/blog/using-page-objects-overcome-protractors-shortcomings
 * @author ldimaggi@redhat.com
 * TODO - Complete the page object model pending completion of UI at: http://demo.almighty.io/
 */

'use strict';

/*
 * Constants Definition
 */

function define(name, value) {
    Object.defineProperty(exports, name, {
        value:      value,
        enumerable: true
    });
}

/* Timers */
define("WAIT", 3000);   /* 3 seconds */
define("LONG_WAIT", 30000);     /* 30 sec */
define("LONGER_WAIT", 300000);     /* 5 min */
define("LONGEST_WAIT", 600000);      /* 10 minutes */
define("PIPELINE_COMPLETE_WAIT", 6000000);      /* 100 minutes */
define("RESET_TENANT_WAIT", 300000);     /* 5 min */
define("JASMINE_TIMEOUT", 1200000);      /* 20 minutes */

/* Pre-test cleanup types */
define("CLEAN_CHE", "CLEAN_CHE");
define("CLEAN_STAGE_RUN", "CLEAN_STAGE_RUN");
define("CLEAN_ALL", "CLEAN_ALL");
<commit_msg>fix(test): increase timeout as tests are passing on production timing out on prod-preview<commit_after>/**
 * AlMighty page object example module for work item list page
 * See: http://martinfowler.com/bliki/PageObject.html,
 * https://www.thoughtworks.com/insights/blog/using-page-objects-overcome-protractors-shortcomings
 * @author ldimaggi@redhat.com
 * TODO - Complete the page object model pending completion of UI at: http://demo.almighty.io/
 */

'use strict';

/*
 * Constants Definition
 */

function define(name, value) {
    Object.defineProperty(exports, name, {
        value:      value,
        enumerable: true
    });
}

/* Timers */
define("WAIT", 3000);   /* 3 seconds */
define("LONG_WAIT", 30000);     /* 30 sec */
define("LONGER_WAIT", 300000);     /* 5 min */
define("LONGEST_WAIT", 900000);      /* 15 minutes */
define("PIPELINE_COMPLETE_WAIT", 6000000);      /* 100 minutes */
define("RESET_TENANT_WAIT", 300000);     /* 5 min */
define("JASMINE_TIMEOUT", 1200000);      /* 20 minutes */

/* Pre-test cleanup types */
define("CLEAN_CHE", "CLEAN_CHE");
define("CLEAN_STAGE_RUN", "CLEAN_STAGE_RUN");
define("CLEAN_ALL", "CLEAN_ALL");
<|endoftext|>


<commit_before>#!/usr/bin/env node

//
// This hook copies various resource files
// from our version control system directories
// into the appropriate platform specific location
//

// configure all the files to copy.
// Key of object is the source file,
// value is the destination location.
// It's fine to put all platforms' icons
// and splash screen files here, even if
// we don't build for all platforms
// on each developer's box.

var filestocopy = [{
    "extra_native_src/android/NativeSettings.java":
    "platforms/android/src/com/ionicframework/referencesidebarapp565061/NativeSettings.java"
}, {
    "extra_native_src/ios/NativeSettings.m":
    "platforms/ios/referenceSidebarApp/Classes/NativeSettings.m"
}, {
    "extra_native_src/ios/NativeSettings.h":
    "platforms/ios/referenceSidebarApp/Classes/NativeSettings.h"
}, {
    "extra_native_src/ios/NativeSettings.xib":
    "platforms/ios/referenceSidebarApp/Classes/NativeSettings.xib"
}];

var fs = require('fs');
var path = require('path');

// no need to configure below
var rootdir = process.argv[2];

filestocopy.forEach(function(obj) {
    Object.keys(obj).forEach(function(key) {
        var val = obj[key];
        var srcfile = path.join(rootdir, key);
        var destfile = path.join(rootdir, val);
        console.log("copying "+srcfile+" to "+destfile);
        var destdir = path.dirname(destfile);
        if (fs.existsSync(srcfile) && fs.existsSync(destdir)) {
            fs.createReadStream(srcfile).pipe(
               fs.createWriteStream(destfile));
        }
    });
});
<commit_msg>Add the xml layout files to the list of files to copy on android<commit_after>#!/usr/bin/env node

//
// This hook copies various resource files
// from our version control system directories
// into the appropriate platform specific location
//

// configure all the files to copy.
// Key of object is the source file,
// value is the destination location.
// It's fine to put all platforms' icons
// and splash screen files here, even if
// we don't build for all platforms
// on each developer's box.

var filestocopy = [{
    "extra_native_src/android/NativeSettings.java":
    "platforms/android/src/com/ionicframework/referencesidebarapp565061/NativeSettings.java"
}, {
    "extra_native_src/android/strings_activity_native_settings.xml":
    "platforms/android/res/values/strings_activity_native_settings.xml"
}, {
    "extra_native_src/android/pref_general.xml":
    "platforms/android/res/xml/pref_general.xml"
}, {
    "extra_native_src/android/pref_headers.xml":
    "platforms/android/res/xml/pref_headers.xml"
}, {
    "extra_native_src/android/pref_data_sync.xml":
    "platforms/android/res/xml/pref_data_sync.xml"
}, {
    "extra_native_src/android/pref_notification.xml":
    "platforms/android/res/xml/pref_notification.xml"
}, {
    "extra_native_src/ios/NativeSettings.m":
    "platforms/ios/referenceSidebarApp/Classes/NativeSettings.m"
}, {
    "extra_native_src/ios/NativeSettings.h":
    "platforms/ios/referenceSidebarApp/Classes/NativeSettings.h"
}, {
    "extra_native_src/ios/NativeSettings.xib":
    "platforms/ios/referenceSidebarApp/Classes/NativeSettings.xib"
}];

var fs = require('fs');
var path = require('path');

// no need to configure below
var rootdir = process.argv[2];

filestocopy.forEach(function(obj) {
    Object.keys(obj).forEach(function(key) {
        var val = obj[key];
        var srcfile = path.join(rootdir, key);
        var destfile = path.join(rootdir, val);
        console.log("copying "+srcfile+" to "+destfile);
        var destdir = path.dirname(destfile);
        if (fs.existsSync(srcfile) && fs.existsSync(destdir)) {
            fs.createReadStream(srcfile).pipe(
               fs.createWriteStream(destfile));
        }
    });
});
<|endoftext|>


<commit_before>/**
 * @ngdoc object
 * @name anol.layer.Feature
 *
 * @param {Object} options AnOl Layer options
 * @param {Object} options.olLayer Options for ol.layer.Vector
 * @param {Object} options.olLayer.source Options for ol.source.Vector
 *
 * @description
 * Inherits from {@link anol.layer.Layer anol.layer.Layer}.
 */
 anol.layer.Feature = function(_options) {
    if(_options === false) {
        anol.layer.Layer.call(this, _options);
        return;
    }
    var self = this;
    var defaults = {};
    var options = $.extend({},
        anol.layer.Layer.prototype.DEFAULT_OPTIONS,
        defaults,
        _options
    );
    options.olLayer.source = new ol.source.Vector(
        this._createSourceOptions(options.olLayer.source)
    );

    options.olLayer = new ol.layer.Vector(options.olLayer);

    anol.layer.Layer.call(this, options);

    this.defaultStyle = this.olLayer.getStyle();
    this.olLayer.setStyle(function(feature) {
        return [self.createStyle(feature)];
    });

    this.isVector = true;
    this.loaded = true;
    this.saveable = options.saveable || false;
    this.editable = options.editable || false;
};
anol.layer.Feature.prototype = new anol.layer.Layer(false);
$.extend(anol.layer.Feature.prototype, {
    CLASS_NAME: 'anol.layer.Feature',
    extent: function() {
        var extent = this.olLayer.getSource().getExtent();
        if(ol.extent.isEmpty(extent)) {
            return false;
        }
        return extent;
    },
    createStyle: function(feature, resolution) {
        var defaultStyle = angular.isFunction(this.defaultStyle) ?
            this.defaultStyle(feature, resolution)[0] : this.defaultStyle;

        if(feature === undefined) {
            return defaultStyle;
        }

        var geometryType = feature.getGeometry().getType();
        var geojsonStyle = feature.get('style');

        if(geojsonStyle === undefined || geojsonStyle === {}) {
            return defaultStyle;
        }
        if(geometryType === 'Point') {
            return new ol.style.Style({
                image: this.createImageStyle(geojsonStyle, defaultStyle.getImage())
            });
        } else {
            // line features ignores fill style
            return new ol.style.Style({
                fill: this.createFillStyle(geojsonStyle, defaultStyle.getFill()),
                stroke: this.createStrokeStyle(geojsonStyle, defaultStyle.getStroke())
            });
        }
    },
    createImageStyle: function(style, defaultImageStyle) {
        var isCircle = style.radius !== undefined;
        var isIcon = style.externalGraphic !== undefined;
        var isDefaultCircle = defaultImageStyle instanceof ol.style.Circle;
        var isDefaultIcon = defaultImageStyle instanceof ol.style.Icon;

        if(isCircle || !(isIcon && isDefaultCircle)) {
            return this.createCircleStyle(style, defaultImageStyle);
        } else if (isIcon || !(isCircle && isDefaultIcon)) {
            return this.createIconStyle(style, defaultImageStyle);
        }
        return defaultImageStyle;
    },
    createCircleStyle: function(style, defaultCircleStyle) {
        var defaultStrokeStyle = new ol.style.Stroke();
        var defaultFillStyle = new ol.style.Fill();
        if(defaultCircleStyle instanceof ol.style.Circle) {
            defaultStrokeStyle = defaultCircleStyle.getStroke();
            defaultFillStyle = defaultCircleStyle.getFill();
        }
        var radius = defaultCircleStyle.getRadius();
        if(style.radius !== undefined) {
            radius = parseFloat(style.radius);
        }
        return new ol.style.Circle({
            radius: radius,
            stroke: this.createStrokeStyle(style, defaultStrokeStyle),
            fill: this.createFillStyle(style, defaultFillStyle)
        });
    },
    createIconStyle: function(style, _defaultIconStyle) {
        var defaultIconStyle = new ol.style.Icon();
        if(_defaultIconStyle instanceof ol.style.Icon) {
            defaultIconStyle = _defaultIconStyle;
        }
        var src = defaultIconStyle.getSrc();
        if(style.externalGraphic !== undefined) {
            src = style.externalGraphic;
        }
        var rotation = defaultIconStyle.getRotation();
        if(style.rotation !== undefined) {
            rotation = parseFloat(style.rotation);
        }
        var scale = defaultIconStyle.getScale();
        if(style.scale !== undefined) {
            scale = parseFloat(style.scale);
        }

        var iconStyle = new ol.style.Icon(({
            src: src,
            rotation: rotation
        }));
        if(scale === undefined && style.graphicWidth !== undefined) {
            // get original image size
            var size = iconStyle.getSize();
            if (size !== null) {
                scale = parseInt(style.graphicWidth) / size[0];
            }
        }
        iconStyle.setScale(scale);
        return iconStyle;
    },
    createFillStyle: function(style, defaultFillStyle) {
        var color = ol.color.asArray(defaultFillStyle.getColor()).slice();
        if (style.fillColor !== undefined) {
            var fillColor = ol.color.asArray(style.fillColor);
            color[0] = fillColor[0];
            color[1] = fillColor[1];
            color[2] = fillColor[2];
        }
        if(style.fillOpacity !== undefined) {
            color[3] = parseFloat(style.fillOpacity);
        }
        return new ol.style.Fill({
            color: color
        });
    },
    createStrokeStyle: function(style, defaultStrokeStyle) {
        var color = ol.color.asArray(defaultStrokeStyle.getColor()).slice();
        var strokeWidth = defaultStrokeStyle.getWidth();
        var strokeDashstyle = defaultStrokeStyle.getLineDash();

        if(style.strokeColor !== undefined) {
            var strokeColor = ol.color.asArray(style.strokeColor);
            color[0] = strokeColor[0];
            color[1] = strokeColor[1];
            color[2] = strokeColor[2];
        }
        if(style.strokeOpacity !== undefined) {
            color[3] = parseFloat(style.strokeOpacity);
        }
        if(style.strokeWidth !== undefined) {
            strokeWidth = parseFloat(style.strokeWidth);
        }
        if(style.strokeDashstyle !== undefined) {
            strokeDashstyle = this.createDashStyle(strokeWidth, style.strokeDashstyle);
        }

        return new ol.style.Stroke({
            color: color,
            width: strokeWidth,
            lineDash: strokeDashstyle,
            lineJoin: 'round'
        });
    },
    // Taken from OpenLayers 2.13.1
    // see https://github.com/openlayers/openlayers/blob/release-2.13.1/lib/OpenLayers/Renderer/SVG.js#L391
    createDashStyle: function(strokeWidth, strokeDashstyle) {
        var w = strokeWidth;
        var str = strokeDashstyle;
        switch (str) {
            case 'dot':
                return [1, 4 * w];
            case 'dash':
                return [4 * w, 4 * w];
            case 'dashdot':
                return [4 * w, 4 * w, 1, 4 * w];
            case 'longdash':
                return [8 * w, 4 * w];
            case 'longdashdot':
                return [8 * w, 4 * w, 1, 4 * w];
            // also matches 'solid'
            default:
                return undefined;
          }
    }
    // TODO add getProperties method including handling of hidden properties like style
    // TODO add hasProperty method
});
<commit_msg>Added default style from json to anol.layer.Feature<commit_after>/**
 * @ngdoc object
 * @name anol.layer.Feature
 *
 * @param {Object} options AnOl Layer options
 * @param {Object} options.olLayer Options for ol.layer.Vector
 * @param {Object} options.olLayer.source Options for ol.source.Vector
 *
 * @description
 * Inherits from {@link anol.layer.Layer anol.layer.Layer}.
 */
 anol.layer.Feature = function(_options) {
    if(_options === false) {
        anol.layer.Layer.call(this, _options);
        return;
    }
    var self = this;
    var defaults = {};
    var options = $.extend({},
        anol.layer.Layer.prototype.DEFAULT_OPTIONS,
        defaults,
        _options
    );
    options.olLayer.source = new ol.source.Vector(
        this._createSourceOptions(options.olLayer.source)
    );

    options.olLayer = new ol.layer.Vector(options.olLayer);

    anol.layer.Layer.call(this, options);

    this.style = options.style || {};
    this.defaultStyle = this.olLayer.getStyle();
    this.olLayer.setStyle(function(feature) {
        return [self.createStyle(feature)];
    });

    this.isVector = true;
    this.loaded = true;
    this.saveable = options.saveable || false;
    this.editable = options.editable || false;
};
anol.layer.Feature.prototype = new anol.layer.Layer(false);
$.extend(anol.layer.Feature.prototype, {
    CLASS_NAME: 'anol.layer.Feature',
    extent: function() {
        var extent = this.olLayer.getSource().getExtent();
        if(ol.extent.isEmpty(extent)) {
            return false;
        }
        return extent;
    },
    createStyle: function(feature, resolution) {
        var defaultStyle = angular.isFunction(this.defaultStyle) ?
            this.defaultStyle(feature, resolution)[0] : this.defaultStyle;

        if(feature === undefined) {
            return defaultStyle;
        }

        var geometryType = feature.getGeometry().getType();
        var geojsonStyle = feature.get('style') || {};
        if(geojsonStyle === {} && this.style === {}) {
            return defaultStyle;
        }
        if(geometryType === 'Point') {
            return new ol.style.Style({
                image: this.createImageStyle(geojsonStyle, defaultStyle.getImage())
            });
        } else {
            // line features ignores fill style
            return new ol.style.Style({
                fill: this.createFillStyle(geojsonStyle, defaultStyle.getFill()),
                stroke: this.createStrokeStyle(geojsonStyle, defaultStyle.getStroke())
            });
        }
    },
    createImageStyle: function(style, defaultImageStyle) {
        var radius = style.radius || this.style.radius;
        var externalGraphic = style.externalGraphic || this.style.externalGraphic;

        var isCircle = radius !== undefined;
        var isIcon = externalGraphic !== undefined;
        var isDefaultCircle = defaultImageStyle instanceof ol.style.Circle;
        var isDefaultIcon = defaultImageStyle instanceof ol.style.Icon;

        if(isCircle || !(isIcon && isDefaultCircle)) {
            return this.createCircleStyle(style, defaultImageStyle);
        } else if (isIcon || !(isCircle && isDefaultIcon)) {
            return this.createIconStyle(style, defaultImageStyle);
        }
        return defaultImageStyle;
    },
    createCircleStyle: function(style, defaultCircleStyle) {
        var defaultStrokeStyle = new ol.style.Stroke();
        var defaultFillStyle = new ol.style.Fill();
        if(defaultCircleStyle instanceof ol.style.Circle) {
            defaultStrokeStyle = defaultCircleStyle.getStroke();
            defaultFillStyle = defaultCircleStyle.getFill();
        }
        var radius = defaultCircleStyle.getRadius();

        var _radius = style.radius || this.style.radius;
        if(_radius !== undefined) {
            radius = parseFloat(_radius);
        }
        return new ol.style.Circle({
            radius: radius,
            stroke: this.createStrokeStyle(style, defaultStrokeStyle),
            fill: this.createFillStyle(style, defaultFillStyle)
        });
    },
    createIconStyle: function(style, _defaultIconStyle) {
        var defaultIconStyle = new ol.style.Icon();
        if(_defaultIconStyle instanceof ol.style.Icon) {
            defaultIconStyle = _defaultIconStyle;
        }
        var src = defaultIconStyle.getSrc();

        var externalGraphic = style.externalGraphic || this.style.externalGraphic;
        if(externalGraphic !== undefined) {
            src = externalGraphic;
        }
        var rotation = defaultIconStyle.getRotation();
        var _rotation = style.rotation || this.style.rotation;
        if(_rotation !== undefined) {
            rotation = parseFloat(_rotation);
        }
        var scale = defaultIconStyle.getScale();
        var _scale = style.scale || this.style.scale;
        if(_scale !== undefined) {
            scale = parseFloat(_scale);
        }

        var iconStyle = new ol.style.Icon(({
            src: src,
            rotation: rotation
        }));
        var graphicWidth = style.graphicWidth || this.style.graphicWidth;
        if(scale === undefined && graphicWidth !== undefined) {
            // get original image size
            var size = iconStyle.getSize();
            if (size !== null) {
                scale = parseInt(graphicWidth) / size[0];
            }
        }
        iconStyle.setScale(scale);
        return iconStyle;
    },
    createFillStyle: function(style, defaultFillStyle) {
        var color = ol.color.asArray(defaultFillStyle.getColor()).slice();
        var fillColor = style.fillColor || this.style.fillColor;
        if (fillColor !== undefined) {
            fillColor = ol.color.asArray(fillColor);
            color[0] = fillColor[0];
            color[1] = fillColor[1];
            color[2] = fillColor[2];
        }
        var fillOpacity = style.fillOpacity || this.style.fillOpacity;
        if(fillOpacity !== undefined) {
            color[3] = parseFloat(fillOpacity);
        }
        return new ol.style.Fill({
            color: color
        });
    },
    createStrokeStyle: function(style, defaultStrokeStyle) {
        var color = ol.color.asArray(defaultStrokeStyle.getColor()).slice();
        var strokeWidth = defaultStrokeStyle.getWidth();
        var strokeDashstyle = defaultStrokeStyle.getLineDash();

        var strokeColor = style.strokeColor || this.style.strokeColor;
        if(strokeColor !== undefined) {
            strokeColor = ol.color.asArray(strokeColor);
            color[0] = strokeColor[0];
            color[1] = strokeColor[1];
            color[2] = strokeColor[2];
        }
        var strokeOpacity = style.strokeOpacity || this.style.strokeOpacity;
        if(strokeOpacity !== undefined) {
            color[3] = parseFloat(strokeOpacity);
        }
        var _strokeWidth = style.strokeWidth || this.style.strokeWidth;
        if(_strokeWidth !== undefined) {
            strokeWidth = parseFloat(_strokeWidth);
        }
        var _strokeDashstyle = style.strokeDashstyle || this.style.strokeDashstyle;
        if(_strokeDashstyle !== undefined) {
            strokeDashstyle = this.createDashStyle(strokeWidth, _strokeDashstyle);
        }

        return new ol.style.Stroke({
            color: color,
            width: strokeWidth,
            lineDash: strokeDashstyle,
            lineJoin: 'round'
        });
    },
    // Taken from OpenLayers 2.13.1
    // see https://github.com/openlayers/openlayers/blob/release-2.13.1/lib/OpenLayers/Renderer/SVG.js#L391
    createDashStyle: function(strokeWidth, strokeDashstyle) {
        var w = strokeWidth;
        var str = strokeDashstyle;
        switch (str) {
            case 'dot':
                return [1, 4 * w];
            case 'dash':
                return [4 * w, 4 * w];
            case 'dashdot':
                return [4 * w, 4 * w, 1, 4 * w];
            case 'longdash':
                return [8 * w, 4 * w];
            case 'longdashdot':
                return [8 * w, 4 * w, 1, 4 * w];
            // also matches 'solid'
            default:
                return undefined;
          }
    }
    // TODO add getProperties method including handling of hidden properties like style
    // TODO add hasProperty method
});
<|endoftext|>


<commit_before>import crypto from 'crypto'
import logger from 'winston'
import atna from 'atna-audit'
import os from 'os'
import { UserModelAPI } from '../model/users'
import { config } from '../config'
import * as auditing from '../auditing'
import _ from 'lodash'

config.api = config.get('api')
config.auditing = config.get('auditing')
const himSourceID = config.auditing.auditEvents.auditSourceID

// will NOT audit any successful logins on the following paths (specified as regex patterns)
// only 'noisy' endpoints should be included, such as heartbeats or endpoints that get polled
//
// /transactions is treated as a special case - see below
const auditingExemptPaths = [
  /\/tasks/,
  /\/events.*/,
  /\/metrics.*/,
  /\/mediators\/.*\/heartbeat/,
  /\/audits/,
  /\/logs/
]

const isUndefOrEmpty = string => (string == null) || (string === '')

export async function authenticate (ctx, next) {
  const { header } = ctx.request
  const email = header['auth-username']
  const authTS = header['auth-ts']
  const authSalt = header['auth-salt']
  const authToken = header['auth-token']

  function auditAuthFailure () {
    let audit = atna.construct.userLoginAudit(atna.constants.OUTCOME_SERIOUS_FAILURE, himSourceID, os.hostname(), email)
    audit = atna.construct.wrapInSyslog(audit)
    return auditing.sendAuditEvent(audit, () => logger.debug('Processed internal audit'))
  }

  // if any of the required headers aren't present
  if (isUndefOrEmpty(email) || isUndefOrEmpty(authTS) || isUndefOrEmpty(authSalt) || isUndefOrEmpty(authToken)) {
    logger.info(`API request made by ${email} from ${ctx.request.host} is missing required API authentication headers, denying access`)
    ctx.status = 401
    auditAuthFailure()
    return
  }

  // check if request is recent
  const requestDate = new Date(Date.parse(authTS))

  const authWindowSeconds = config.api.authWindowSeconds != null ? config.api.authWindowSeconds : 10
  const to = new Date()
  to.setSeconds(to.getSeconds() + authWindowSeconds)
  const from = new Date()
  from.setSeconds(from.getSeconds() - authWindowSeconds)

  if ((requestDate < from) || (requestDate > to)) {
    // request expired
    logger.info(`API request made by ${email} from ${ctx.request.host} has expired, denying access`)
    ctx.status = 401
    auditAuthFailure()
    return
  }
  const emailReg = new RegExp(_.escapeRegExp(email), 'i')
  const user = await UserModelAPI.findOne({ email: emailReg }).exec()
  ctx.authenticated = user

  if (!user) {
    // not authenticated - user not found
    logger.info(`No user exists for ${email}, denying access to API, request originated from ${ctx.request.host}`)
    ctx.status = 401
    auditAuthFailure()
    return
  }

  const hash = crypto.createHash('sha512')
  hash.update(user.passwordHash)
  hash.update(authSalt)
  hash.update(authTS)

  if (authToken === hash.digest('hex')) {
    // authenticated

    if (ctx.path === '/transactions') {
      if (!ctx.query.filterRepresentation || (ctx.query.filterRepresentation !== 'full')) {
        // exempt from auditing success
        await next()
        return
      }
    } else {
      for (const pathTest of Array.from(auditingExemptPaths)) {
        if (pathTest.test(ctx.path)) {
          // exempt from auditing success
          await next()
          return
        }
      }
    }

    // send audit
    let audit = atna.construct.userLoginAudit(atna.constants.OUTCOME_SUCCESS, himSourceID, os.hostname(), email, user.groups.join(','), user.groups.join(','))
    audit = atna.construct.wrapInSyslog(audit)
    auditing.sendAuditEvent(audit, () => logger.debug('Processed internal audit'))
    await next()
  } else {
    // not authenticated - token mismatch
    logger.info(`API token did not match expected value, denying access to API, the request was made by ${email} from ${ctx.request.host}`)
    ctx.status = 401
    return auditAuthFailure()
  }
}
<commit_msg>OHM-526 Fix failing tests<commit_after>import crypto from 'crypto'
import logger from 'winston'
import atna from 'atna-audit'
import os from 'os'
import { UserModelAPI } from '../model/users'
import { config } from '../config'
import * as auditing from '../auditing'
import _ from 'lodash'

config.api = config.get('api')
config.auditing = config.get('auditing')
const himSourceID = config.auditing.auditEvents.auditSourceID

// will NOT audit any successful logins on the following paths (specified as regex patterns)
// only 'noisy' endpoints should be included, such as heartbeats or endpoints that get polled
//
// /transactions is treated as a special case - see below
const auditingExemptPaths = [
  /\/tasks/,
  /\/events.*/,
  /\/metrics.*/,
  /\/mediators\/.*\/heartbeat/,
  /\/audits/,
  /\/logs/
]

const isUndefOrEmpty = string => (string == null) || (string === '')

export async function authenticate (ctx, next) {
  const { header } = ctx.request
  const email = header['auth-username']
  const authTS = header['auth-ts']
  const authSalt = header['auth-salt']
  const authToken = header['auth-token']

  function auditAuthFailure () {
    let audit = atna.construct.userLoginAudit(atna.constants.OUTCOME_SERIOUS_FAILURE, himSourceID, os.hostname(), email)
    audit = atna.construct.wrapInSyslog(audit)
    return auditing.sendAuditEvent(audit, () => logger.debug('Processed internal audit'))
  }

  // if any of the required headers aren't present
  if (isUndefOrEmpty(email) || isUndefOrEmpty(authTS) || isUndefOrEmpty(authSalt) || isUndefOrEmpty(authToken)) {
    logger.info(`API request made by ${email} from ${ctx.request.host} is missing required API authentication headers, denying access`)
    ctx.status = 401
    auditAuthFailure()
    return
  }

  // check if request is recent
  const requestDate = new Date(Date.parse(authTS))

  const authWindowSeconds = config.api.authWindowSeconds != null ? config.api.authWindowSeconds : 10
  const to = new Date()
  to.setSeconds(to.getSeconds() + authWindowSeconds)
  const from = new Date()
  from.setSeconds(from.getSeconds() - authWindowSeconds)

  if ((requestDate < from) || (requestDate > to)) {
    // request expired
    logger.info(`API request made by ${email} from ${ctx.request.host} has expired, denying access`)
    ctx.status = 401
    auditAuthFailure()
    return
  }
  const emailReg = new RegExp(`^${_.escapeRegExp(email)}$`, 'i')
  const user = await UserModelAPI.findOne({ email : emailReg })
  ctx.authenticated = user

  if (!user) {
    // not authenticated - user not found
    logger.info(`No user exists for ${email}, denying access to API, request originated from ${ctx.request.host}`)
    ctx.status = 401
    auditAuthFailure()
    return
  }

  const hash = crypto.createHash('sha512')
  hash.update(user.passwordHash)
  hash.update(authSalt)
  hash.update(authTS)

  if (authToken === hash.digest('hex')) {
    // authenticated

    if (ctx.path === '/transactions') {
      if (!ctx.query.filterRepresentation || (ctx.query.filterRepresentation !== 'full')) {
        // exempt from auditing success
        await next()
        return
      }
    } else {
      for (const pathTest of Array.from(auditingExemptPaths)) {
        if (pathTest.test(ctx.path)) {
          // exempt from auditing success
          await next()
          return
        }
      }
    }

    // send audit
    let audit = atna.construct.userLoginAudit(atna.constants.OUTCOME_SUCCESS, himSourceID, os.hostname(), email, user.groups.join(','), user.groups.join(','))
    audit = atna.construct.wrapInSyslog(audit)
    auditing.sendAuditEvent(audit, () => logger.debug('Processed internal audit'))
    await next()
  } else {
    // not authenticated - token mismatch
    logger.info(`API token did not match expected value, denying access to API, the request was made by ${email} from ${ctx.request.host}`)
    ctx.status = 401
    return auditAuthFailure()
  }
}
<|endoftext|>


<commit_before>const BaseHistogram = require('./base-histogram');
const debug = require('debug')('windshaft:dataview:date-histogram');

const dateIntervalQueryTpl = ctx => `
    WITH
    __cdb_dates AS (
        SELECT
            MAX(${ctx.column}::timestamp) AS __cdb_end,
            MIN(${ctx.column}::timestamp) AS __cdb_start
        FROM (${ctx.query}) __cdb_source
    ),
    __cdb_interval_in_days AS (
        SELECT
            DATE_PART('day', __cdb_end - __cdb_start) AS __cdb_days
        FROM __cdb_dates
    ),
    __cdb_interval_in_hours AS (
        SELECT
            __cdb_days * 24 + DATE_PART('hour', __cdb_end - __cdb_start) AS __cdb_hours
        FROM __cdb_interval_in_days, __cdb_dates
    ),
    __cdb_interval_in_minutes AS (
        SELECT
            __cdb_hours * 60 + DATE_PART('minute', __cdb_end - __cdb_start) AS __cdb_minutes
        FROM __cdb_interval_in_hours, __cdb_dates
    ),
    __cdb_interval_in_seconds AS (
        SELECT
            __cdb_minutes * 60 + DATE_PART('second', __cdb_end - __cdb_start) AS __cdb_seconds
        FROM __cdb_interval_in_minutes, __cdb_dates
    )
    SELECT
        ROUND(__cdb_days / 365) AS year,
        ROUND(__cdb_days / 90) AS quarter,
        ROUND(__cdb_days / 30) AS month,
        ROUND(__cdb_days / 7) AS week,
        __cdb_days AS day,
        __cdb_hours AS hour,
        __cdb_minutes AS minute,
        __cdb_seconds AS second
    FROM __cdb_interval_in_days, __cdb_interval_in_hours, __cdb_interval_in_minutes, __cdb_interval_in_seconds
`;

const nullsQueryTpl = ctx => `
    __cdb_nulls AS (
        SELECT
            count(*) AS __cdb_nulls_count
        FROM (${ctx._query}) __cdb_histogram_nulls
        WHERE ${ctx._column} IS NULL
    )
`;

const dateBasicsQueryTpl = ctx => `
    __cdb_basics AS (
        SELECT
            max(date_part('epoch', ${ctx._column})) AS __cdb_max_val,
            min(date_part('epoch', ${ctx._column})) AS __cdb_min_val,
            avg(date_part('epoch', ${ctx._column})) AS __cdb_avg_val,
            min(
                date_trunc(
                    '${ctx._aggregation}', ${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}'
                )
            ) AS __cdb_start_date,
            max(${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}') AS __cdb_end_date,
            count(1) AS __cdb_total_rows
        FROM (${ctx._query}) __cdb_basics_query
    )
`;

const dateOverrideBasicsQueryTpl = ctx => `
    __cdb_basics AS (
        SELECT
            max(${ctx._end}) AS __cdb_max_val,
            min(${ctx._start}) AS __cdb_min_val,
            avg(date_part('epoch', ${ctx._column})) AS __cdb_avg_val,
            min(
                date_trunc(
                    '${ctx._aggregation}',
                    TO_TIMESTAMP(${ctx._start})::timestamp AT TIME ZONE '${ctx._offset}'
                )
            ) AS __cdb_start_date,
            max(
                TO_TIMESTAMP(${ctx._end})::timestamp AT TIME ZONE '${ctx._offset}'
            ) AS __cdb_end_date,
            count(1) AS __cdb_total_rows
        FROM (${ctx._query}) __cdb_basics_query
    )
`;

const dateBinsQueryTpl = ctx => `
    __cdb_bins AS (
        SELECT
            __cdb_bins_array,
            ARRAY_LENGTH(__cdb_bins_array, 1) AS __cdb_bins_number
        FROM (
            SELECT
                ARRAY(
                    SELECT GENERATE_SERIES(
                        __cdb_start_date::timestamptz,
                        __cdb_end_date::timestamptz,
                        ${ctx._aggregation === 'quarter' ? `'3 month'::interval` : `'1 ${ctx._aggregation}'::interval`}
                    )
                ) AS __cdb_bins_array
            FROM __cdb_basics
        ) __cdb_bins_array_query
    )
`;

const dateHistogramQueryTpl = ctx => `
    SELECT
        (__cdb_max_val - __cdb_min_val) / cast(__cdb_bins_number as float) AS bin_width,
        __cdb_bins_number AS bins_number,
        __cdb_nulls_count AS nulls_count,
        CASE WHEN __cdb_min_val = __cdb_max_val
        THEN 0
        ELSE GREATEST(
            1,
            LEAST(
                WIDTH_BUCKET(
                    ${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}',
                    __cdb_bins_array
                ),
                __cdb_bins_number
            )
        ) - 1
        END AS bin,
        min(
            date_part(
                'epoch',
                date_trunc(
                    '${ctx._aggregation}', ${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}'
                ) AT TIME ZONE '${ctx._offset}'
            )
        )::numeric AS timestamp,
        date_part('epoch', __cdb_start_date)::numeric AS timestamp_start,
        min(date_part('epoch', ${ctx._column}))::numeric AS min,
        max(date_part('epoch', ${ctx._column}))::numeric AS max,
        avg(date_part('epoch', ${ctx._column}))::numeric AS avg,
        count(*) AS freq
    FROM (${ctx._query}) __cdb_histogram, __cdb_basics, __cdb_bins, __cdb_nulls
    WHERE date_part('epoch', ${ctx._column}) IS NOT NULL
    GROUP BY bin, bins_number, bin_width, nulls_count, timestamp_start
    ORDER BY bin
`;

const MAX_INTERVAL_VALUE = 366;

const DATE_AGGREGATIONS = {
    'auto': true,
    'minute': true,
    'hour': true,
    'day': true,
    'week': true,
    'month': true,
    'quarter': true,
    'year': true
};

/**
    date_histogram: {
        type: 'histogram',
        options: {
            column: 'date', // column data type: date
            aggregation: 'day' // auto by default
            offset: -7200 // OPTIONAL (UTC offset in seconds)
        }
    }
*/
module.exports = class DateHistogram extends BaseHistogram {
    constructor (query, options, queries) {
        super(query, options, queries);

        this.aggregation = options.aggregation;
        this.offset = options.offset;
    }

    _buildQueryTpl (ctx) {
        return `
            WITH
            ${ctx._override && ctx._override.hasOwnProperty('start') && ctx._override.hasOwnProperty('end') ?
                dateOverrideBasicsQueryTpl(ctx) :
                dateBasicsQueryTpl(ctx)},
            ${dateBinsQueryTpl(ctx)},
            ${nullsQueryTpl(ctx)}
            ${dateHistogramQueryTpl(ctx)}
        `;
    }

    _buildQuery (psql, override, callback) {
        if (!DATE_AGGREGATIONS.hasOwnProperty(this._getAggregation(override))) {
            return callback(new Error('Invalid aggregation value. Valid ones: ' +
                Object.keys(DATE_AGGREGATIONS).join(', ')
            ));
        }

        if (this._getAggregation(override) === 'auto') {
            this._getAutomaticAggregation(psql, function (err, aggregation) {
                if (err || aggregation === 'none') {
                    this.aggregation = 'day';
                } else {
                    this.aggregation = aggregation;
                }
                override.aggregation = this.aggregation;
                this._buildQuery(psql, override, callback);
            }.bind(this));
            return null;
        }

        const histogramSql = this._buildQueryTpl({
            _override: override,
            _query: this.query,
            _column: this.column,
            _aggregation: this._getAggregation(override),
            _start: this._getBinStart(override),
            _end: this._getBinEnd(override),
            _offset: this._parseOffset(override)
        });

        debug(histogramSql);

        return callback(null, histogramSql);
    }

    _getAutomaticAggregation (psql, callback) {
        const dateIntervalQuery = dateIntervalQueryTpl({
            query: this.query,
            column: this.column
        });

        psql.query(dateIntervalQuery, function (err, result) {
            if (err) {
                return callback(err);
            }

            const aggegations = result.rows[0];
            const aggregation = Object.keys(aggegations)
                .map(function (key) {
                    return {
                        name: key,
                        value: aggegations[key]
                    };
                })
                .reduce(function (closer, current) {
                    if (current.value > MAX_INTERVAL_VALUE) {
                        return closer;
                    }

                    const closerDiff = MAX_INTERVAL_VALUE - closer.value;
                    const currentDiff = MAX_INTERVAL_VALUE - current.value;

                    if (Number.isFinite(current.value) && closerDiff > currentDiff) {
                        return current;
                    }

                    return closer;
                }, { name: 'none', value: -1 });

            callback(null, aggregation.name);
        });
    }

    _getSummary (result, override) {
        const firstRow = result.rows[0] || {};

        return {
            aggregation: this._getAggregation(override),
            offset: this._getOffset(override),
            timestamp_start: firstRow.timestamp_start,

            bin_width: firstRow.bin_width,
            bins_count: firstRow.bins_number,
            bins_start: firstRow.timestamp,
            nulls: firstRow.nulls_count,
            infinities: firstRow.infinities_count,
            nans: firstRow.nans_count,
            avg: firstRow.avg_val
        };
    }

    _getBuckets (result) {
        return result.rows.map(row => {
            const { bin, min, max, avg, freq, timestamp } = row;
            return { bin, min, max, avg, freq, timestamp };
        });
    }

    _getAggregation (override = {}) {
        return override.aggregation ? override.aggregation : this.aggregation;
    }

    _getOffset (override = {}) {
        return Number.isFinite(override.offset) ? override.offset : (this.offset || 0);
    }

    _parseOffset (override) {
        if (this._shouldIgnoreOffset(override)) {
            return '0';
        }

        const offsetInHours = Math.ceil(this._getOffset(override) / 3600);

        return '' + offsetInHours;
    }

    _shouldIgnoreOffset (override) {
        return (this._getAggregation(override) === 'hour' || this._getAggregation(override) === 'minute');
    }
};
<commit_msg>Extract method to check valid aggregation<commit_after>const BaseHistogram = require('./base-histogram');
const debug = require('debug')('windshaft:dataview:date-histogram');

const dateIntervalQueryTpl = ctx => `
    WITH
    __cdb_dates AS (
        SELECT
            MAX(${ctx.column}::timestamp) AS __cdb_end,
            MIN(${ctx.column}::timestamp) AS __cdb_start
        FROM (${ctx.query}) __cdb_source
    ),
    __cdb_interval_in_days AS (
        SELECT
            DATE_PART('day', __cdb_end - __cdb_start) AS __cdb_days
        FROM __cdb_dates
    ),
    __cdb_interval_in_hours AS (
        SELECT
            __cdb_days * 24 + DATE_PART('hour', __cdb_end - __cdb_start) AS __cdb_hours
        FROM __cdb_interval_in_days, __cdb_dates
    ),
    __cdb_interval_in_minutes AS (
        SELECT
            __cdb_hours * 60 + DATE_PART('minute', __cdb_end - __cdb_start) AS __cdb_minutes
        FROM __cdb_interval_in_hours, __cdb_dates
    ),
    __cdb_interval_in_seconds AS (
        SELECT
            __cdb_minutes * 60 + DATE_PART('second', __cdb_end - __cdb_start) AS __cdb_seconds
        FROM __cdb_interval_in_minutes, __cdb_dates
    )
    SELECT
        ROUND(__cdb_days / 365) AS year,
        ROUND(__cdb_days / 90) AS quarter,
        ROUND(__cdb_days / 30) AS month,
        ROUND(__cdb_days / 7) AS week,
        __cdb_days AS day,
        __cdb_hours AS hour,
        __cdb_minutes AS minute,
        __cdb_seconds AS second
    FROM __cdb_interval_in_days, __cdb_interval_in_hours, __cdb_interval_in_minutes, __cdb_interval_in_seconds
`;

const nullsQueryTpl = ctx => `
    __cdb_nulls AS (
        SELECT
            count(*) AS __cdb_nulls_count
        FROM (${ctx._query}) __cdb_histogram_nulls
        WHERE ${ctx._column} IS NULL
    )
`;

const dateBasicsQueryTpl = ctx => `
    __cdb_basics AS (
        SELECT
            max(date_part('epoch', ${ctx._column})) AS __cdb_max_val,
            min(date_part('epoch', ${ctx._column})) AS __cdb_min_val,
            avg(date_part('epoch', ${ctx._column})) AS __cdb_avg_val,
            min(
                date_trunc(
                    '${ctx._aggregation}', ${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}'
                )
            ) AS __cdb_start_date,
            max(${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}') AS __cdb_end_date,
            count(1) AS __cdb_total_rows
        FROM (${ctx._query}) __cdb_basics_query
    )
`;

const dateOverrideBasicsQueryTpl = ctx => `
    __cdb_basics AS (
        SELECT
            max(${ctx._end}) AS __cdb_max_val,
            min(${ctx._start}) AS __cdb_min_val,
            avg(date_part('epoch', ${ctx._column})) AS __cdb_avg_val,
            min(
                date_trunc(
                    '${ctx._aggregation}',
                    TO_TIMESTAMP(${ctx._start})::timestamp AT TIME ZONE '${ctx._offset}'
                )
            ) AS __cdb_start_date,
            max(
                TO_TIMESTAMP(${ctx._end})::timestamp AT TIME ZONE '${ctx._offset}'
            ) AS __cdb_end_date,
            count(1) AS __cdb_total_rows
        FROM (${ctx._query}) __cdb_basics_query
    )
`;

const dateBinsQueryTpl = ctx => `
    __cdb_bins AS (
        SELECT
            __cdb_bins_array,
            ARRAY_LENGTH(__cdb_bins_array, 1) AS __cdb_bins_number
        FROM (
            SELECT
                ARRAY(
                    SELECT GENERATE_SERIES(
                        __cdb_start_date::timestamptz,
                        __cdb_end_date::timestamptz,
                        ${ctx._aggregation === 'quarter' ? `'3 month'::interval` : `'1 ${ctx._aggregation}'::interval`}
                    )
                ) AS __cdb_bins_array
            FROM __cdb_basics
        ) __cdb_bins_array_query
    )
`;

const dateHistogramQueryTpl = ctx => `
    SELECT
        (__cdb_max_val - __cdb_min_val) / cast(__cdb_bins_number as float) AS bin_width,
        __cdb_bins_number AS bins_number,
        __cdb_nulls_count AS nulls_count,
        CASE WHEN __cdb_min_val = __cdb_max_val
        THEN 0
        ELSE GREATEST(
            1,
            LEAST(
                WIDTH_BUCKET(
                    ${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}',
                    __cdb_bins_array
                ),
                __cdb_bins_number
            )
        ) - 1
        END AS bin,
        min(
            date_part(
                'epoch',
                date_trunc(
                    '${ctx._aggregation}', ${ctx._column}::timestamp AT TIME ZONE '${ctx._offset}'
                ) AT TIME ZONE '${ctx._offset}'
            )
        )::numeric AS timestamp,
        date_part('epoch', __cdb_start_date)::numeric AS timestamp_start,
        min(date_part('epoch', ${ctx._column}))::numeric AS min,
        max(date_part('epoch', ${ctx._column}))::numeric AS max,
        avg(date_part('epoch', ${ctx._column}))::numeric AS avg,
        count(*) AS freq
    FROM (${ctx._query}) __cdb_histogram, __cdb_basics, __cdb_bins, __cdb_nulls
    WHERE date_part('epoch', ${ctx._column}) IS NOT NULL
    GROUP BY bin, bins_number, bin_width, nulls_count, timestamp_start
    ORDER BY bin
`;

const MAX_INTERVAL_VALUE = 366;

const DATE_AGGREGATIONS = {
    'auto': true,
    'minute': true,
    'hour': true,
    'day': true,
    'week': true,
    'month': true,
    'quarter': true,
    'year': true
};

/**
    date_histogram: {
        type: 'histogram',
        options: {
            column: 'date', // column data type: date
            aggregation: 'day' // auto by default
            offset: -7200 // OPTIONAL (UTC offset in seconds)
        }
    }
*/
module.exports = class DateHistogram extends BaseHistogram {
    constructor (query, options, queries) {
        super(query, options, queries);

        this.aggregation = options.aggregation;
        this.offset = options.offset;
    }

    _buildQueryTpl (ctx) {
        return `
            WITH
            ${ctx._override && ctx._override.hasOwnProperty('start') && ctx._override.hasOwnProperty('end') ?
                dateOverrideBasicsQueryTpl(ctx) :
                dateBasicsQueryTpl(ctx)},
            ${dateBinsQueryTpl(ctx)},
            ${nullsQueryTpl(ctx)}
            ${dateHistogramQueryTpl(ctx)}
        `;
    }

    _buildQuery (psql, override, callback) {
        if (!this.isValidAggregation(override)) {
            return callback(new Error('Invalid aggregation value. Valid ones: ' +
                Object.keys(DATE_AGGREGATIONS).join(', ')
            ));
        }

        if (this._getAggregation(override) === 'auto') {
            this._getAutomaticAggregation(psql, function (err, aggregation) {
                if (err || aggregation === 'none') {
                    this.aggregation = 'day';
                } else {
                    this.aggregation = aggregation;
                }
                override.aggregation = this.aggregation;
                this._buildQuery(psql, override, callback);
            }.bind(this));
            return null;
        }

        const histogramSql = this._buildQueryTpl({
            _override: override,
            _query: this.query,
            _column: this.column,
            _aggregation: this._getAggregation(override),
            _start: this._getBinStart(override),
            _end: this._getBinEnd(override),
            _offset: this._parseOffset(override)
        });

        debug(histogramSql);

        return callback(null, histogramSql);
    }

    _isValidAggregation (override) {
        return DATE_AGGREGATIONS.hasOwnProperty(this._getAggregation(override));
    }

    _getAutomaticAggregation (psql, callback) {
        const dateIntervalQuery = dateIntervalQueryTpl({
            query: this.query,
            column: this.column
        });

        psql.query(dateIntervalQuery, function (err, result) {
            if (err) {
                return callback(err);
            }

            const aggegations = result.rows[0];
            const aggregation = Object.keys(aggegations)
                .map(function (key) {
                    return {
                        name: key,
                        value: aggegations[key]
                    };
                })
                .reduce(function (closer, current) {
                    if (current.value > MAX_INTERVAL_VALUE) {
                        return closer;
                    }

                    const closerDiff = MAX_INTERVAL_VALUE - closer.value;
                    const currentDiff = MAX_INTERVAL_VALUE - current.value;

                    if (Number.isFinite(current.value) && closerDiff > currentDiff) {
                        return current;
                    }

                    return closer;
                }, { name: 'none', value: -1 });

            callback(null, aggregation.name);
        });
    }

    _getSummary (result, override) {
        const firstRow = result.rows[0] || {};

        return {
            aggregation: this._getAggregation(override),
            offset: this._getOffset(override),
            timestamp_start: firstRow.timestamp_start,

            bin_width: firstRow.bin_width,
            bins_count: firstRow.bins_number,
            bins_start: firstRow.timestamp,
            nulls: firstRow.nulls_count,
            infinities: firstRow.infinities_count,
            nans: firstRow.nans_count,
            avg: firstRow.avg_val
        };
    }

    _getBuckets (result) {
        return result.rows.map(row => {
            const { bin, min, max, avg, freq, timestamp } = row;
            return { bin, min, max, avg, freq, timestamp };
        });
    }

    _getAggregation (override = {}) {
        return override.aggregation ? override.aggregation : this.aggregation;
    }

    _getOffset (override = {}) {
        return Number.isFinite(override.offset) ? override.offset : (this.offset || 0);
    }

    _parseOffset (override) {
        if (this._shouldIgnoreOffset(override)) {
            return '0';
        }

        const offsetInHours = Math.ceil(this._getOffset(override) / 3600);

        return '' + offsetInHours;
    }

    _shouldIgnoreOffset (override) {
        return (this._getAggregation(override) === 'hour' || this._getAggregation(override) === 'minute');
    }
};
<|endoftext|>


<commit_before>// Fetches tweets from Twitters streaming service, which uses keyword search.

var Twit = require('twit');
var ContentService = require('../content-service');
var config = require('../../../config/secrets');
var util = require('util');
var _ = require('underscore');

// options.keywords - The keywords to pass to Twitter
var TwitterContentService = function(options) {
  this._twit = new Twit(_.clone(config.get().twitter));
  this._keywords = options.keywords;
  this.fetchType = 'push';
  this._isStreaming = false;
  ContentService.call(this, options);
};

util.inherits(TwitterContentService, ContentService);

// Set/change filter stream
TwitterContentService.prototype.setFilterStream = function(keywords) {
  if (typeof keywords === 'string') {
    this._keywords = keywords;
  }
};

// Start/resume streaming of filtered data
TwitterContentService.prototype.start = function() {
  if (this._isStreaming) return;
  if (this._stream) {
    this._stream.start();
  } else {
    this._streamName = 'statuses/filter';
    this._stream = this._getStream();
    this.addListeners();
  }
  this._isStreaming = true;
};

// Stop the stream
TwitterContentService.prototype.stop = function() {
  if (this._stream)
    this._stream.stop();

  this._isStreaming = false;
};

// Wrapper for stream event listener
TwitterContentService.prototype.addListeners = function() {
  if (this._stream) {
    // Avoid re-adding the same listeners
    var listeners = this._stream.listeners('tweet');
    if (listeners.length !== 0) return;
  } else {
    // Bail if stream has not been started
    return;
  }
  var self = this;
  this._stream.on('tweet', function(tweet) {
    self.emit('report', self._parse(tweet));
  });
  this._stream.on('limit', function(message) {
    self.emit('warning', new Error('Twitter sent rate limitation: ' + JSON.stringify(message.limit)));
  });
  this._stream.on('disconnect', function(message) {
    self.emit('warning', new Error('Twitter sent disconnect: ' + JSON.stringify(message.disconnect)));
  });
  this._stream.on('reconnect', function(request, response, connectInterval) {
    self.emit('warning', new Error('Reconnecting to Twitter in ' + (connectInterval / 1000) + ' seconds'));
  });
  this._stream.on('warning', function(message) {
    self.emit('warning', new Error('Twitter sent warning: ' + JSON.stringify(message.warning)));
  });
};

// Gets a Twit stream object
TwitterContentService.prototype._getStream = function() {
  return this._twit.stream(this._streamName, {track: this._keywords});
};

TwitterContentService.prototype._parse = function(data) {
  var author = data.user ? data.user.screen_name : null;
  return {
    authoredAt: new Date(data.created_at),
    fetchedAt: new Date(),
    content: data.text,
    author: author,
    url: author ? 'https://twitter.com/' + author + '/status/' + data.id_str : null
  };
};

TwitterContentService.prototype.reloadSettings = function() {
  this.stop();
  delete this._stream;
  delete this._twit;
  this._twit = new Twit(_.clone(config.get().twitter));
  this.start();
};

module.exports = TwitterContentService;
<commit_msg>4247: Cleaning and manual testing<commit_after>// Fetches tweets from Twitters streaming service, which uses keyword search.

var Twit = require('twit');
var ContentService = require('../content-service');
var config = require('../../../config/secrets');
var util = require('util');
var _ = require('underscore');

// options.keywords - The keywords to pass to Twitter
var TwitterContentService = function(options) {
  this._twit = new Twit(_.clone(config.get().twitter));
  this._keywords = options.keywords;
  this.fetchType = 'push';
  this._isStreaming = false;
  ContentService.call(this, options);
};

util.inherits(TwitterContentService, ContentService);

// Set/change filter stream
TwitterContentService.prototype.setFilterStream = function(keywords) {
  if (typeof keywords === 'string') {
    this._keywords = keywords;
  }
};

// Start/resume streaming of filtered data
TwitterContentService.prototype.start = function() {
  if (this._isStreaming) return;
  if (this._stream) {
    this._stream.start();
  } else {
    this._streamName = 'statuses/filter';
    this._stream = this._getStream();
    this.addListeners();
  }
  this._isStreaming = true;
};

// Stop the stream
TwitterContentService.prototype.stop = function() {
  if (this._stream)
    this._stream.stop();

  this._isStreaming = false;
};

// Wrapper for stream event listener
TwitterContentService.prototype.addListeners = function() {
  if (this._stream) {
    // Avoid re-adding the same listeners
    var listeners = this._stream.listeners('tweet');
    if (listeners.length !== 0) return;
  } else {
    // Bail if stream has not been started
    return;
  }
  var self = this;
  this._stream.on('tweet', function(tweet) {
    self.emit('report', self._parse(tweet));
  });
  this._stream.on('limit', function(message) {
    self.emit('warning', new Error('Twitter sent rate limitation: ' + JSON.stringify(message.limit)));
  });
  this._stream.on('disconnect', function(message) {
    self.emit('warning', new Error('Twitter sent disconnect: ' + JSON.stringify(message.disconnect)));
  });
  this._stream.on('reconnect', function(request, response, connectInterval) {
    self.emit('warning', new Error('Reconnecting to Twitter in ' + (connectInterval / 1000) + ' seconds'));
  });
  this._stream.on('warning', function(message) {
    self.emit('warning', new Error('Twitter sent warning: ' + JSON.stringify(message.warning)));
  });
};

// Gets a Twit stream object
TwitterContentService.prototype._getStream = function() {
  return this._twit.stream(this._streamName, {track: this._keywords});
};

TwitterContentService.prototype._parse = function(data) {
  var author = data.user ? data.user.screen_name : null;
  return {
    authoredAt: new Date(data.created_at),
    fetchedAt: new Date(),
    content: data.text,
    author: author,
    url: author ? 'https://twitter.com/' + author + '/status/' + data.id_str : null
  };
};

TwitterContentService.prototype.reloadSettings = function() {
  this.stop();
  delete this._stream;
  delete this._twit;

  this._twit = new Twit(_.clone(config.get().twitter));
  this.start();
};

module.exports = TwitterContentService;
<|endoftext|>


<commit_before><commit_msg>lint/add logic<commit_after><|endoftext|>