<commit_before>//http://ejohn.org/blog/fireunit/
function test_add_your_own_tag() {
    $('#typed_tag').val("wxy");
    //$('#typed_tag').trigger('keyup');
    setTimeout(function() {
        var own_tag = $("#suggested_tags_widget .tag:contains('wxy')");
        fireunit.ok(own_tag.length == 1, "Testing if tag has been added");
        var isSelected = own_tag.attr("class").search("ui-state-active")
        fireunit.ok(isSelected > -1, "Testing if tag is selected");
    }, 7000);
}
function test_tag_click() {
    var suggested_tags_selector = $('#suggested_tags_widget .tag:first');
    suggested_tags_selector.trigger('click');
    var suggested_tags_val = suggested_tags_selector.text();
    //this is assuming that we call the replace method in our code
    suggested_tags_val = suggested_tags_val.replace("\n", "").trim();
    setTimeout(function() {
     var isSelected = $("#suggested_tags_widget .tag:contains('"+suggested_tags_val+"')").attr("class").search("ui-state-active");
        fireunit.ok(isSelected > -1, "Testing if tag is selected");
        var details = $('#offer').val();
        //look for the tag in details
        var details_result = details.search("#" + suggested_tags_val);
        fireunit.ok(details_result > -1, "Testing if tag click adds item to details");
        var currency_tags_selector = $('#post_your_own_currency_tags .tag:first');
        currency_tags_val = currency_tags_selector.text();
        currency_tags_val = currency_tags_val.replace("\n", "").trim();
        currency_tags_selector.trigger('click');
        //look for both the currency tag in details now this assumes that we do not immediately update 'message_to_send' after
        //a suggested tag is clicked
        var message_to_send = $("#message_to_send").val();
        var message_result = message_to_send.search("#" + currency_tags_val);
        fireunit.ok(message_result > -1, "Testing if currency tag click adds to message_to_send");
        message_result = message_to_send.search("#" + suggested_tags_val);
        fireunit.ok(message_result > -1, "Testing if suggested tag is added to message_to_send");
    }, 4000);

};
/*
function test_tag_click() {
    var suggested_tags_selector = $('#suggested_tags_widget .tag:first');
    var suggested_tags_val = suggested_tags_selector.text();
    //this is assuming that we call the replace method in our code
    suggested_tags_val = suggested_tags_val.replace("\n", "").trim();
    suggested_tags_selector.trigger('click');
    var isSelected = $(suggested_tags_selector).attr("class").search("ui-state-active");
    fireunit.ok(isSelected > -1, "Testing if tag is selected");
    var details = $('#offer').val();
    //look for the tag in details
    var details_result = details.search("#" + suggested_tags_val);
    fireunit.ok(details_result > -1, "Testing if tag click adds item to details");
    var currency_tags_selector = $('#post_your_own_currency_tags .tag:first');
    currency_tags_val = currency_tags_selector.text();
    currency_tags_val = currency_tags_val.replace("\n", "").trim();
    currency_tags_selector.trigger('click');
    //look for both the currency tag in details now this assumes that we do not immediately update 'message_to_send' after
    //a suggested tag is clicked
    var message_to_send=$("#message_to_send").val();
    var message_result = message_to_send.search("#" + currency_tags_val);
    fireunit.ok(message_result > -1, "Testing if currency tag click adds to message_to_send");
    message_result = message_to_send.search("#" + suggested_tags_val);
    fireunit.ok(message_result > -1, "Testing if suggested tag is added to message_to_send");

};
*/
setTimeout(test_add_your_own_tag, 10000);
setTimeout(test_tag_click, 18000);
<commit_msg>added remove test<commit_after>//http://ejohn.org/blog/fireunit/
function test_add_your_own_tag() {
    $('#typed_tag').val("wxy");
    //$('#typed_tag').trigger('keyup');
    setTimeout(function() {
        var own_tag = $("#suggested_tags_widget .tag:contains('wxy')");
        fireunit.ok(own_tag.length == 1, "Testing if tag has been added");
        var isSelected = own_tag.attr("class").search("ui-state-active")
        fireunit.ok(isSelected > -1, "Testing if tag is selected");
    }, 7000);
}
function test_tag_click() {
    var suggested_tags_selector = $('#suggested_tags_widget .tag:first');
    suggested_tags_selector.trigger('click');
    var suggested_tags_val = suggested_tags_selector.text();
    //this is assuming that we call the replace method in our code
    suggested_tags_val = suggested_tags_val.replace("\n", "").trim();
    setTimeout(function() {
     var isSelected = $("#suggested_tags_widget .tag:contains('"+suggested_tags_val+"')").attr("class").search("ui-state-active");
        fireunit.ok(isSelected > -1, "Testing if tag is selected");
        var details = $('#offer').val();
        //look for the tag in details
        var details_result = details.search("#" + suggested_tags_val);
        fireunit.ok(details_result > -1, "Testing if tag click adds item to details");
        var currency_tags_selector = $('#post_your_own_currency_tags .tag:first');
        currency_tags_val = currency_tags_selector.text();
        currency_tags_val = currency_tags_val.replace("\n", "").trim();
        currency_tags_selector.trigger('click');
        //look for both the currency tag in details now this assumes that we do not immediately update 'message_to_send' after
        //a suggested tag is clicked
        var message_to_send = $("#message_to_send").val();
        var message_result = message_to_send.search("#" + currency_tags_val);
        fireunit.ok(message_result > -1, "Testing if currency tag click adds to message_to_send");
        message_result = message_to_send.search("#" + suggested_tags_val);
        fireunit.ok(message_result > -1, "Testing if suggested tag is added to message_to_send");
    }, 4000);

};

function deselect_removes_tags() {
    var suggested_tags_selector = $('#suggested_tags_widget .tag[class~=ui-state-active]:first');
    suggested_tags_selector.trigger('click');
    var suggested_tags_val = suggested_tags_selector.text();
    suggested_tags_val = suggested_tags_val.replace("\n", "").trim();
    setTimeout(function() {
     var isSelected = $("#suggested_tags_widget .tag:contains('"+suggested_tags_val+"')").attr("class").search("ui-state-active");
        fireunit.ok(isSelected == -1, "Testing if tag is deselected");
        var details = $('#offer').val();
        //look for the tag in details
        var details_result = details.search("#" + suggested_tags_val);
        fireunit.ok(details_result == -1, "Testing if tag click removes item from details");
        var message_to_send = $("#message_to_send").val();
        var message_result = message_to_send.search("#" + suggested_tags_val);
        fireunit.ok(message_result == -1, "Testing if suggested tag is removeed from to message_to_send");
    }, 4000);
} /*
function test_tag_click() {
    var suggested_tags_selector = $('#suggested_tags_widget .tag:first');
    var suggested_tags_val = suggested_tags_selector.text();
    //this is assuming that we call the replace method in our code
    suggested_tags_val = suggested_tags_val.replace("\n", "").trim();
    suggested_tags_selector.trigger('click');
    var isSelected = $(suggested_tags_selector).attr("class").search("ui-state-active");
    fireunit.ok(isSelected > -1, "Testing if tag is selected");
    var details = $('#offer').val();
    //look for the tag in details
    var details_result = details.search("#" + suggested_tags_val);
    fireunit.ok(details_result > -1, "Testing if tag click adds item to details");
    var currency_tags_selector = $('#post_your_own_currency_tags .tag:first');
    currency_tags_val = currency_tags_selector.text();
    currency_tags_val = currency_tags_val.replace("\n", "").trim();
    currency_tags_selector.trigger('click');
    //look for both the currency tag in details now this assumes that we do not immediately update 'message_to_send' after
    //a suggested tag is clicked
    var message_to_send=$("#message_to_send").val();
    var message_result = message_to_send.search("#" + currency_tags_val);
    fireunit.ok(message_result > -1, "Testing if currency tag click adds to message_to_send");
    message_result = message_to_send.search("#" + suggested_tags_val);
    fireunit.ok(message_result > -1, "Testing if suggested tag is added to message_to_send");

};
*/
setTimeout(test_add_your_own_tag, 10000);
setTimeout(test_tag_click, 18000);
setTimeout(deselect_removes_tags, 25000);<|endoftext|>


<commit_before>/**
 * DashboardAllTrafficWidget component
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { _x } from '@wordpress/i18n';
import { useState, useEffect } from '@wordpress/element';
import { isURL } from '@wordpress/url';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import {
	UI_DIMENSION_NAME,
	UI_DIMENSION_VALUE,
	DATE_RANGE_OFFSET,
	MODULES_ANALYTICS,
	UI_ALL_TRAFFIC_LOADED,
} from '../../../datastore/constants';
import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants';
import { CORE_USER } from '../../../../../googlesitekit/datastore/user/constants';
import { CORE_UI } from '../../../../../googlesitekit/datastore/ui/constants';
import { Grid, Row, Cell } from '../../../../../material-components/layout';
import { isZeroReport } from '../../../util';
import { generateDateRangeArgs } from '../../../util/report-date-range-args';
import { getURLPath } from '../../../../../util';
import whenActive from '../../../../../util/when-active';
import SourceLink from '../../../../../components/SourceLink';
import TotalUserCount from './TotalUserCount';
import UserCountGraph from './UserCountGraph';
import DimensionTabs from './DimensionTabs';
import UserDimensionsPieChart from './UserDimensionsPieChart';
import EmptyPieChart from './EmptyPieChart';
const { useSelect, useInViewSelect, useDispatch } = Data;

function DashboardAllTrafficWidget( {
	Widget,
	WidgetReportZero,
	WidgetReportError,
} ) {
	const isGatheringData = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).isGatheringData()
	);

	const [ firstLoad, setFirstLoad ] = useState( true );
	const [ currentRange, setCurrentRange ] = useState( '' );

	const dateRange = useSelect( ( select ) =>
		select( CORE_USER ).getDateRange()
	);
	const dimensionName = useSelect(
		( select ) =>
			select( CORE_UI ).getValue( UI_DIMENSION_NAME ) ||
			'ga:channelGrouping'
	);
	const dimensionValue = useSelect( ( select ) =>
		select( CORE_UI ).getValue( UI_DIMENSION_VALUE )
	);
	const entityURL = useSelect( ( select ) =>
		select( CORE_SITE ).getCurrentEntityURL()
	);

	const { startDate, endDate, compareStartDate, compareEndDate } = useSelect(
		( select ) =>
			select( CORE_USER ).getDateRangeDates( {
				compare: true,
				offsetDays: DATE_RANGE_OFFSET,
			} )
	);

	const baseArgs = {
		startDate,
		endDate,
		metrics: [ { expression: 'ga:users' } ],
	};

	const pieArgs = {
		...baseArgs,
		compareStartDate,
		compareEndDate,
		dimensions: [ dimensionName ],
		orderby: {
			fieldName: 'ga:users',
			sortOrder: 'DESCENDING',
		},
		limit: 6,
	};

	const graphArgs = {
		...baseArgs,
		dimensions: [ 'ga:date' ],
	};

	const totalsArgs = {
		...baseArgs,
		compareStartDate,
		compareEndDate,
	};

	if ( entityURL ) {
		pieArgs.url = entityURL;
		graphArgs.url = entityURL;
		totalsArgs.url = entityURL;
	}

	if ( dimensionName && dimensionValue ) {
		graphArgs.dimensionFilters = { [ dimensionName ]: dimensionValue };
		totalsArgs.dimensionFilters = { [ dimensionName ]: dimensionValue };
	}

	const pieChartLoaded = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).hasFinishedResolution( 'getReport', [
			pieArgs,
		] )
	);
	const pieChartError = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getErrorForSelector( 'getReport', [
			pieArgs,
		] )
	);
	const pieChartReport = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getReport( pieArgs )
	);

	const userCountGraphLoaded = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).hasFinishedResolution( 'getReport', [
			graphArgs,
		] )
	);
	const userCountGraphError = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getErrorForSelector( 'getReport', [
			graphArgs,
		] )
	);
	const userCountGraphReport = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getReport( graphArgs )
	);

	const totalUsersLoaded = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).hasFinishedResolution( 'getReport', [
			totalsArgs,
		] )
	);
	const totalUsersError = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getErrorForSelector( 'getReport', [
			totalsArgs,
		] )
	);
	const totalUsersReport = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getReport( totalsArgs )
	);

	let reportType;
	switch ( dimensionName ) {
		case 'ga:country':
			reportType = 'visitors-geo';
			break;
		case 'ga:deviceCategory':
			reportType = 'visitors-mobile-overview';
			break;
		case 'ga:channelGrouping':
		default:
			reportType = 'trafficsources-overview';
			break;
	}

	const reportArgs = generateDateRangeArgs( {
		startDate,
		endDate,
		compareStartDate,
		compareEndDate,
	} );

	if ( isURL( entityURL ) ) {
		reportArgs[ 'explorer-table.plotKeys' ] = '[]';
		reportArgs[ '_r.drilldown' ] = `analytics.pagePath:${ getURLPath(
			entityURL
		) }`;
	}

	const serviceReportURL = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getServiceReportURL(
			reportType,
			reportArgs
		)
	);

	useEffect( () => {
		if ( dateRange !== currentRange ) {
			setFirstLoad( true );
			setCurrentRange( dateRange );
		} else if (
			pieChartLoaded &&
			totalUsersLoaded &&
			userCountGraphLoaded
		) {
			setFirstLoad( false );
		}
	}, [
		pieChartLoaded,
		totalUsersLoaded,
		userCountGraphLoaded,
		dateRange,
		currentRange,
	] );

	// Set a flag in the core/ui store when all data is loaded.
	// Currently only used by the feature tour to delay showing
	// while the widget is in a loading state.
	const { setValue } = useDispatch( CORE_UI );
	useEffect( () => {
		if (
			firstLoad &&
			pieChartLoaded &&
			totalUsersLoaded &&
			userCountGraphLoaded
		) {
			setValue( UI_ALL_TRAFFIC_LOADED, true );
		}
	}, [
		firstLoad,
		pieChartLoaded,
		totalUsersLoaded,
		userCountGraphLoaded,
		setValue,
	] );

	if ( pieChartError ) {
		return (
			<Widget>
				<WidgetReportError
					moduleSlug="analytics"
					error={ pieChartError }
				/>
			</Widget>
		);
	}

	const pieChartReportIsZero = isZeroReport( pieChartReport );
	if ( isGatheringData && pieChartReportIsZero ) {
		return (
			<Widget>
				<WidgetReportZero moduleSlug="analytics" />
			</Widget>
		);
	}

	return (
		<Widget
			className="googlesitekit-widget--footer-v2 googlesitekit-widget__analytics--all-traffic"
			Footer={ () => (
				<SourceLink
					className="googlesitekit-data-block__source"
					name={ _x(
						'Analytics',
						'Service name',
						'google-site-kit'
					) }
					href={ serviceReportURL }
					external
				/>
			) }
			noPadding
		>
			<Grid>
				<Row>
					<Cell
						className="googlesitekit-widget--analyticsAllTraffic__totals"
						lgSize={ 7 }
						mdSize={ 8 }
					>
						<TotalUserCount
							loaded={ totalUsersLoaded && ! firstLoad }
							report={ totalUsersReport }
							error={ totalUsersError }
							dimensionValue={ dimensionValue }
						/>

						<UserCountGraph
							loaded={ userCountGraphLoaded && ! firstLoad }
							error={ userCountGraphError }
							report={ userCountGraphReport }
						/>
					</Cell>

					<Cell
						className="googlesitekit-widget--analyticsAllTraffic__dimensions"
						lgSize={ 5 }
						mdSize={ 8 }
					>
						<DimensionTabs
							loaded={ ! firstLoad }
							dimensionName={ dimensionName }
							gatheringData={ isGatheringData }
						/>

						{ ! pieChartReportIsZero && (
							<UserDimensionsPieChart
								dimensionName={ dimensionName }
								dimensionValue={ dimensionValue }
								gatheringData={ isGatheringData }
								loaded={ pieChartLoaded && ! firstLoad }
								report={ pieChartReport }
							/>
						) }

						{ pieChartReportIsZero && <EmptyPieChart /> }
					</Cell>
				</Row>
			</Grid>
		</Widget>
	);
}

export default whenActive( { moduleName: 'analytics' } )(
	DashboardAllTrafficWidget
);
<commit_msg>Only render EmptyPieChart when zeroDataStates feature not enabled.<commit_after>/**
 * DashboardAllTrafficWidget component
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { _x } from '@wordpress/i18n';
import { useState, useEffect } from '@wordpress/element';
import { isURL } from '@wordpress/url';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import {
	UI_DIMENSION_NAME,
	UI_DIMENSION_VALUE,
	DATE_RANGE_OFFSET,
	MODULES_ANALYTICS,
	UI_ALL_TRAFFIC_LOADED,
} from '../../../datastore/constants';
import { CORE_SITE } from '../../../../../googlesitekit/datastore/site/constants';
import { CORE_USER } from '../../../../../googlesitekit/datastore/user/constants';
import { CORE_UI } from '../../../../../googlesitekit/datastore/ui/constants';
import { Grid, Row, Cell } from '../../../../../material-components/layout';
import { isZeroReport } from '../../../util';
import { generateDateRangeArgs } from '../../../util/report-date-range-args';
import { getURLPath } from '../../../../../util';
import whenActive from '../../../../../util/when-active';
import SourceLink from '../../../../../components/SourceLink';
import TotalUserCount from './TotalUserCount';
import UserCountGraph from './UserCountGraph';
import DimensionTabs from './DimensionTabs';
import UserDimensionsPieChart from './UserDimensionsPieChart';
import EmptyPieChart from './EmptyPieChart';
import { useFeature } from '../../../../../hooks/useFeature';
const { useSelect, useInViewSelect, useDispatch } = Data;

function DashboardAllTrafficWidget( {
	Widget,
	WidgetReportZero,
	WidgetReportError,
} ) {
	const zeroDataStatesEnabled = useFeature( 'zeroDataStates' );

	const isGatheringData = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).isGatheringData()
	);

	const [ firstLoad, setFirstLoad ] = useState( true );
	const [ currentRange, setCurrentRange ] = useState( '' );

	const dateRange = useSelect( ( select ) =>
		select( CORE_USER ).getDateRange()
	);
	const dimensionName = useSelect(
		( select ) =>
			select( CORE_UI ).getValue( UI_DIMENSION_NAME ) ||
			'ga:channelGrouping'
	);
	const dimensionValue = useSelect( ( select ) =>
		select( CORE_UI ).getValue( UI_DIMENSION_VALUE )
	);
	const entityURL = useSelect( ( select ) =>
		select( CORE_SITE ).getCurrentEntityURL()
	);

	const { startDate, endDate, compareStartDate, compareEndDate } = useSelect(
		( select ) =>
			select( CORE_USER ).getDateRangeDates( {
				compare: true,
				offsetDays: DATE_RANGE_OFFSET,
			} )
	);

	const baseArgs = {
		startDate,
		endDate,
		metrics: [ { expression: 'ga:users' } ],
	};

	const pieArgs = {
		...baseArgs,
		compareStartDate,
		compareEndDate,
		dimensions: [ dimensionName ],
		orderby: {
			fieldName: 'ga:users',
			sortOrder: 'DESCENDING',
		},
		limit: 6,
	};

	const graphArgs = {
		...baseArgs,
		dimensions: [ 'ga:date' ],
	};

	const totalsArgs = {
		...baseArgs,
		compareStartDate,
		compareEndDate,
	};

	if ( entityURL ) {
		pieArgs.url = entityURL;
		graphArgs.url = entityURL;
		totalsArgs.url = entityURL;
	}

	if ( dimensionName && dimensionValue ) {
		graphArgs.dimensionFilters = { [ dimensionName ]: dimensionValue };
		totalsArgs.dimensionFilters = { [ dimensionName ]: dimensionValue };
	}

	const pieChartLoaded = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).hasFinishedResolution( 'getReport', [
			pieArgs,
		] )
	);
	const pieChartError = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getErrorForSelector( 'getReport', [
			pieArgs,
		] )
	);
	const pieChartReport = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getReport( pieArgs )
	);

	const userCountGraphLoaded = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).hasFinishedResolution( 'getReport', [
			graphArgs,
		] )
	);
	const userCountGraphError = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getErrorForSelector( 'getReport', [
			graphArgs,
		] )
	);
	const userCountGraphReport = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getReport( graphArgs )
	);

	const totalUsersLoaded = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).hasFinishedResolution( 'getReport', [
			totalsArgs,
		] )
	);
	const totalUsersError = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getErrorForSelector( 'getReport', [
			totalsArgs,
		] )
	);
	const totalUsersReport = useInViewSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getReport( totalsArgs )
	);

	let reportType;
	switch ( dimensionName ) {
		case 'ga:country':
			reportType = 'visitors-geo';
			break;
		case 'ga:deviceCategory':
			reportType = 'visitors-mobile-overview';
			break;
		case 'ga:channelGrouping':
		default:
			reportType = 'trafficsources-overview';
			break;
	}

	const reportArgs = generateDateRangeArgs( {
		startDate,
		endDate,
		compareStartDate,
		compareEndDate,
	} );

	if ( isURL( entityURL ) ) {
		reportArgs[ 'explorer-table.plotKeys' ] = '[]';
		reportArgs[ '_r.drilldown' ] = `analytics.pagePath:${ getURLPath(
			entityURL
		) }`;
	}

	const serviceReportURL = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getServiceReportURL(
			reportType,
			reportArgs
		)
	);

	useEffect( () => {
		if ( dateRange !== currentRange ) {
			setFirstLoad( true );
			setCurrentRange( dateRange );
		} else if (
			pieChartLoaded &&
			totalUsersLoaded &&
			userCountGraphLoaded
		) {
			setFirstLoad( false );
		}
	}, [
		pieChartLoaded,
		totalUsersLoaded,
		userCountGraphLoaded,
		dateRange,
		currentRange,
	] );

	// Set a flag in the core/ui store when all data is loaded.
	// Currently only used by the feature tour to delay showing
	// while the widget is in a loading state.
	const { setValue } = useDispatch( CORE_UI );
	useEffect( () => {
		if (
			firstLoad &&
			pieChartLoaded &&
			totalUsersLoaded &&
			userCountGraphLoaded
		) {
			setValue( UI_ALL_TRAFFIC_LOADED, true );
		}
	}, [
		firstLoad,
		pieChartLoaded,
		totalUsersLoaded,
		userCountGraphLoaded,
		setValue,
	] );

	if ( pieChartError ) {
		return (
			<Widget>
				<WidgetReportError
					moduleSlug="analytics"
					error={ pieChartError }
				/>
			</Widget>
		);
	}

	const pieChartReportIsZero = isZeroReport( pieChartReport );
	if ( isGatheringData && pieChartReportIsZero ) {
		return (
			<Widget>
				<WidgetReportZero moduleSlug="analytics" />
			</Widget>
		);
	}

	const showEmptyPieChart = ! zeroDataStatesEnabled && pieChartReportIsZero;

	return (
		<Widget
			className="googlesitekit-widget--footer-v2 googlesitekit-widget__analytics--all-traffic"
			Footer={ () => (
				<SourceLink
					className="googlesitekit-data-block__source"
					name={ _x(
						'Analytics',
						'Service name',
						'google-site-kit'
					) }
					href={ serviceReportURL }
					external
				/>
			) }
			noPadding
		>
			<Grid>
				<Row>
					<Cell
						className="googlesitekit-widget--analyticsAllTraffic__totals"
						lgSize={ 7 }
						mdSize={ 8 }
					>
						<TotalUserCount
							loaded={ totalUsersLoaded && ! firstLoad }
							report={ totalUsersReport }
							error={ totalUsersError }
							dimensionValue={ dimensionValue }
						/>

						<UserCountGraph
							loaded={ userCountGraphLoaded && ! firstLoad }
							error={ userCountGraphError }
							report={ userCountGraphReport }
						/>
					</Cell>

					<Cell
						className="googlesitekit-widget--analyticsAllTraffic__dimensions"
						lgSize={ 5 }
						mdSize={ 8 }
					>
						<DimensionTabs
							loaded={ ! firstLoad }
							dimensionName={ dimensionName }
							gatheringData={ isGatheringData }
						/>

						{ ! showEmptyPieChart && (
							<UserDimensionsPieChart
								dimensionName={ dimensionName }
								dimensionValue={ dimensionValue }
								gatheringData={ isGatheringData }
								loaded={ pieChartLoaded && ! firstLoad }
								report={ pieChartReport }
							/>
						) }

						{ showEmptyPieChart && <EmptyPieChart /> }
					</Cell>
				</Row>
			</Grid>
		</Widget>
	);
}

export default whenActive( { moduleName: 'analytics' } )(
	DashboardAllTrafficWidget
);
<|endoftext|>


<commit_before>import 'lb-bootstrap.scss';

import 'jquery-ui/jquery-ui';
import 'jquery-jcrop';
import 'jquery-gridster';
import 'moment-timezone';
import 'bootstrap';
import 'angular';
import 'angular-moment';
import 'angular-bootstrap-npm';
import 'angular-resource';
import 'angular-route';
import 'angular-gettext';
import 'angular-mocks';
import 'angular-animate';
import 'angular-embedly';
import 'angular-contenteditable';
import 'angular-vs-repeat';
import 'ng-file-upload';
import 'raven-js';
import 'rangy';
import 'rangy-saverestore';
import 'ment.io';
import 'angular-embed/dist/angular-embed';
import 'angular-contenteditable';
import 'angular-messages';
import 'lr-infinite-scroll';

import _ from 'lodash';
import moment from 'moment';

_.sortByOrder = require('lodash.sortbyorder');

// This is an ugly little hack required by the venerable superdesk.editor to work
import MediumEditor from 'medium-editor';
window.MediumEditor = MediumEditor;

import 'superdesk-core/scripts/core/activity';
import 'superdesk-core/scripts/core/analytics';
import 'superdesk-core/scripts/core/api';
import 'superdesk-core/scripts/core/auth';
import 'superdesk-core/scripts/core/beta';
import 'superdesk-core/scripts/core/datetime';
import 'superdesk-core/scripts/core/error';
import 'superdesk-core/scripts/core/elastic';
import 'superdesk-core/scripts/core/filters';
import 'superdesk-core/scripts/core/services';
import 'superdesk-core/scripts/core/directives';
import 'superdesk-core/scripts/core/editor2';
import 'superdesk-core/scripts/core/spellcheck';
import 'superdesk-core/scripts/core/editor3';
import 'superdesk-core/scripts/core/features';
import 'superdesk-core/scripts/core/list';
import 'superdesk-core/scripts/core/keyboard';
import 'superdesk-core/scripts/core/privileges';
import 'superdesk-core/scripts/core/notification';
import 'superdesk-core/scripts/core/itemList';
import 'superdesk-core/scripts/core/menu';
import 'superdesk-core/scripts/core/notify';
import 'superdesk-core/scripts/core/ui';
import 'superdesk-core/scripts/core/upload';
import 'superdesk-core/scripts/core/lang';
import 'superdesk-core/scripts/core/config';
import 'superdesk-core/scripts/core/loading';

import 'superdesk-core/scripts/apps/workspace';
import 'superdesk-core/scripts/apps/dashboard';
import 'superdesk-core/scripts/apps/users';
//import 'superdesk-core/scripts/apps/groups';
//import 'superdesk-core/scripts/apps/products';
import 'superdesk-core/scripts/apps/publish';
import 'superdesk-core/scripts/apps/templates';
import 'superdesk-core/scripts/apps/profiling';
import 'superdesk-core/scripts/apps/desks';
import 'superdesk-core/scripts/apps/authoring';
import 'superdesk-core/scripts/apps/search';
import 'superdesk-core/scripts/apps/legal-archive';
//import 'superdesk-core/scripts/apps/stream';
import 'superdesk-core/scripts/apps/packaging';
import 'superdesk-core/scripts/apps/highlights';
import 'superdesk-core/scripts/apps/translations';
import 'superdesk-core/scripts/apps/content-filters';
import 'superdesk-core/scripts/apps/dictionaries';
import 'superdesk-core/scripts/apps/vocabularies';
import 'superdesk-core/scripts/apps/archive';
import 'superdesk-core/scripts/apps/monitoring';
import 'superdesk-core/scripts/apps/settings';
import 'superdesk-core/scripts/apps/ingest';
//import 'superdesk-core/scripts/apps/search-providers';

import 'liveblog-bloglist';
import 'liveblog-edit';
import 'liveblog-themes';
import 'liveblog-settings';
import 'liveblog-syndication';

import 'liveblog-security.service';

import config from './../../config';

let sdCore = angular.module('superdesk.core', [
    'ngRoute',
    'ngResource',
    'ngFileUpload',

    'ui.bootstrap',

    'superdesk.core.activity',
    'superdesk.core.analytics',
    'superdesk.core.api',
    'superdesk.core.auth',
    'superdesk.core.datetime',
    'superdesk.core.elastic',
    'superdesk.core.error',
    'superdesk.core.notify',
    'superdesk.core.ui',
    'superdesk.core.upload',
    'superdesk.core.menu',
    'superdesk.core.filters',
    'superdesk.core.preferences',
    'superdesk.core.translate',
    'superdesk.core.workflow',
    'superdesk.core.loading',
    'superdesk.core.editor3',

    // services/
    'superdesk.core.services.beta',
    'superdesk.core.services.data',
    'superdesk.core.services.modal',
    'superdesk.core.services.dragdrop',
    'superdesk.core.services.server',
    'superdesk.core.services.entity',
    'superdesk.core.services.permissions',
    'superdesk.core.services.storage',
    'superdesk.core.services.pageTitle',

    'superdesk.core.directives'
]);

angular.module('superdesk.apps', [
    'superdesk.apps.settings',
    'superdesk.apps.dashboard',
    'superdesk.apps.users',
    'superdesk.apps.users.import',
    'superdesk.apps.users.profile',
    'superdesk.apps.users.activity',
    'superdesk.apps.archive',
    'superdesk.apps.ingest',
    'superdesk.apps.desks',
    //'superdesk.apps.groups',
    //'superdesk.apps.products',
    'superdesk.apps.authoring',
    'superdesk.apps.packaging',
    'superdesk.apps.spellcheck',
    'superdesk.apps.notification',
    'superdesk.apps.highlights',
    'superdesk.apps.translations',
    'superdesk.apps.content_filters', // Can't remove
    'superdesk.apps.dictionaries',
    'superdesk.apps.vocabularies',
    //'superdesk.apps.searchProviders',
    //'superdesk.apps.stream',
    'superdesk.apps.publish', // Can't remove
    'superdesk.apps.templates',
    'superdesk.apps.monitoring',
    'superdesk.apps.profiling'
]);

angular.module('superdesk.config').constant('config', config);

let liveblogModules = [
    'liveblog.bloglist',
    'liveblog.edit',
    'liveblog.posts',
    'liveblog.blog',
    'liveblog.themes',
    'ngMessages'
];

if (config.syndication)
    liveblogModules.push('liveblog.syndication');

let liveblog = angular.module('liveblog', liveblogModules);

sdCore.constant('lodash', _);

liveblog.constant('config', config);
liveblog.constant('lodash', _);
liveblog.constant('moment', moment);

liveblog.config(['$routeProvider', '$locationProvider', ($routeProvider, $locationProvider) => {
    $locationProvider.hashPrefix('');
    $routeProvider.when('/', {redirectTo: '/liveblog'});
}]);

liveblog.run(['$rootScope', '$timeout', 'notify', 'gettext', 'session',
    function($rootScope, $timeout, notify, gettext, session) {
        var alertTimeout;
        $rootScope.$on('disconnected', function(event) {
            $timeout.cancel(alertTimeout);
            if (session && session.sessionId) {
                alertTimeout = $timeout(function() {
                    notify.pop();
                    notify.error(gettext('Disconnected from Notification Server, attempting to reconnect ...'), 20000);
                }, 100);
            }
        });
        $rootScope.$on('connected', function(event) {
            //only show the 'connected' message if there was a disconnect event
            if (alertTimeout) {
                $timeout.cancel(alertTimeout);
                alertTimeout = $timeout(function() {
                    notify.pop();
                    notify.success(gettext('Connected to Notification Server!'));
                }, 100);
            }
        });
    }]);

let body = angular.element('body');

body.ready(() => {
    /**
     * @ngdoc module
     * @name superdesk-client
     * @packageName superdesk-client
     * @description The root superdesk module.
     */
    angular.bootstrap(body, [
        'superdesk.config',
        'superdesk.core',
        'superdesk.apps',
        'liveblog'
    ], {strictDi: true});

    window.superdeskIsReady = true;
});
<commit_msg>Added a comment to comment the comment<commit_after>import 'lb-bootstrap.scss';

import 'jquery-ui/jquery-ui';
import 'jquery-jcrop';
import 'jquery-gridster';
import 'moment-timezone';
import 'bootstrap';
import 'angular';
import 'angular-moment';
import 'angular-bootstrap-npm';
import 'angular-resource';
import 'angular-route';
import 'angular-gettext';
import 'angular-mocks';
import 'angular-animate';
import 'angular-embedly';
import 'angular-contenteditable';
import 'angular-vs-repeat';
import 'ng-file-upload';
import 'raven-js';
import 'rangy';
import 'rangy-saverestore';
import 'ment.io';
import 'angular-embed/dist/angular-embed';
import 'angular-contenteditable';
import 'angular-messages';
import 'lr-infinite-scroll';

import _ from 'lodash';
import moment from 'moment';

_.sortByOrder = require('lodash.sortbyorder');

// This is an ugly little hack required by the venerable superdesk.editor to work
import MediumEditor from 'medium-editor';
window.MediumEditor = MediumEditor;

import 'superdesk-core/scripts/core/activity';
import 'superdesk-core/scripts/core/analytics';
import 'superdesk-core/scripts/core/api';
import 'superdesk-core/scripts/core/auth';
import 'superdesk-core/scripts/core/beta';
import 'superdesk-core/scripts/core/datetime';
import 'superdesk-core/scripts/core/error';
import 'superdesk-core/scripts/core/elastic';
import 'superdesk-core/scripts/core/filters';
import 'superdesk-core/scripts/core/services';
import 'superdesk-core/scripts/core/directives';
import 'superdesk-core/scripts/core/editor2';
import 'superdesk-core/scripts/core/spellcheck';
import 'superdesk-core/scripts/core/editor3';
import 'superdesk-core/scripts/core/features';
import 'superdesk-core/scripts/core/list';
import 'superdesk-core/scripts/core/keyboard';
import 'superdesk-core/scripts/core/privileges';
import 'superdesk-core/scripts/core/notification';
import 'superdesk-core/scripts/core/itemList';
import 'superdesk-core/scripts/core/menu';
import 'superdesk-core/scripts/core/notify';
import 'superdesk-core/scripts/core/ui';
import 'superdesk-core/scripts/core/upload';
import 'superdesk-core/scripts/core/lang';
import 'superdesk-core/scripts/core/config';
import 'superdesk-core/scripts/core/loading';

import 'superdesk-core/scripts/apps/workspace';
import 'superdesk-core/scripts/apps/dashboard';
import 'superdesk-core/scripts/apps/users';
//import 'superdesk-core/scripts/apps/groups';
//import 'superdesk-core/scripts/apps/products';
import 'superdesk-core/scripts/apps/publish';
import 'superdesk-core/scripts/apps/templates';
import 'superdesk-core/scripts/apps/profiling';
import 'superdesk-core/scripts/apps/desks';
import 'superdesk-core/scripts/apps/authoring';
import 'superdesk-core/scripts/apps/search';
import 'superdesk-core/scripts/apps/legal-archive';
//import 'superdesk-core/scripts/apps/stream';
import 'superdesk-core/scripts/apps/packaging';
import 'superdesk-core/scripts/apps/highlights';
import 'superdesk-core/scripts/apps/translations';
import 'superdesk-core/scripts/apps/content-filters';
import 'superdesk-core/scripts/apps/dictionaries';
import 'superdesk-core/scripts/apps/vocabularies';
import 'superdesk-core/scripts/apps/archive';
import 'superdesk-core/scripts/apps/monitoring';
import 'superdesk-core/scripts/apps/settings';
import 'superdesk-core/scripts/apps/ingest';
//import 'superdesk-core/scripts/apps/search-providers';

import 'liveblog-bloglist';
import 'liveblog-edit';
import 'liveblog-themes';
import 'liveblog-settings';
import 'liveblog-syndication';

import 'liveblog-security.service';

import config from './../../config';

// Commented angular modules are not required to run liveblog
// But they are shown here to give a perspective of
// what is required to run liveblog
let sdCore = angular.module('superdesk.core', [
    'ngRoute',
    'ngResource',
    'ngFileUpload',

    'ui.bootstrap',

    'superdesk.core.activity',
    'superdesk.core.analytics',
    'superdesk.core.api',
    'superdesk.core.auth',
    'superdesk.core.datetime',
    'superdesk.core.elastic',
    'superdesk.core.error',
    'superdesk.core.notify',
    'superdesk.core.ui',
    'superdesk.core.upload',
    'superdesk.core.menu',
    'superdesk.core.filters',
    'superdesk.core.preferences',
    'superdesk.core.translate',
    'superdesk.core.workflow',
    'superdesk.core.loading',
    'superdesk.core.editor3',

    // services/
    'superdesk.core.services.beta',
    'superdesk.core.services.data',
    'superdesk.core.services.modal',
    'superdesk.core.services.dragdrop',
    'superdesk.core.services.server',
    'superdesk.core.services.entity',
    'superdesk.core.services.permissions',
    'superdesk.core.services.storage',
    'superdesk.core.services.pageTitle',

    'superdesk.core.directives'
]);

angular.module('superdesk.apps', [
    'superdesk.apps.settings',
    'superdesk.apps.dashboard',
    'superdesk.apps.users',
    'superdesk.apps.users.import',
    'superdesk.apps.users.profile',
    'superdesk.apps.users.activity',
    'superdesk.apps.archive',
    'superdesk.apps.ingest',
    'superdesk.apps.desks',
    //'superdesk.apps.groups',
    //'superdesk.apps.products',
    'superdesk.apps.authoring',
    'superdesk.apps.packaging',
    'superdesk.apps.spellcheck',
    'superdesk.apps.notification',
    'superdesk.apps.highlights',
    'superdesk.apps.translations',
    'superdesk.apps.content_filters', // Can't remove
    'superdesk.apps.dictionaries',
    'superdesk.apps.vocabularies',
    //'superdesk.apps.searchProviders',
    //'superdesk.apps.stream',
    'superdesk.apps.publish', // Can't remove
    'superdesk.apps.templates',
    'superdesk.apps.monitoring',
    'superdesk.apps.profiling'
]);

angular.module('superdesk.config').constant('config', config);

let liveblogModules = [
    'liveblog.bloglist',
    'liveblog.edit',
    'liveblog.posts',
    'liveblog.blog',
    'liveblog.themes',
    'ngMessages'
];

if (config.syndication)
    liveblogModules.push('liveblog.syndication');

let liveblog = angular.module('liveblog', liveblogModules);

sdCore.constant('lodash', _);

liveblog.constant('config', config);
liveblog.constant('lodash', _);
liveblog.constant('moment', moment);

liveblog.config(['$routeProvider', '$locationProvider', ($routeProvider, $locationProvider) => {
    $locationProvider.hashPrefix('');
    $routeProvider.when('/', {redirectTo: '/liveblog'});
}]);

liveblog.run(['$rootScope', '$timeout', 'notify', 'gettext', 'session',
    function($rootScope, $timeout, notify, gettext, session) {
        var alertTimeout;
        $rootScope.$on('disconnected', function(event) {
            $timeout.cancel(alertTimeout);
            if (session && session.sessionId) {
                alertTimeout = $timeout(function() {
                    notify.pop();
                    notify.error(gettext('Disconnected from Notification Server, attempting to reconnect ...'), 20000);
                }, 100);
            }
        });
        $rootScope.$on('connected', function(event) {
            //only show the 'connected' message if there was a disconnect event
            if (alertTimeout) {
                $timeout.cancel(alertTimeout);
                alertTimeout = $timeout(function() {
                    notify.pop();
                    notify.success(gettext('Connected to Notification Server!'));
                }, 100);
            }
        });
    }]);

let body = angular.element('body');

body.ready(() => {
    /**
     * @ngdoc module
     * @name superdesk-client
     * @packageName superdesk-client
     * @description The root superdesk module.
     */
    angular.bootstrap(body, [
        'superdesk.config',
        'superdesk.core',
        'superdesk.apps',
        'liveblog'
    ], {strictDi: true});

    window.superdeskIsReady = true;
});
<|endoftext|>


<commit_before>// @flow

import type {Subscription} from './UserSubscriptions'

type Permission = {
  type: string,
  feeds: Array<string>
}

type Project = {
  defaultFeeds: Array<string>,
  project_id: string,
  permissions: Array<Permission>
}

type Organization = {
  organization_id: string,
  permissions: Array<Permission>
}

export type DatatoolsJson = {
  client_id: string,
  organizations: Array<Organization>,
  permissions: Array<Permission>,
  projects: Array<Project>,
  subscriptions: Array<Subscription>
}

export type DatatoolsApps = Array<DatatoolsJson>

export default class UserPermissions {
  appPermissionLookup: {[string]: Permission} = {}

  organizationLookup: {[string]: Organization} = {}

  orgPermissionLookup: {[string]: Permission} = {}

  projectLookup: {[string]: Project} = {}

  constructor (datatoolsApps?: DatatoolsApps) {
    // check for map of datatools apps and get the right application
    const clientId = process.env.AUTH0_CLIENT_ID
    if (!clientId) throw new Error('Auth0 client ID must be set in config')
    // If missing datatoolsApp, construct an empty permissions object.
    if (!datatoolsApps) return
    else if (!Array.isArray(datatoolsApps)) {
      console.warn('User app_metadata is misconfigured.', datatoolsApps)
      return
    }
    const datatoolsJson = datatoolsApps.find(dt => dt.client_id === clientId)
    if (datatoolsJson) {
      const {organizations, permissions, projects} = datatoolsJson
      if (permissions) {
        for (const appPermission of permissions) {
          this.appPermissionLookup[appPermission.type] = appPermission
        }
      }
      if (organizations) {
        for (const organization of organizations) {
          this.organizationLookup[organization.organization_id] = organization
          const {permissions: orgPermissions} = organization
          // There should only be one organization per user, so there shouldn't
          // be any chance that the permission lookup is overwritten.
          for (const permission of orgPermissions) {
            this.orgPermissionLookup[permission.type] = permission
          }
        }
      }
      if (projects) {
        for (const project of projects) {
          this.projectLookup[project.project_id] = project
        }
      }
    } else {
      throw new Error('User app_metadata is misconfigured.')
    }
  }

  isApplicationAdmin () {
    return ('administer-application' in this.appPermissionLookup)
  }

  hasOrganization (orgId: string) {
    if (this.isApplicationAdmin()) return true
    return (orgId in this.organizationLookup)
  }

  getOrganizationId () {
    const keys = Object.keys(this.organizationLookup)
    return keys && keys.length ? keys[0] : null
  }

  // as opposed to the specific isOrganizationAdmin, this method checks for generic admin access to some organization
  canAdministerAnOrganization () {
    if (this.isApplicationAdmin()) return true
    return ('administer-organization' in this.orgPermissionLookup)
  }

  isOrganizationAdmin (orgId: ?string) {
    if (this.isApplicationAdmin()) return true
    return orgId &&
      this.hasOrganization(orgId) &&
      this.getOrganizationPermission(orgId, 'administer-organization') != null
  }

  getOrganizationPermission (organizationId: string, permissionType: string) {
    if (!this.hasOrganization(organizationId)) return null
    for (const permission of this.getOrganizationPermissions(organizationId)) {
      if (permission.type === permissionType) return permission
    }
    return null
  }

  getOrganizationPermissions (organizationId: string): Array<Permission> {
    if (!this.hasOrganization(organizationId)) return []
    return this.organizationLookup[organizationId].permissions
  }

  hasProject (projectId: string, organizationId: ?string) {
    if (this.isOrganizationAdmin(organizationId)) return true
    return (projectId in this.projectLookup)
  }

  isProjectAdmin (projectId: string, organizationId: ?string) {
    if (this.isApplicationAdmin()) return true
    // TODO: make project admin subject to org admin
    if (this.isOrganizationAdmin(organizationId)) return true
    return this.hasProject(projectId) && this.getProjectPermission(projectId, 'administer-project') != null
  }

  getProjectPermissions (projectId: string): Array<Permission> {
    if (!this.hasProject(projectId)) return []
    return this.projectLookup[projectId].permissions
  }

  getProjectDefaultFeeds (projectId: string): Array<string> {
    if (!this.hasProject(projectId)) return []
    return this.projectLookup[projectId].defaultFeeds || []
  }

  hasProjectPermission (organizationId: string, projectId: string, permissionType: string) {
    if (this.isProjectAdmin(projectId, organizationId)) return true
    const p = this.getProjectPermission(projectId, permissionType)
    return (p !== null)
  }

  getProjectPermission (projectId: string, permissionType: string): ?Permission {
    if (!this.hasProject(projectId)) return null
    var projectPermissions = this.getProjectPermissions(projectId)
    for (const permission of projectPermissions) {
      if (permission.type === permissionType) return permission
    }
    return null
  }

  hasFeed (listOfFeeds: Array<string>, feedId: string) {
    if (listOfFeeds.indexOf('*') > -1) return true
    else if (listOfFeeds.indexOf(feedId) > -1) return true
    return null
  }

  hasFeedPermission (organizationId: ?string, projectId: string, feedId: string, permissionType: string) {
    if (this.isProjectAdmin(projectId, organizationId)) return true
    const permission = this.getProjectPermission(projectId, permissionType)
    if (permission) {
      const defaultFeeds = this.getProjectDefaultFeeds(projectId)
      const permissionFeeds = permission.feeds || []
      if (this.hasFeed(defaultFeeds, feedId)) return true
      if (this.hasFeed(permissionFeeds, feedId)) return true
    }
    return null
  }
}
<commit_msg>refactor(user): do not throw error if invalid user app_metadata encountered<commit_after>// @flow

import type {Subscription} from './UserSubscriptions'

type Permission = {
  type: string,
  feeds: Array<string>
}

type Project = {
  defaultFeeds: Array<string>,
  project_id: string,
  permissions: Array<Permission>
}

type Organization = {
  organization_id: string,
  permissions: Array<Permission>
}

export type DatatoolsJson = {
  client_id: string,
  organizations: Array<Organization>,
  permissions: Array<Permission>,
  projects: Array<Project>,
  subscriptions: Array<Subscription>
}

export type DatatoolsApps = Array<DatatoolsJson>

export default class UserPermissions {
  appPermissionLookup: {[string]: Permission} = {}

  organizationLookup: {[string]: Organization} = {}

  orgPermissionLookup: {[string]: Permission} = {}

  projectLookup: {[string]: Project} = {}

  constructor (datatoolsApps?: DatatoolsApps) {
    // check for map of datatools apps and get the right application
    const clientId = process.env.AUTH0_CLIENT_ID
    if (!clientId) throw new Error('Auth0 client ID must be set in config')
    // If missing datatoolsApp, construct an empty permissions object.
    if (!datatoolsApps) return
    else if (!Array.isArray(datatoolsApps)) {
      console.warn('User app_metadata is misconfigured.', datatoolsApps)
      return
    }
    const datatoolsJson = datatoolsApps.find(dt => dt.client_id === clientId)
    if (datatoolsJson) {
      const {organizations, permissions, projects} = datatoolsJson
      if (permissions) {
        for (const appPermission of permissions) {
          this.appPermissionLookup[appPermission.type] = appPermission
        }
      }
      if (organizations) {
        for (const organization of organizations) {
          this.organizationLookup[organization.organization_id] = organization
          const {permissions: orgPermissions} = organization
          // There should only be one organization per user, so there shouldn't
          // be any chance that the permission lookup is overwritten.
          for (const permission of orgPermissions) {
            this.orgPermissionLookup[permission.type] = permission
          }
        }
      }
      if (projects) {
        for (const project of projects) {
          this.projectLookup[project.project_id] = project
        }
      }
    } else {
      console.warn('User does not have permissions for this application\'s client ID.', datatoolsApps)
    }
  }

  isApplicationAdmin () {
    return ('administer-application' in this.appPermissionLookup)
  }

  hasOrganization (orgId: string) {
    if (this.isApplicationAdmin()) return true
    return (orgId in this.organizationLookup)
  }

  getOrganizationId () {
    const keys = Object.keys(this.organizationLookup)
    return keys && keys.length ? keys[0] : null
  }

  // as opposed to the specific isOrganizationAdmin, this method checks for generic admin access to some organization
  canAdministerAnOrganization () {
    if (this.isApplicationAdmin()) return true
    return ('administer-organization' in this.orgPermissionLookup)
  }

  isOrganizationAdmin (orgId: ?string) {
    if (this.isApplicationAdmin()) return true
    return orgId &&
      this.hasOrganization(orgId) &&
      this.getOrganizationPermission(orgId, 'administer-organization') != null
  }

  getOrganizationPermission (organizationId: string, permissionType: string) {
    if (!this.hasOrganization(organizationId)) return null
    for (const permission of this.getOrganizationPermissions(organizationId)) {
      if (permission.type === permissionType) return permission
    }
    return null
  }

  getOrganizationPermissions (organizationId: string): Array<Permission> {
    if (!this.hasOrganization(organizationId)) return []
    return this.organizationLookup[organizationId].permissions
  }

  hasProject (projectId: string, organizationId: ?string) {
    if (this.isOrganizationAdmin(organizationId)) return true
    return (projectId in this.projectLookup)
  }

  isProjectAdmin (projectId: string, organizationId: ?string) {
    if (this.isApplicationAdmin()) return true
    // TODO: make project admin subject to org admin
    if (this.isOrganizationAdmin(organizationId)) return true
    return this.hasProject(projectId) && this.getProjectPermission(projectId, 'administer-project') != null
  }

  getProjectPermissions (projectId: string): Array<Permission> {
    if (!this.hasProject(projectId)) return []
    return this.projectLookup[projectId].permissions
  }

  getProjectDefaultFeeds (projectId: string): Array<string> {
    if (!this.hasProject(projectId)) return []
    return this.projectLookup[projectId].defaultFeeds || []
  }

  hasProjectPermission (organizationId: string, projectId: string, permissionType: string) {
    if (this.isProjectAdmin(projectId, organizationId)) return true
    const p = this.getProjectPermission(projectId, permissionType)
    return (p !== null)
  }

  getProjectPermission (projectId: string, permissionType: string): ?Permission {
    if (!this.hasProject(projectId)) return null
    var projectPermissions = this.getProjectPermissions(projectId)
    for (const permission of projectPermissions) {
      if (permission.type === permissionType) return permission
    }
    return null
  }

  hasFeed (listOfFeeds: Array<string>, feedId: string) {
    if (listOfFeeds.indexOf('*') > -1) return true
    else if (listOfFeeds.indexOf(feedId) > -1) return true
    return null
  }

  hasFeedPermission (organizationId: ?string, projectId: string, feedId: string, permissionType: string) {
    if (this.isProjectAdmin(projectId, organizationId)) return true
    const permission = this.getProjectPermission(projectId, permissionType)
    if (permission) {
      const defaultFeeds = this.getProjectDefaultFeeds(projectId)
      const permissionFeeds = permission.feeds || []
      if (this.hasFeed(defaultFeeds, feedId)) return true
      if (this.hasFeed(permissionFeeds, feedId)) return true
    }
    return null
  }
}
<|endoftext|>


<commit_before>'use strict';

var Pqueue = require('../');
var assert = require('assert');

describe('constructor', function() {
  it('should return a pqueue object', function() {
    var q = new Pqueue();
    assert(q.length === 0);
  });

  it('should initialize the queue with an array argument', function() {
    var q = new Pqueue([1,2,3]);
    assert(q.length === 3);
    assert(q.pop() === 3);
    assert(q.pop() === 2);
    assert(q.pop() === 1);
    assert(q.pop() === undefined);
  });

  it('should initialize the queue with array, min=true and custom getPriority', function() {
    var data = [
      {k: 'a', v: 1},
      {k: 'b', v: 3},
      {k: 'c', v: 2}
    ];

    var opts = {
      min: true,
      getPriority: (o) => o.v
    };

    var q = new Pqueue(data, opts);

    assert(q.length === 3);
    assert(q.pop().k === 'a');
    assert(q.pop().k === 'c');
    assert(q.pop().k === 'b');
    assert(q.pop() === undefined);
  });
});

describe('min push', function() {
  it('should push values, min values should be at the top', function() {
    var q = new Pqueue({min: true});
    q.push(3);
    assert(q.peek() === 3);
    assert(q.length === 1);
    q.push(5);
    assert(q.peek() === 3);
    assert(q.length === 2);
    q.push(1);
    assert(q.peek() === 1);
    assert(q.length === 3);
  });
});

describe('min pop', function() {
  it('should pop highest priority values and re-order the queue', function() {
    var q = new Pqueue({min: true});
    q.push(3);
    q.push(5);
    q.push(6);
    q.push(1);
    q.push(0);
    q.push(7);

    assert(q.pop() === 0);
    assert(q.pop() === 1);
    assert(q.pop() === 3);
    assert(q.pop() === 5);
    assert(q.pop() === 6);
    assert(q.pop() === 7);
    assert(q.pop() === undefined);
    assert(q.peek() === undefined);
    assert(q.length === 0);
  });
});

describe('max push', function() {
  it('should push values, max values should be at the top', function() {
    var q = new Pqueue();
    q.push(3);
    assert(q.peek() === 3);
    assert(q.length === 1);
    q.push(5);
    assert(q.peek() === 5);
    assert(q.length === 2);
    q.push(1);
    assert(q.peek() === 5);
    assert(q.length === 3);
  });
});

describe('max pop', function() {
  it('should pop highest priority values and re-order the queue', function() {
    var q = new Pqueue();
    q.push(3);
    q.push(5);
    q.push(6);
    q.push(1);
    q.push(0);
    q.push(7);
    assert(q.pop() === 7);
    assert(q.pop() === 6);
    assert(q.pop() === 5);
    assert(q.pop() === 3);
    assert(q.pop() === 1);
    assert(q.pop() === 0);
    assert(q.length === 0);
  });
});

describe('peek', function() {
  it('should return the front of the queue', function() {
    var q = new Pqueue();
    assert(q.peek() === undefined);
    q.push(3);
    assert(q.peek() === 3);
    assert(q.length === 1);
  });
});

describe('clear', function() {
  it('should clear the queue', function() {
    var q = new Pqueue();
    assert(q.length === 0);
    q.push(3);
    q.push(5);
    assert(q.length === 2);
    q.clear();
    assert(q.length === 0);
  });
});
<commit_msg>Adds tests for multi field priority<commit_after>'use strict';

var Pqueue = require('../');
var assert = require('assert');

describe('constructor', function() {
  it('should return a pqueue object', function() {
    var q = new Pqueue();
    assert(q.length === 0);
  });

  it('should initialize the queue with an array argument', function() {
    var q = new Pqueue([1,2,3]);
    assert(q.length === 3);
    assert(q.pop() === 3);
    assert(q.pop() === 2);
    assert(q.pop() === 1);
    assert(q.pop() === undefined);
  });

  it('should initialize the queue with array, min=true and custom getPriority', function() {
    var data = [
      {k: 'a', v: 1},
      {k: 'b', v: 3},
      {k: 'c', v: 2}
    ];

    var opts = {
      min: true,
      getPriority: (o) => o.v
    };

    var q = new Pqueue(data, opts);

    assert(q.length === 3);
    assert(q.pop().k === 'a');
    assert(q.pop().k === 'c');
    assert(q.pop().k === 'b');
    assert(q.pop() === undefined);
  });

  it('should initialize the queue with array and custom comparator', function () {
    var data = [
      {k: 'a', x: 2, y: 2},
      {k: 'b', x: 3, y: 1},
      {k: 'c', x: 1, y: 2}
    ];

    var opts = {
      comparator: (a, b) => a.y === b.y ? a.x - b.x : a.y - b.y
    };

    var q = new Pqueue(data, opts);

    assert(q.length === 3);
    assert(q.pop().k === 'a');
    assert(q.pop().k === 'c');
    assert(q.pop().k === 'b');
    assert(q.pop() === undefined);
  });

  it('should initialize the queue with array, min=true and custom comparator', function () {
    var data = [
      {k: 'a', x: 2, y: 2},
      {k: 'b', x: 3, y: 1},
      {k: 'c', x: 1, y: 2}
    ];

    var opts = {
      min: true,
      comparator: (a, b) => a.y === b.y ? a.x - b.x : a.y - b.y
    };

    var q = new Pqueue(data, opts);

    assert(q.length === 3);
    assert(q.pop().k === 'b');
    assert(q.pop().k === 'c');
    assert(q.pop().k === 'a');
    assert(q.pop() === undefined);
  });
});

describe('min push', function() {
  it('should push values, min values should be at the top', function() {
    var q = new Pqueue({min: true});
    q.push(3);
    assert(q.peek() === 3);
    assert(q.length === 1);
    q.push(5);
    assert(q.peek() === 3);
    assert(q.length === 2);
    q.push(1);
    assert(q.peek() === 1);
    assert(q.length === 3);
  });
});

describe('min pop', function() {
  it('should pop highest priority values and re-order the queue', function() {
    var q = new Pqueue({min: true});
    q.push(3);
    q.push(5);
    q.push(6);
    q.push(1);
    q.push(0);
    q.push(7);

    assert(q.pop() === 0);
    assert(q.pop() === 1);
    assert(q.pop() === 3);
    assert(q.pop() === 5);
    assert(q.pop() === 6);
    assert(q.pop() === 7);
    assert(q.pop() === undefined);
    assert(q.peek() === undefined);
    assert(q.length === 0);
  });
});

describe('max push', function() {
  it('should push values, max values should be at the top', function() {
    var q = new Pqueue();
    q.push(3);
    assert(q.peek() === 3);
    assert(q.length === 1);
    q.push(5);
    assert(q.peek() === 5);
    assert(q.length === 2);
    q.push(1);
    assert(q.peek() === 5);
    assert(q.length === 3);
  });
});

describe('max pop', function() {
  it('should pop highest priority values and re-order the queue', function() {
    var q = new Pqueue();
    q.push(3);
    q.push(5);
    q.push(6);
    q.push(1);
    q.push(0);
    q.push(7);
    assert(q.pop() === 7);
    assert(q.pop() === 6);
    assert(q.pop() === 5);
    assert(q.pop() === 3);
    assert(q.pop() === 1);
    assert(q.pop() === 0);
    assert(q.length === 0);
  });
});

describe('peek', function() {
  it('should return the front of the queue', function() {
    var q = new Pqueue();
    assert(q.peek() === undefined);
    q.push(3);
    assert(q.peek() === 3);
    assert(q.length === 1);
  });
});

describe('clear', function() {
  it('should clear the queue', function() {
    var q = new Pqueue();
    assert(q.length === 0);
    q.push(3);
    q.push(5);
    assert(q.length === 2);
    q.clear();
    assert(q.length === 0);
  });
});
<|endoftext|>


<commit_before>var Bubo = require('../index');
var _ = require('underscore');
var expect = require('chai').expect;
var util = require('util');

function getAttributeString(pt, ignoredAttributes) {
    ignoredAttributes = ignoredAttributes || {};
    var keys = [];
    _.each(pt, function(val, key) {
        if (!ignoredAttributes[key]) {
            keys.push(key);
        }
    });

    keys.sort();

    var attrString = '';
    var comma = '';
    for (var tag = keys.length - 1; tag >= 0; tag--) {
        attrString = keys[tag] + '=' + pt[keys[tag]] + comma + attrString;
        comma = ',';
    }

    return attrString;
}

var result = {};

function add(bubo, bucket, point) {
    return bubo.add(bucket, point, result);
}

function contains(bubo, bucket, point) {
    return bubo.contains(bucket, point);
}

var options = {}

describe('bubo', function() {
    it('initializes properly', function() {
        var bubo = new Bubo(options);

        expect(bubo.add).is.a.function;

        expect(function() {
            bubo.add({});
        }).to.throw('Add: invalid arguments');

    });

    it('runs the c++ unit tests', function() {
        var bubo = new Bubo(options);
        bubo.test();
    });

    it('handles add and remove correctly', function() {
        var bubo = new Bubo(options);

        var pt = {
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var expected = getAttributeString(pt);

        // add in different space-buckets combinations.
        // The 'found' should be false when the space-bucket is called first time.

        var found = add(bubo, 'aaa', pt);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.false;

        found = add(bubo, 'bbb', pt);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.false;

        found = add(bubo, 'ccc', pt);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.false;

        // The same calls should have found == true the second time.
        found = add(bubo, 'aaa', pt);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.true;

        found = add(bubo, 'bbb', pt);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.true;

        found = add(bubo, 'ccc', pt);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.true;

        // modify pt and add. This should be false, and the attr_atr should differ from expected.
        var pt2 = JSON.parse(JSON.stringify(pt));
        pt2.pop = 'NY';
        found = add(bubo, 'aaa', pt2);
        expect(result.attr_str).not.equal(expected);
        expect(found).to.be.false;

    });

    it('contains: checks if a point has been added without adding it', function() {
        var bubo = new Bubo(options);
        var bucket = 'contains_bucket';

        var pt = {
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var found = contains(bubo, bucket, pt);
        expect(found).equal(false);

        found = contains(bubo, bucket, pt);
        expect(found).equal(false);

        found = add(bubo, bucket, pt);
        expect(found).equal(false);

        found = contains(bubo, bucket, pt);
        expect(found).equal(true);

        found = contains(bubo, bucket, pt);
        expect(found).equal(true);
    });

    it('delete: removes a specified point', function() {
        var bubo = new Bubo(options);

        var pt = {
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var bucket = 'delete_test';

        add(bubo, bucket, pt);

        expect(contains(bubo, bucket, pt)).equal(true);

        bubo.delete('delete_test', pt);

        expect(contains(bubo, bucket, pt)).equal(false);
    });

    it('get_buckets', function() {
        var bubo = new Bubo(options);

        var initial_buckets = bubo.get_buckets();
        expect(initial_buckets).deep.equal([]);

        var pt = {
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var bucket1 = 'bucket1';
        var bucket2 = 'bucket2';
        var bucket3 = 'bucket3';

        add(bubo, bucket1, pt);
        add(bubo, bucket2, pt);
        add(bubo, bucket3, pt);

        var buckets = bubo.get_buckets().sort();

        expect(buckets).deep.equal([bucket1, bucket2, bucket3]);
    });

    it('handles delete_bucket correctly', function() {
        var bubo = new Bubo(options);

        var pt = {
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var bucket = 'delete_this_bucket';

        var found = add(bubo, bucket, pt);
        expect(found).to.be.false;
        expect(contains(bubo, bucket, pt)).equal(true);

        bubo.delete_bucket(bucket);
        expect(contains(bubo, bucket, pt)).equal(false);
    });

    it('returns appropriate stats', function() {
        var bubo = new Bubo({
            ignoredAttributes: {
                time: true,
                value: true,
                source_type: true
            }
        });

        var s1 = {};
        bubo.stats(s1);
        expect(s1.strings_table.num_tags).equal(0);

        // [1]
        var pt = {
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric'
        };

        var found = add(bubo, 'spc@bkt', pt);
        s1 = {};
        bubo.stats(s1);

        expect(s1.strings_table.num_tags).equal(6); // 9 attributes. ignoring time, value, and source_type, 6.
        expect(s1.strings_table.pop).equal(1);
        expect(s1['spc@bkt'].attr_entries).equal(1);
        expect(s1['spc@bkt'].blob_allocated_bytes).equal(20971520); //20MB default size
        expect(s1['spc@bkt'].blob_used_bytes).equal(13); // 1 byte for size, 6 x 2 bytes since all small numbers.

        // [2] another point that reuses some strings.
        pt = {
            name: 'apple',
            pop: 'NY',
        };
        found = add(bubo, 'spc@bkt', pt);
        s1 = {};
        bubo.stats(s1);

        expect(s1.strings_table.num_tags).equal(6); // no new tags. should be same.
        expect(s1.strings_table.name).equal(2);
        expect(s1.strings_table.pop).equal(2);
        expect(s1['spc@bkt'].attr_entries).equal(2);
        expect(s1['spc@bkt'].blob_allocated_bytes).equal(20971520); //20MB default size
        expect(s1['spc@bkt'].blob_used_bytes).equal(18); // 1 byte for size + 2 x 2 bytes = 5. already have 13, so total 18.
    });

    it('has an ignoredAttributes per Bubo', function() {
        var bucket = 'ignored_attributes_bucket';
        var pt = {
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var ignoredAttributes1 = {
            time: true
        };

        var ignoredAttributes2 = {
            time: true,
            pop: true,
            name: true
        };

        var bubo1 = new Bubo({
            ignoredAttributes: ignoredAttributes1
        });

        var bubo2 = new Bubo({
            ignoredAttributes: ignoredAttributes2
        });

        var expected1 = getAttributeString(pt, ignoredAttributes1);
        add(bubo1, bucket, pt);
        expect(result.attr_str).equal(expected1);

        var expected2 = getAttributeString(pt, ignoredAttributes2);
        add(bubo2, bucket, pt);
        expect(result.attr_str).equal(expected2);
    });

    it.skip('profiles the memory use of adding 7 million points', function() {
        this.timeout(900000);
        var bubo = new Bubo(options);

        var pt = {
            space: 'default',
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var s1 = {};
        console.log('js:  ' + util.inspect(process.memoryUsage()));
        for (var i = 0; i < 7000000; i++) {
            pt.pop = 'SF' + i;
            pt.value2 = pt.value2 + (2* i);
            add(bubo, 'test-space-1@421', pt);
            if (i % 1000000 === 0) {
                s1 = {};
                bubo.stats(s1);
                console.log('[' + i + ']' + JSON.stringify(s1, null, 4));
            }
        }
        console.log('js:  ' + util.inspect(process.memoryUsage()));

        s1 = {};
        bubo.stats(s1);
        console.log(JSON.stringify(s1, null, 4));

    });

    it('throws an error looking up a big point', function() {
        var bubo = new Bubo(options);
        var too_big_string = '';

        for (var k = 0; k < 5000; k++) {
            too_big_string += 'dave rules!! ';
        }

        var point = {
            source_type: 'metric',
            space: 'default',
            value: 1,
            time: Date.now(),
            too_big: too_big_string
        };

        try {
            add(bubo, 'big@data', point);
            throw new Error('add should have failed');
        } catch (err) {
            expect(err.message).equal('point too big');
        }
    });
});
<commit_msg>why do I declare this doofy point 8 times<commit_after>var Bubo = require('../index');
var _ = require('underscore');
var expect = require('chai').expect;
var util = require('util');

function getAttributeString(point, ignoredAttributes) {
    ignoredAttributes = ignoredAttributes || {};
    var keys = [];
    _.each(point, function(val, key) {
        if (!ignoredAttributes[key]) {
            keys.push(key);
        }
    });

    keys.sort();

    var attrString = '';
    var comma = '';
    for (var tag = keys.length - 1; tag >= 0; tag--) {
        attrString = keys[tag] + '=' + point[keys[tag]] + comma + attrString;
        comma = ',';
    }

    return attrString;
}

var result = {};

function add(bubo, bucket, point) {
    return bubo.add(bucket, point, result);
}

function contains(bubo, bucket, point) {
    return bubo.contains(bucket, point);
}

var options = {}

describe('bubo', function() {
    var point = {
        name: 'cpu.system',
        pop: 'SF',
        host: 'foo.com',
        time: new Date(),
        time2: new Date(),
        value: 100,
        value2: 100,
        value3: 100.999,
        source_type: 'metric',
    };

    it('initializes properly', function() {
        var bubo = new Bubo(options);

        expect(bubo.add).is.a.function;

        expect(function() {
            bubo.add({});
        }).to.throw('Add: invalid arguments');

    });

    it('runs the c++ unit tests', function() {
        var bubo = new Bubo(options);
        bubo.test();
    });

    it('handles add and remove correctly', function() {
        var bubo = new Bubo(options);

        var expected = getAttributeString(point);

        // add in different space-buckets combinations.
        // The 'found' should be false when the space-bucket is called first time.

        var found = add(bubo, 'aaa', point);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.false;

        found = add(bubo, 'bbb', point);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.false;

        found = add(bubo, 'ccc', point);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.false;

        // The same calls should have found == true the second time.
        found = add(bubo, 'aaa', point);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.true;

        found = add(bubo, 'bbb', point);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.true;

        found = add(bubo, 'ccc', point);
        expect(result.attr_str).equal(expected);
        expect(found).to.be.true;

        // modify point and add. This should be false, and the attr_atr should differ from expected.
        var point2 = JSON.parse(JSON.stringify(point));
        point2.pop = 'NY';
        found = add(bubo, 'aaa', point2);
        expect(result.attr_str).not.equal(expected);
        expect(found).to.be.false;

    });

    it('contains: checks if a point has been added without adding it', function() {
        var bubo = new Bubo(options);
        var bucket = 'contains_bucket';

        var found = contains(bubo, bucket, point);
        expect(found).equal(false);

        found = contains(bubo, bucket, point);
        expect(found).equal(false);

        found = add(bubo, bucket, point);
        expect(found).equal(false);

        found = contains(bubo, bucket, point);
        expect(found).equal(true);

        found = contains(bubo, bucket, point);
        expect(found).equal(true);
    });

    it('delete: removes a specified point', function() {
        var bubo = new Bubo(options);
        var bucket = 'delete_test';

        add(bubo, bucket, point);

        expect(contains(bubo, bucket, point)).equal(true);

        bubo.delete('delete_test', point);

        expect(contains(bubo, bucket, point)).equal(false);
    });

    it('get_buckets', function() {
        var bubo = new Bubo(options);
        var initial_buckets = bubo.get_buckets();

        expect(initial_buckets).deep.equal([]);

        var bucket1 = 'bucket1';
        var bucket2 = 'bucket2';
        var bucket3 = 'bucket3';

        add(bubo, bucket1, point);
        add(bubo, bucket2, point);
        add(bubo, bucket3, point);

        var buckets = bubo.get_buckets().sort();

        expect(buckets).deep.equal([bucket1, bucket2, bucket3]);
    });

    it('handles delete_bucket correctly', function() {
        var bubo = new Bubo(options);
        var bucket = 'delete_this_bucket';

        var found = add(bubo, bucket, point);
        expect(found).to.be.false;
        expect(contains(bubo, bucket, point)).equal(true);

        bubo.delete_bucket(bucket);
        expect(contains(bubo, bucket, point)).equal(false);
    });

    it('returns appropriate stats', function() {
        var bubo = new Bubo({
            ignoredAttributes: {
                time: true,
                value: true,
                source_type: true
            }
        });

        var s1 = {};
        bubo.stats(s1);
        expect(s1.strings_table.num_tags).equal(0);

        var found = add(bubo, 'spc@bkt', point);
        s1 = {};
        bubo.stats(s1);

        expect(s1.strings_table.num_tags).equal(6); // 9 attributes. ignoring time, value, and source_type, 6.
        expect(s1.strings_table.pop).equal(1);
        expect(s1['spc@bkt'].attr_entries).equal(1);
        expect(s1['spc@bkt'].blob_allocated_bytes).equal(20971520); //20MB default size
        expect(s1['spc@bkt'].blob_used_bytes).equal(13); // 1 byte for size, 6 x 2 bytes since all small numbers.

        var point2 = {
            name: 'apple',
            pop: 'NY',
        };
        found = add(bubo, 'spc@bkt', point2);
        s1 = {};
        bubo.stats(s1);

        expect(s1.strings_table.num_tags).equal(6); // no new tags. should be same.
        expect(s1.strings_table.name).equal(2);
        expect(s1.strings_table.pop).equal(2);
        expect(s1['spc@bkt'].attr_entries).equal(2);
        expect(s1['spc@bkt'].blob_allocated_bytes).equal(20971520); //20MB default size
        expect(s1['spc@bkt'].blob_used_bytes).equal(18); // 1 byte for size + 2 x 2 bytes = 5. already have 13, so total 18.
    });

    it('has an ignoredAttributes per Bubo', function() {
        var bucket = 'ignored_attributes_bucket';
        var ignoredAttributes1 = {
            time: true
        };

        var ignoredAttributes2 = {
            time: true,
            pop: true,
            name: true
        };

        var bubo1 = new Bubo({
            ignoredAttributes: ignoredAttributes1
        });

        var bubo2 = new Bubo({
            ignoredAttributes: ignoredAttributes2
        });

        var expected1 = getAttributeString(point, ignoredAttributes1);
        add(bubo1, bucket, point);
        expect(result.attr_str).equal(expected1);

        var expected2 = getAttributeString(point, ignoredAttributes2);
        add(bubo2, bucket, point);
        expect(result.attr_str).equal(expected2);
    });

    it.skip('profiles the memory use of adding 7 million points', function() {
        this.timeout(900000);
        var bubo = new Bubo(options);

        var pt = {
            space: 'default',
            name: 'cpu.system',
            pop: 'SF',
            host: 'foo.com',
            time: new Date(),
            time2: new Date(),
            value: 100,
            value2: 100,
            value3: 100.999,
            source_type: 'metric',
        };

        var s1 = {};
        console.log('js:  ' + util.inspect(process.memoryUsage()));
        for (var i = 0; i < 7000000; i++) {
            pt.pop = 'SF' + i;
            pt.value2 = pt.value2 + (2* i);
            add(bubo, 'test-space-1@421', pt);
            if (i % 1000000 === 0) {
                s1 = {};
                bubo.stats(s1);
                console.log('[' + i + ']' + JSON.stringify(s1, null, 4));
            }
        }
        console.log('js:  ' + util.inspect(process.memoryUsage()));

        s1 = {};
        bubo.stats(s1);
        console.log(JSON.stringify(s1, null, 4));

    });

    it('throws an error looking up a big point', function() {
        var bubo = new Bubo(options);
        var too_big_string = '';

        for (var k = 0; k < 5000; k++) {
            too_big_string += 'dave rules!! ';
        }

        var point = {
            source_type: 'metric',
            space: 'default',
            value: 1,
            time: Date.now(),
            too_big: too_big_string
        };

        try {
            add(bubo, 'big@data', point);
            throw new Error('add should have failed');
        } catch (err) {
            expect(err.message).equal('point too big');
        }
    });
});
<|endoftext|>


<commit_before>'use strict';

const os = require('os');
const test = require('tape');
const cliParse = require('../lib/cli-parse');

test('cli-parse: series', (t) => {
    let result = cliParse(['--series', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    let expected = {
        name: 'run',
        cmd: 'ls && pwd',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: parallel', (t) => {
    let result = cliParse(['--parallel', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    let expected = {
        name: 'run',
        cmd: 'ls & pwd',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: parallel --quiet', (t) => {
    let result = cliParse(['--parallel', 'one', 'two', '--quiet'], {
        one: 'ls',
        two: 'pwd'
    });

    let expected = {
        name: 'run',
        cmd: 'ls & pwd',
        quiet: true,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: parallel: before script', (t) => {
    let result = cliParse(['main', '--parallel', 'one', 'two'], {
        one: 'ls',
        two: 'pwd',
        main: 'echo hi'
    });

    let expected = {
        name: 'run',
        cmd: 'echo hi && ls & pwd',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: series and parallel', (t) => {
    let result = cliParse(['--s', 'one', 'two', '-p', 'three', 'four'], {
        one: 'ls',
        two: 'pwd',
        three: 'whoami',
        four: 'ps aux'
    });

    let expected = {
        name: 'run',
        cmd: 'ls && pwd && whoami & ps aux',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: series calm: linux', (t) => {
    const platform = os.platform;

    os.platform = () => 'linux';

    const result = cliParse(['--series-calm', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    const expected = {
        name: 'run',
        cmd: 'ls || true && pwd || true',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object with "true"');

    os.platfrom = platform;

    t.end();
});

test('cli-parse: parallel calm: windows', (t) => {
    let platform = os.platform;

    os.platform = () => 'win32';

    let result = cliParse(['--parallel-calm', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    let expected = {
        name: 'run',
        cmd: 'ls || (exit 0) & pwd || (exit 0)',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object with "exit 0"');

    os.platfrom = platform;

    t.end();
});

test('cli-parse: --calm: linux', (t) => {
    let platform = os.platform;

    os.platform = () => 'linux';

    let result = cliParse(['--calm', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    let expected = {
        name: 'run',
        cmd: 'ls || true && pwd || true',
        quiet: false,
        calm: true
    };

    t.deepEqual(result, expected, 'should build cmd object with "exit 0"');

    os.platfrom = platform;

    t.end();
});

test('cli-parse: scripts arguments', (t) => {
    let result = cliParse(['o*', '--', '--parallel', 'three', 'four'], {
        one: 'ls'
    });

    let expected = {
        name: 'run',
        cmd: 'ls --parallel three four',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object that contains arguments');

    t.end();
});

test('cli-parse: scripts arguments: parallel', (t) => {
    let result = cliParse(['o*', '--', '--parallel', 'three', 'four'], {
        one: 'ls',
        on: 'who'
    });

    let expected = {
        name: 'run',
        cmd: 'ls --parallel three four && who --parallel three four',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object that contains arguments');

    t.end();
});

test('cli-parse: --version', (t) => {
    let version = cliParse.version();
    let result = cliParse(['--version'], {
    });

    let expected = {
        name: 'version',
        output: version
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: -v', (t) => {
    let version = cliParse.version();
    let result = cliParse(['-v'], {
    });

    let expected = {
        name: 'version',
        output: version
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: --help', (t) => {
    let help = cliParse.help();
    let result = cliParse(['--help'], {
    });

    let expected = {
        name: 'help',
        output: help
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: -h', (t) => {
    let help = cliParse.help();
    let result = cliParse(['-h'], {
    });

    let expected = {
        name: 'help',
        output: help
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: unknown short argument', (t) => {
    let unknown = cliParse.unknown;
    let result = cliParse(['-w'], {
    });

    let expected = {
        name: 'unknown',
        output: unknown('w')
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: unknown long argument', (t) => {
    let unknown = cliParse.unknown;
    let result = cliParse(['--world'], {
    });

    let expected = {
        name: 'unknown',
        output: unknown('world')
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: script not found', (t) => {
    let scriptNotFound = cliParse.scriptNotFound;
    let result = cliParse(['hello'], {
    });

    let expected = {
        name: 'script-not-found',
        output: scriptNotFound({
            _: ['hello'],
            p: [],
            s: []
        })
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: deep script not found', (t) => {
    const result = cliParse(['docker'], {
        docker: 'redrun docker:pull:node docker:build docker:push',
        'docker:pull:node': 'echo "docker pull node"'
    });

    let expected = {
        calm: false,
        cmd: 'echo One of scripts not found: docker:pull:node docker:build docker:push',
        name: 'run',
        quiet: false
    };

    t.deepEqual(result, expected, 'should return object with name and output');

    t.end();
});

test('cli-parse: deep scripts are empty', (t) => {
    const result = cliParse(['docker'], {
        docker: 'redrun docker:pull:node docker:build docker:push',
        'docker:pull:node': 'echo "docker pull node"',
        'docker:build': '',
        'docker:push': ''
    });

    let expected = {
        calm: false,
        cmd: 'echo "docker pull node"',
        name: 'run',
        quiet: false
    };

    t.deepEqual(result, expected, 'should return object with name and output');

    t.end();
});
test('cli-parse: args: no scripts', (t) => {
    let fn = () => cliParse([]);
    t.throws(fn, /scripts should be object!/, 'should throw when no scripts');
    t.end();
});

test('cli-parse: args: no', (t) => {
    t.throws(cliParse, /argv should be an array!/, 'should throw when no args');
    t.end();
});

<commit_msg>chore(cli-parse) let -> const<commit_after>'use strict';

const os = require('os');
const test = require('tape');
const cliParse = require('../lib/cli-parse');

test('cli-parse: series', (t) => {
    const result = cliParse(['--series', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    const expected = {
        name: 'run',
        cmd: 'ls && pwd',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: parallel', (t) => {
    const result = cliParse(['--parallel', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    const expected = {
        name: 'run',
        cmd: 'ls & pwd',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: parallel --quiet', (t) => {
    const result = cliParse(['--parallel', 'one', 'two', '--quiet'], {
        one: 'ls',
        two: 'pwd'
    });

    const expected = {
        name: 'run',
        cmd: 'ls & pwd',
        quiet: true,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: parallel: before script', (t) => {
    const result = cliParse(['main', '--parallel', 'one', 'two'], {
        one: 'ls',
        two: 'pwd',
        main: 'echo hi'
    });

    const expected = {
        name: 'run',
        cmd: 'echo hi && ls & pwd',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: series and parallel', (t) => {
    const result = cliParse(['--s', 'one', 'two', '-p', 'three', 'four'], {
        one: 'ls',
        two: 'pwd',
        three: 'whoami',
        four: 'ps aux'
    });

    const expected = {
        name: 'run',
        cmd: 'ls && pwd && whoami & ps aux',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object');

    t.end();
});

test('cli-parse: series calm: linux', (t) => {
    const platform = os.platform;

    os.platform = () => 'linux';

    const result = cliParse(['--series-calm', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    const expected = {
        name: 'run',
        cmd: 'ls || true && pwd || true',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object with "true"');

    os.platfrom = platform;

    t.end();
});

test('cli-parse: parallel calm: windows', (t) => {
    const platform = os.platform;

    os.platform = () => 'win32';

    const result = cliParse(['--parallel-calm', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    const expected = {
        name: 'run',
        cmd: 'ls || (exit 0) & pwd || (exit 0)',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object with "exit 0"');

    os.platfrom = platform;

    t.end();
});

test('cli-parse: --calm: linux', (t) => {
    const platform = os.platform;

    os.platform = () => 'linux';

    const result = cliParse(['--calm', 'one', 'two'], {
        one: 'ls',
        two: 'pwd'
    });

    const expected = {
        name: 'run',
        cmd: 'ls || true && pwd || true',
        quiet: false,
        calm: true
    };

    t.deepEqual(result, expected, 'should build cmd object with "exit 0"');

    os.platfrom = platform;

    t.end();
});

test('cli-parse: scripts arguments', (t) => {
    const result = cliParse(['o*', '--', '--parallel', 'three', 'four'], {
        one: 'ls'
    });

    const expected = {
        name: 'run',
        cmd: 'ls --parallel three four',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object that contains arguments');

    t.end();
});

test('cli-parse: scripts arguments: parallel', (t) => {
    const result = cliParse(['o*', '--', '--parallel', 'three', 'four'], {
        one: 'ls',
        on: 'who'
    });

    const expected = {
        name: 'run',
        cmd: 'ls --parallel three four && who --parallel three four',
        quiet: false,
        calm: false
    };

    t.deepEqual(result, expected, 'should build cmd object that contains arguments');

    t.end();
});

test('cli-parse: --version', (t) => {
    const version = cliParse.version();
    const result = cliParse(['--version'], {
    });

    const expected = {
        name: 'version',
        output: version
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: -v', (t) => {
    const version = cliParse.version();
    const result = cliParse(['-v'], {
    });

    const expected = {
        name: 'version',
        output: version
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: --help', (t) => {
    const help = cliParse.help();
    const result = cliParse(['--help'], {
    });

    const expected = {
        name: 'help',
        output: help
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: -h', (t) => {
    const help = cliParse.help();
    const result = cliParse(['-h'], {
    });

    const expected = {
        name: 'help',
        output: help
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: unknown short argument', (t) => {
    const unknown = cliParse.unknown;
    const result = cliParse(['-w'], {
    });

    const expected = {
        name: 'unknown',
        output: unknown('w')
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: unknown long argument', (t) => {
    const unknown = cliParse.unknown;
    const result = cliParse(['--world'], {
    });

    const expected = {
        name: 'unknown',
        output: unknown('world')
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: script not found', (t) => {
    const scriptNotFound = cliParse.scriptNotFound;
    const result = cliParse(['hello'], {
    });

    const expected = {
        name: 'script-not-found',
        output: scriptNotFound({
            _: ['hello'],
            p: [],
            s: []
        })
    };

    t.deepEqual(result, expected, 'should return object with name, output and cmd');

    t.end();
});

test('cli-parse: deep script not found', (t) => {
    const result = cliParse(['docker'], {
        docker: 'redrun docker:pull:node docker:build docker:push',
        'docker:pull:node': 'echo "docker pull node"'
    });

    const expected = {
        calm: false,
        cmd: 'echo One of scripts not found: docker:pull:node docker:build docker:push',
        name: 'run',
        quiet: false
    };

    t.deepEqual(result, expected, 'should return object with name and output');

    t.end();
});

test('cli-parse: deep scripts are empty', (t) => {
    const result = cliParse(['docker'], {
        docker: 'redrun docker:pull:node docker:build docker:push',
        'docker:pull:node': 'echo "docker pull node"',
        'docker:build': '',
        'docker:push': ''
    });

    const expected = {
        calm: false,
        cmd: 'echo "docker pull node"',
        name: 'run',
        quiet: false
    };

    t.deepEqual(result, expected, 'should return object with name and output');

    t.end();
});
test('cli-parse: args: no scripts', (t) => {
    const fn = () => cliParse([]);
    t.throws(fn, /scripts should be object!/, 'should throw when no scripts');
    t.end();
});

test('cli-parse: args: no', (t) => {
    t.throws(cliParse, /argv should be an array!/, 'should throw when no args');
    t.end();
});

<|endoftext|>