123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- 'use strict';
- var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
- /**
- * Licensed Materials - Property of IBM
- * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2018, 2020
- * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- */
- define(['underscore', '../../app/util/GlassUtil', 'text!../schema/schemaDefs.json', 'text!../schema/schemaMappings.json', 'text!../schema/nameSchema.json', 'text!../schema/layoutSchema.json', 'text!../schema/layoutFragmentSchema.json', 'text!../schema/themeSchema.json', 'text!../schema/versionSchema.json', 'text!../schema/eventGroupsSchema.json', 'text!../schema/drillThroughSchema.json', 'text!../schema/propertiesSchema.json', 'text!../schema/pageContextSchema.json', 'text!../schema/widgetsSchema.json', 'text!../schema/widgetsFragmentSchema.json', 'text!../schema/widgetsTemplateSchema.json', 'jquery'], function (_, GlassUtil, SchemaDefs, SchemaMappings, NameSchema, LayoutSchema, LayoutFragmentSchema, ThemeSchema, VersionSchema, EventGroupsSchema, DrillThroughSchema, PropertiesSchema, PageContextSchema, WidgetsSchema, WidgetsFragmentSchema, WidgetsTemplateSchema, $) {
- var findObj = function findObj(ary, id) {
- return _.find(ary, function (ele) {
- return id === ele.id;
- });
- };
- var DashboardSpecHelper = function () {
- /**
- * Gets the spec content of a selected widget
- * @param {object} controller - the interaction controller (either a interaction/Controller or interaction/BaseController
- * from which the spec content is derived
- */
- function DashboardSpecHelper(controller) {
- _classCallCheck(this, DashboardSpecHelper);
- // TODO lifecycle_cleanup -- removethe controller dependency here
- this.controller = controller;
- this.dashboardApi = controller.dashboardApi;
- this.glassContext = controller.glassContext;
- this.logger = this.glassContext && this.glassContext.getCoreSvc ? this.glassContext.getCoreSvc('.Logger') : null;
- }
- /**
- * Gets the spec content of a widget
- * @param {boolean} pinning - indicates if the call was triggered from pinning. If so, get more content
- * @param {ContentAPI} content - ContentApi object to get the contentSpec from (Used through api when content is not from selection)
- * @returns returns the spec content for each selected node as an array element
- */
- DashboardSpecHelper.prototype.getContents = function getContents(pinning, content) {
- var _this = this;
- var specContents = [];
- var selectedContent = [];
- var internalAPI = this.dashboardApi.getFeature('internal');
- var boardModel = internalAPI.getBoardModel();
- var specVersion = boardModel.version;
- if (content) {
- selectedContent = [content];
- } else {
- selectedContent = this.dashboardApi.getCanvas().getSelectedContentList();
- }
- selectedContent.forEach(function (selectedContent) {
- var layoutModel = selectedContent.getFeature('Models.internal').getLayoutModel();
- var layoutWidgetIds = layoutModel.listWidgets([layoutModel.id]);
- var layoutWidgets = [];
- var nonMergedWidgets = [];
- var timelineEpisodes = [];
- var sourceIds = [];
- var errMsg = void 0;
- // ALERT! WARNING! DANGER! `widgetModel` is set in the loop below, and the last-instance of it is consumed
- // when setting specContent.drillThrough. Screwy javascript scoping rules for `var` types hid this,
- // but it's almost certianly a bug in some context?
- var widgetModel = void 0;
- for (var j = 0; j < layoutWidgetIds.length; j++) {
- var widgetContent = _this.dashboardApi.getCanvas().getContent(layoutWidgetIds[j]) || selectedContent.getContent(layoutWidgetIds[j]) || selectedContent;
- var internalModels = widgetContent.getFeature('Models.internal');
- widgetModel = internalModels.getWidgetModel();
- var mergedModel = _this._addFilterToWidgetModel(widgetModel);
- // Copy scenario, but don't copy filter widgets since filters are not supported
- if (!pinning) {
- if (widgetModel.type !== 'filter') {
- nonMergedWidgets.push(widgetModel);
- layoutWidgets.push(mergedModel);
- } else {
- errMsg = 'filterWidgetCopyWarn';
- }
- } else {
- layoutWidgets.push(mergedModel);
- }
- var timelineEpisodeForPinning = _this._getTimeLineEpisodeForPinning(boardModel, layoutWidgetIds[j]);
- if (timelineEpisodeForPinning) {
- timelineEpisodes.push(timelineEpisodeForPinning);
- }
- var viz = widgetContent.getFeature('Visualization');
- if (viz) {
- var dataSource = viz.getDataSource();
- var sourceId = dataSource ? dataSource.getId() : null;
- if (sourceId && sourceIds.indexOf(sourceId) === -1) {
- sourceIds.push(sourceId);
- }
- }
- }
- var dataSources = _this._getDataSources(sourceIds, boardModel.dataSources);
- var specContent = {
- specVersion: specVersion,
- layout: layoutModel,
- widgets: layoutWidgets,
- dataSources: dataSources,
- dashboardID: '',
- errMsg: errMsg,
- nodeCopied: true
- };
- if (errMsg) {
- specContent.nodeCopied = false;
- }
- // make this consistent with specContent.widgets
- specContent.nonMergedWidgets = nonMergedWidgets;
- if (selectedContent.getContainer()) {
- specContent.parentID = selectedContent.getContainer().getId();
- }
- if (boardModel.drillThrough && typeof boardModel.drillThrough.getModels === 'function') {
- var drillThroughService = _this.dashboardApi.getDashboardCoreSvc('DrillThroughService');
- // ALERT! WARNING! DANGER! (see above)
- specContent.drillThrough = drillThroughService.getDrillThroughByWidgetAndGroups(widgetModel);
- }
- if (boardModel.properties && boardModel.properties.customColors) {
- specContent.properties = {
- customColors: {
- colors: boardModel.properties.customColors.colors
- }
- };
- }
- if (boardModel.fredIsRed) {
- specContent.fredIsRed = boardModel.fredIsRed;
- }
- // TODO: instead of stripping out a known attribute, we should ignore anything runtime/transient attribute (eg. runtimeOnly flag) in the model.
- // When explicitScale is set to truthy, the VIDA normalization is not set; hence we does not want this property
- if (layoutModel.content && layoutModel.content.properties) {
- var contentProperties = Object.keys(layoutModel.content && layoutModel.content.properties);
- if (contentProperties.indexOf('explicitScale') !== -1) {
- delete specContent.layout.content.properties['explicitScale'];
- }
- }
- // Time Line model exists
- if (timelineEpisodes.length > 0) {
- specContent.episodes = timelineEpisodes;
- }
- // pageContext only needs to be added once
- if (specContents.length === 0) {
- var pageContext = _this._getPageContext(boardModel.pageContext);
- if (pageContext && pageContext.length) {
- specContent.pageContext = pageContext;
- // also add dashboard id, top level of the layout (needed to determine if pasting in the same DB)
- if (_this.controller.layoutController.topLayoutModel.id) {
- specContent.dashboardID = _this.controller.layoutController.topLayoutModel.id;
- }
- }
- }
- specContents.push(specContent);
- });
- return specContents;
- };
- /**
- * Gets all the filters applied on the widget
- * @param {Object} widgetModel - the widget associated inside the spec
- * @returns returns the widget obj adding the filters if any
- */
- DashboardSpecHelper.prototype._addFilterToWidgetModel = function _addFilterToWidgetModel(widgetModel) {
- var content = this.dashboardApi.getCanvas().getContent(widgetModel.id);
- var legacyVisualization = content && content.getFeature('Visualization.legacy');
- if (legacyVisualization) {
- var widgetModelClone = $.extend(true, {}, widgetModel);
- var portableFilters = legacyVisualization.getLegacyManagers().visFilterSupport.getAllFiltersAsLocalFiltersForPinning();
- if (portableFilters) {
- widgetModelClone.localFilters = portableFilters;
- delete widgetModelClone.filters;
- }
- return widgetModelClone;
- } else {
- return widgetModel;
- }
- };
- /**
- * Gets the spec content of a widget as JSON, it assumes that no pinning content is needed
- * @param {ContentAPI} content (Optional - if undefined, the selected content list will be used instead)
- * @returns {Object} Returns an object which contains the spec content represented as JSON, use property `spec` to access the spec JSON string
- */
- DashboardSpecHelper.prototype.getContentsToJSONSpec = function getContentsToJSONSpec(content) {
- var JSONcontent = {};
- var JSONSpec = void 0;
- var specContents = this.getContents(false, content);
- var assets = _.countBy(specContents, function (content) {
- return content.nodeCopied ? 'copied' : 'notCopied';
- });
- specContents = this._removeErrorsFromSpec(specContents, JSONcontent);
- if (specContents.length) {
- if (specContents[0].specVersion) {
- JSONcontent.specVersion = specContents[0].specVersion;
- }
- JSONcontent.layout = _.map(specContents, function (content) {
- return content.layout.toJSON();
- });
- JSONcontent.parentID = _.map(specContents, function (content) {
- return content.parentID;
- });
- JSONcontent.pageContext = _.map(specContents[0].pageContext, function (content) {
- return content.toJSON();
- });
- JSONcontent.dashboardID = specContents[0].dashboardID;
- JSONcontent.widgets = _.chain(specContents).map(function (content) {
- return content.widgets;
- }).flatten().map(function (widget) {
- return widget.toJSON();
- }).value();
- JSONcontent.nonMergedWidgets = _.chain(specContents).map(function (content) {
- return content.nonMergedWidgets;
- }).flatten().map(function (widget) {
- return widget.toJSON();
- }).value();
- JSONcontent.dataSources = {};
- JSONcontent.dataSources.version = specContents[0].dataSources.version;
- JSONcontent.dataSources.sources = [];
- var resultDrillThroughDefinitions = [];
- _.each(specContents, function (content) {
- _.each(content.dataSources.sources, function (source) {
- var idx = -1;
- // if the dataSource is already there, don't add it again
- if (JSONcontent.dataSources.sources.length > 0) {
- idx = JSONcontent.dataSources.sources.findIndex(function (ele) {
- return ele.id === source.id;
- });
- }
- if (idx < 0) {
- var sourceJSON = source.toJSON();
- // if the datasource is saved as an embedded module, the embedded module is saved
- // as an attachment to the original dashboard object in CM. Sharing the embedded module
- // can lead to side effects which was not intended from the original dashboard, which
- // leads to a stale datasource.
- // Remove the embeddedModuleId so that the target dashboard does not share the embedded module
- // with the source dashboard, but rather builds its own embedded module.
- if (sourceJSON && sourceJSON.shaping && sourceJSON.shaping.embeddedModuleId) {
- delete sourceJSON.shaping.embeddedModuleId;
- delete sourceJSON.shaping.embeddedModuleName;
- sourceJSON.shaping.embeddedModuleUpToDate = false;
- }
- JSONcontent.dataSources.sources.push(sourceJSON);
- }
- });
- // Add drill through definitions from selected widget without duplicates.
- if (content.drillThrough && content.drillThrough.length) {
- content.drillThrough.forEach(function (drillDefinition) {
- var definitionExists = _.find(resultDrillThroughDefinitions, function (resultDefinition) {
- return resultDefinition.id === drillDefinition.id;
- });
- if (!definitionExists) {
- resultDrillThroughDefinitions.push(drillDefinition.toJSON());
- }
- });
- }
- });
- if (specContents[0].properties) {
- JSONcontent.properties = specContents[0].properties;
- }
- if (specContents[0].fredIsRed) {
- JSONcontent.fredIsRed = specContents[0].fredIsRed;
- }
- if (resultDrillThroughDefinitions.length) {
- JSONcontent.drillThrough = resultDrillThroughDefinitions;
- }
- var episodes = _.chain(specContents).map(function (content) {
- return content.episodes;
- }).flatten().filter(Boolean).map(function (episode) {
- return episode.toJSON();
- }).value();
- if (episodes.length) {
- JSONcontent.episodes = episodes;
- }
- }
- if (JSONcontent) {
- JSONSpec = JSON.stringify(JSONcontent, null, 4);
- }
- return {
- spec: JSONSpec,
- count: assets.copied,
- errMsg: JSONcontent.errMsg
- };
- };
- /**
- * Validates a dashboard JSON spec
- * @param {string} spec - spec to validate
- * @param {array} retErrors - (optional), if present, all errors are added to it and not shown in the Dashboard.
- * The object is {schema,schemaCondition,schemaKeyword,schemaDataPath,data,msg};
- * @returns returns the JSON objects of the valid spec, otherwise null if validation fails
- */
- DashboardSpecHelper.prototype.validateDashboardSpec = function validateDashboardSpec(spec, retErrors) {
- var _this2 = this;
- return Promise.resolve().then(function () {
- var defaultSchema = 'noSchema';
- var defaultSchemaCondition = 'noSchemaCondition';
- var defaultSchemaKeyword = 'noSchemaKeyword';
- var defaultSchemaDataPath = 'noSchemaDataPath';
- var defaultData = 'noData';
- var defaultMsg = 'noMsg';
- var errorObj = {
- 'schema': defaultSchema,
- 'schemaCondition': defaultSchemaCondition,
- 'schemaKeyword': defaultSchemaKeyword,
- 'schemaDataPath': defaultSchemaDataPath,
- 'data': defaultData,
- 'msg': defaultMsg
- };
- var outSpec = void 0;
- try {
- outSpec = JSON.parse(spec);
- } catch (err) {
- var msg = 'Invalid JSON provided';
- if (_this2.logger) {
- _this2.logger.error(msg, err, _this2);
- }
- if (retErrors !== undefined && retErrors !== null && _.isArray(retErrors)) {
- var errObj = Object.create(errorObj);
- errObj.data = err;
- errObj.msg = msg;
- retErrors.push(errObj);
- return null;
- } else {
- throw new Error('invalidJSONResponse');
- }
- }
- // finds a widget
- function findWidget(id, type, widgetIds) {
- if (type === 'widget') {
- var widget = findObj(outSpec.widgets, id);
- if (!widget) {
- // if widget can not be found, save layout id
- widgetIds.push(id);
- }
- }
- }
- // recursively finds widgets
- function findWidgets(items, widgetIds) {
- _.each(items, function (item) {
- return item.items ? findWidgets(item.items, widgetIds) : findWidget(item.id, item.type, widgetIds);
- });
- }
- var errors = [];
- // check for empty spec
- var specFragment = false;
- if (!outSpec || Object.keys(outSpec).length === undefined || Object.keys(outSpec).length === 0) {
- // signal error
- var _msg = 'Spec is empty';
- if (retErrors !== undefined && retErrors !== null && _.isArray(retErrors)) {
- var _errObj = Object.create(errorObj);
- _errObj.msg = _msg;
- retErrors.push(_errObj);
- return null;
- } else {
- throw new Error(_msg);
- }
- } else if (Array.isArray(outSpec.layout)) {
- // spec fragment since the only other choice is an array
- specFragment = true;
- }
- // if in production, do not validate the spec against schemas
- if (!GlassUtil.isDevMode(_this2.glassContext)) {
- return outSpec;
- }
- return _this2._getAjv().then(function (ajv) {
- // use key/value pair to determine which schema to use with a spec key
- var shmaMappings = JSON.parse(SchemaMappings);
- // validate all of the spec, accumulate errors if any
- Object.keys(outSpec).forEach(function (specKey) {
- var keySchema = shmaMappings[specKey];
- if (specFragment && specKey === 'layout') {
- keySchema = shmaMappings['layoutFragment'];
- }
- if (specFragment && specKey === 'widgets') {
- keySchema = shmaMappings['widgetsFragment'];
- }
- var specValue = outSpec[specKey];
- if (keySchema) {
- var validate = ajv.getSchema(keySchema);
- if (validate) {
- if (!validate(specValue)) {
- // each call to validate destroys any previous errors so much save them
- validate.errors.forEach(function (error) {
- var errObj = Object.create(errorObj);
- errObj.schema = error.schemaPath;
- errObj.schemaCondition = error.parentSchema;
- errObj.schemaKeyword = error.keyword;
- errObj.schemaDataPath = error.dataPath;
- errObj.data = error.data;
- errObj.msg = error.message;
- errors.push(errObj);
- });
- }
- }
- }
- // NOTE: for now leave this commented out since we don't have schemas for all spec keys
- // else {
- // generate error, invalid key
- // errors.push( {
- // 'msg': 'Spec Key: ' + specKey + ' does not exists in schema mapping definition'
- // });
- // }
- });
- // semantic checks
- // 1. check widget id in layout to make sure there is a widget in the spec for it. If any widget ids are returned,
- // then there is no widget object for it
- var widgetIds = [];
- if (outSpec.widgets) {
- if (outSpec.layout.items) {
- findWidgets(outSpec.layout.items, widgetIds);
- } else {
- findWidgets(outSpec.layout, widgetIds);
- }
- if (widgetIds.length) {
- var _errObj2 = Object.create(errorObj);
- _errObj2.schema = defaultSchema;
- _errObj2.schemaDataPath = defaultSchemaDataPath;
- _errObj2.schemaKeyword = defaultSchemaKeyword;
- _errObj2.data = defaultData;
- _errObj2.schemaCondition = 'semantic layout widget id check';
- _errObj2.msg = 'No widget objects in the specification for layout ids ' + widgetIds.toString();
- errors.push(_errObj2);
- }
- }
- if (errors.length) {
- outSpec = null;
- if (retErrors !== undefined && retErrors !== null && _.isArray(retErrors)) {
- retErrors.push(errors);
- } else {
- // log all errors
- if (_this2.logger) {
- _.each(errors, function (err) {
- var msg = void 0;
- if (err.msg !== defaultMsg) {
- msg = ' error: ' + err.msg;
- }
- if (err.data !== defaultData) {
- msg += ' data: ' + err.data;
- }
- if (err.schema !== defaultSchema) {
- msg += ' schema: ' + err.schema;
- }
- if (err.schemaCondition !== defaultSchemaCondition) {
- msg += ' schema defn: ' + err.schemaCondition.$id;
- }
- _this2.logger.error('schema validation error:', msg, _this2);
- });
- }
- throw new Error('schema validation errors logged above');
- }
- }
- return outSpec;
- });
- });
- };
- DashboardSpecHelper.prototype._getAjv = function _getAjv() {
- var _this3 = this;
- if (this.ajv) {
- return Promise.resolve(this.ajv);
- } else {
- return new Promise(function (resolve, reject) {
- require(['ajv'], function (Ajv) {
- try {
- var shmaDefs = JSON.parse(SchemaDefs);
- // let shma = JSON.parse(Schema);
- var nameShma = JSON.parse(NameSchema);
- var layoutSchema = _this3._getLayoutSchema();
- var layoutFragmentShma = JSON.parse(LayoutFragmentSchema);
- var themeShma = JSON.parse(ThemeSchema);
- var versionShma = JSON.parse(VersionSchema);
- var evtGrpsShma = JSON.parse(EventGroupsSchema);
- var drillThroughShma = JSON.parse(DrillThroughSchema);
- var propertiesShma = JSON.parse(PropertiesSchema);
- var pageContextShma = JSON.parse(PageContextSchema);
- var widgetsShma = JSON.parse(WidgetsSchema);
- var widgetsFragmentShma = JSON.parse(WidgetsFragmentSchema);
- var widgetsTemplateSchema = JSON.parse(WidgetsTemplateSchema);
- _this3.ajv = new Ajv({ allErrors: true, verbose: true, schemas: [shmaDefs, nameShma, layoutSchema, layoutFragmentShma, themeShma, versionShma, evtGrpsShma, drillThroughShma, propertiesShma, pageContextShma, widgetsShma, widgetsFragmentShma, widgetsTemplateSchema] });
- resolve(_this3.ajv);
- } catch (err) {
- reject(err);
- }
- }, reject);
- });
- }
- };
- /**
- * get layout schema, it will add the registered types to the layoutType.enum
- * @returns the layout schema
- */
- DashboardSpecHelper.prototype._getLayoutSchema = function _getLayoutSchema() {
- var layoutSchema = JSON.parse(LayoutSchema);
- var contentTypeRegistry = this.dashboardApi.getFeature('ContentTypeRegistry');
- var layoutTypeEnum = layoutSchema.definitions.layoutType.enum;
- layoutSchema.definitions.layoutType.enum = layoutTypeEnum.concat(contentTypeRegistry.getRegisteredTypes());
- return layoutSchema;
- };
- /**
- * Creates an datasource spec object
- * @param {array} sourceIds - array of data source ids
- * @param {object} dataSources - array of datasources
- * @returns returns an object containing the datasource version and source objects
- */
- DashboardSpecHelper.prototype._getDataSources = function _getDataSources(sourceIds, dataSources) {
- var copiedInfo = {
- version: '0.0',
- sources: []
- };
- if (dataSources && (typeof dataSources === 'undefined' ? 'undefined' : _typeof(dataSources)) === 'object') {
- copiedInfo.version = dataSources.version;
- var sources = copiedInfo.sources;
- sourceIds.forEach(function (id) {
- sources.push(dataSources.get('sources').get(id));
- });
- }
- return copiedInfo;
- };
- /**
- * @param boardModel the boardModel
- * @param layoutWidgetId The layout id for the widget being processed.
- * @returns the timelineEpisode for widget which is attached to the pin for this layoutWidgetId. or null if there is none.
- * The model is returned from the board spec.
- *
- */
- DashboardSpecHelper.prototype._getTimeLineEpisodeForPinning = function _getTimeLineEpisodeForPinning(boardModel, layoutWidgetId) {
- if (boardModel.timeline && typeof this.controller.boardModel.timeline.get === 'function') {
- return boardModel.timeline.episodes.get(layoutWidgetId);
- }
- return null;
- };
- /**
- * Get current tab and global pageContexts
- * @param {array} pageContext - pageContext objects
- */
- DashboardSpecHelper.prototype._getPageContext = function _getPageContext(pageContext) {
- if (pageContext && pageContext.getModels().length) {
- var pageCxt = [];
- var tabId = this.controller.layoutController.getCurrentSubViewId();
- _.each(pageContext.getModels(), function (pc) {
- if (pc.getScope() === tabId || pc.getScope() === 'global') {
- pageCxt.push(pc);
- }
- });
- return pageCxt;
- }
- };
- /**
- * checks spec contents object for filter widget (denoted by errMsg being defined) and removes that element from the array
- * @param {array} specContents - content objects
- * @param {object} jsonContent - content to be converted to json object
- */
- DashboardSpecHelper.prototype._removeErrorsFromSpec = function _removeErrorsFromSpec(specContents, jsonContent) {
- jsonContent.errMsg = _.first(_.compact(_.map(specContents, function (content) {
- return content.errMsg;
- })));
- if (jsonContent.errMsg) {
- // keep the widget(s) that are not in error
- return _.filter(specContents, function (content) {
- return content.errMsg === undefined;
- });
- }
- return specContents;
- };
- return DashboardSpecHelper;
- }();
- return DashboardSpecHelper;
- });
- //# sourceMappingURL=DashboardSpecHelper.js.map
|