Skip to content

Commit

Permalink
New: Asset Search Provider Service & Component (#683)
Browse files Browse the repository at this point in the history
* New: Added assets search provider and display component

* Update: Gold bar

* Update: Private display component name

* Update: Add comments to tests

* Update: Refactored tests

* Update: Addressing comments

* Update packages/core/addon/mirage/utils/rsql-utils.js

Co-Authored-By: Kevin Hinterlong <kevinhinterlong@users.noreply.github.com>

* Update packages/core/addon/mirage/utils/rsql-utils.js

Co-Authored-By: Kevin Hinterlong <kevinhinterlong@users.noreply.github.com>

* Update packages/core/app/mirage/routes/dashboard.js

Co-Authored-By: Kevin Hinterlong <kevinhinterlong@users.noreply.github.com>

* Update packages/core/app/mirage/routes/report.js

Co-Authored-By: Kevin Hinterlong <kevinhinterlong@users.noreply.github.com>

* Update packages/search/addon/components/navi-search-result/asset.hbs

Co-Authored-By: Kevin Hinterlong <kevinhinterlong@users.noreply.github.com>

* Update packages/search/addon/services/navi-search/navi-asset-search-provider.js

Co-Authored-By: Kevin Hinterlong <kevinhinterlong@users.noreply.github.com>

* Update: Addressing comments

* Update: Addressing comments

* Update: Addressing comments

* Update: Typo

* Update: Addressing comments

* Update: Add package lock

* Update: Parse route in component

* Update: Added comments

* Update: Addressing comments

* Update bard.js

* Update bard-dimensions.js

* Update: Addressed comments

* Update: Addressed comments

* Update: Addressing comments

Co-authored-by: Kevin Hinterlong <kevinhinterlong@users.noreply.github.com>
  • Loading branch information
lydakis and kevinhinterlong committed Mar 5, 2020
1 parent 99ffd4a commit 3744eed
Show file tree
Hide file tree
Showing 18 changed files with 44,395 additions and 729 deletions.
2 changes: 1 addition & 1 deletion packages/core/addon/mirage/fixtures/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default [
},
{
id: 4,
title: 'Dashboard 4',
title: 'Revenue Dashboard',
authorId: 'ciela',
dashboardWidgetIds: [6],
createdOn: '2016-01-01 00:00:00',
Expand Down
2 changes: 1 addition & 1 deletion packages/core/addon/mirage/fixtures/reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ export default [
title: 'Revenue report 1',
createdOn: '2015-01-01 00:00:00',
updatedOn: '2015-01-01 00:00:00',
authorId: 'navi_user',
authorId: 'ciela',
deliveryRuleIds: [],
visualization: {
type: 'table',
Expand Down
77 changes: 77 additions & 0 deletions packages/core/addon/mirage/utils/rsql-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Copyright 2020, Yahoo Holdings Inc.
* Licensed under the terms of the MIT license. See accompanying LICENSE.md file for terms.
*
* Utilities for parsing RSQL.
*/

import Response from 'ember-cli-mirage/response';

/**
* @method getFilterParams – Parse filter parameters in the form of "(title==*H*,request==*Revenue*)";author==*ramvish*
* to a list of all the OR parameters, ie., [H, Revenue]
* @param {String} queryFilter
* @returns {Array} Filter parameters
*/
export function getFilterParams(queryFilter) {
if (queryFilter && queryFilter.includes('author') && queryFilter.includes(';')) {
queryFilter = queryFilter.split(';')[0];
return queryFilter
.replace(/[()*]/g, '')
.split(',')
.map(el => el.split('=='));
}
return null;
}

/**
* @method getQueryAuthor – Parse filter parameters in the form of "(title==*H*,request==*Revenue*)";author==*ramvish*
* to get the author, ie., ramvish
* @param {String} queryFilter
* @returns Author
*/
export function getQueryAuthor(queryFilter) {
if (queryFilter && queryFilter.includes('author')) {
if (queryFilter.includes('(')) {
queryFilter = queryFilter.split(';')[1];
}
return queryFilter.split('==')[1];
}
return null;
}

/**
* @method filterModel – Filter a model's entries (report, dashboard) based on a query filter
* @param {Object} model
* @param {String} queryFilter
* @returns {Object} modelObject
*/
export function filterModel(model, queryFilter) {
let modelObject;
try {
let filterParameters = getFilterParams(queryFilter);
let author = getQueryAuthor(queryFilter);
if (filterParameters == null && author == null) {
throw new Error('No search parameters');
}
modelObject = model.where(report => {
// Author can be optional, ie., not included in the query, but filterparameters are always included.
const matchesFilterParameterIfExists = filterParameters
? filterParameters.some(filterParameter =>
JSON.stringify(report[filterParameter[0]]).match(new RegExp(filterParameter[1], 'i'))
)
: false;
const matchesAuthorIfExists = author ? report.authorId.match(new RegExp(author, 'i')) : true;
return matchesFilterParameterIfExists && matchesAuthorIfExists;
});
} catch (error) {
modelObject = new Response(
400,
{ data: {} },
{
errors: ['InvalidPredicateException: Invalid filter format']
}
);
}
return modelObject;
}
11 changes: 8 additions & 3 deletions packages/core/app/mirage/routes/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Response from 'ember-cli-mirage/response';
import moment from 'moment';
import RESPONSE_CODES from '../enums/response-codes';
import { filterModel } from 'navi-core/mirage/utils/rsql-utils';

const TIMESTAMP_FORMAT = 'YYYY-MM-DD HH:mm:ss';

Expand Down Expand Up @@ -43,17 +44,21 @@ export default function() {
});

this.get('/dashboards', ({ dashboards }, request) => {
let dashboardObject;
let idFilter = request.queryParams['filter[dashboards.id]'];
let queryFilter = request.queryParams['filter[dashboards]'];

// Allow filtering
if (idFilter) {
let ids = idFilter.split(',');
dashboards = dashboards.find(ids);
dashboardObject = dashboards.find(ids);
} else if (queryFilter) {
dashboardObject = filterModel(dashboards, queryFilter);
} else {
dashboards = dashboards.all();
dashboardObject = dashboards.all();
}

return dashboards;
return dashboardObject;
});

this.post('/dashboards', function({ dashboards, users }) {
Expand Down
17 changes: 11 additions & 6 deletions packages/core/app/mirage/routes/report.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import Mirage from 'ember-cli-mirage';
import Response from 'ember-cli-mirage/response';
import moment from 'moment';
import RESPONSE_CODES from '../enums/response-codes';
import { filterModel } from 'navi-core/mirage/utils/rsql-utils';
const TIMESTAMP_FORMAT = 'YYYY-MM-DD HH:mm:ss';

export default function() {
/**
* reports/ - GET endpoint to fetch many reports
*/
this.get('/reports', function({ reports }, request) {
let reportObject;
let idFilter = request.queryParams['filter[reports.id]'];
let queryFilter = request.queryParams['filter[reports]'];

// Allow filtering
if (idFilter) {
let ids = idFilter.split(',');
reports = reports.find(ids);
reportObject = reports.find(ids);
} else if (queryFilter) {
reportObject = filterModel(reports, queryFilter);
} else {
reports = reports.all();
reportObject = reports.all();
}

return reports;
return reportObject;
});

/**
Expand Down Expand Up @@ -56,7 +61,7 @@ export default function() {
user = users.find(report.authorId);

if (!report) {
return new Mirage.Response(RESPONSE_CODES.NOT_FOUND, {}, { errors: [`Unknown identifier '${id}'`] });
return new Response(RESPONSE_CODES.NOT_FOUND, {}, { errors: [`Unknown identifier '${id}'`] });
}

// Delete report from user
Expand All @@ -65,6 +70,6 @@ export default function() {
});
report.destroy();

return new Mirage.Response(RESPONSE_CODES.NO_CONTENT);
return new Response(RESPONSE_CODES.NO_CONTENT);
});
}
10 changes: 10 additions & 0 deletions packages/search/addon/components/navi-search-result/asset.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{{! Copyright 2020, Yahoo Holdings Inc. Licensed under the terms of the MIT license. See accompanying LICENSE.md file for terms. }}
<ul class='navi-search-results' ...attributes>
{{#each this.results as |result|}}
<li class='navi-search-result__asset'>
<LinkTo @route={{result.route}} @model={{result.modelId}}>
{{result.title}}
</LinkTo>
</li>
{{/each}}
</ul>
33 changes: 33 additions & 0 deletions packages/search/addon/components/navi-search-result/asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright 2020, Yahoo Holdings Inc.
* Licensed under the terms of the MIT license. See accompanying LICENSE.md file for terms.
*
* A component that displays results for reports and dashboards
*/

import Component from '@glimmer/component';
import { pluralize } from 'ember-inflector';

export default class NaviAssetSearchResultComponent extends Component {
/**
* @property {Array} results
*/
get results() {
return this.args?.data?.map(value => {
value.route = this._getRouteFor(value);
return value;
});
}

/**
* @method _extractRoute – Extracts the route name of a given asset (report or dashboard)
* @private
* @param {Object} asset
* @returns {String} Route
*/
_getRouteFor(asset) {
const type = asset?.constructor?.modelName,
pluralType = pluralize(type);
return `${pluralType}.${type}`;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Copyright 2020, Yahoo Holdings Inc.
* Licensed under the terms of the MIT license. See accompanying LICENSE.md file for terms.
*
* This service is used to search for reports and dashboards stored in the persistence layer.
*/

import { inject as service } from '@ember/service';
import NaviBaseSearchProviderService from '../navi-base-search-provider';
import { keepLatestTask } from 'ember-concurrency-decorators';
import { pluralize } from 'ember-inflector';

export default class NaviAssetSearchProviderService extends NaviBaseSearchProviderService {
/**
* @property {Ember.Service} store
*/
@service store;

/**
* @property {Ember.Service} user
*/
@service user;

/**
* @property {String} _displayComponentName
* @private
*/
_displayComponentName = 'navi-search-result/asset';

/**
* @method _parseParamsFilterString – Parses string query to search parameters
* @private
* @param {String} query
* @param {String} type
* @returns {Object} query object
*/
_parseParamsFilterString(query, type) {
let paramsFilterString = '';
if (typeof query == 'string' && query) {
if (type === 'report') {
paramsFilterString = `(title==*${query}*,request==*${query}*)`;
} else if (type === 'dashboard') {
paramsFilterString = `(title==*${query}*)`;
}
}
return paramsFilterString;
}

/**
* @method _constructSearchQuery – Constructs the query filter parameters adhering to the RSQL standard
* The query is built to return results from the specified author. The user's query is matched against multiple fields,
* therefore an 'or' is used between the different parameters and finally there's an 'and' with the author.
* @private
* @param {String} userQuery
* @param {String} type
* @returns {Object} search query object
*/
_constructSearchQuery(userQuery, type) {
const author = this.user.getUser().id;
const pluralType = pluralize(type);
let query = { filter: { [pluralType]: '' } };

const paramsFilterString = this._parseParamsFilterString(userQuery, type);
const authorFilterString = author ? (paramsFilterString ? `;author==${author}` : `author==${author}`) : '';

query.filter[pluralType] = `${paramsFilterString}${authorFilterString}`;

return query;
}

/**
* @method search – Searches for reports and dashboards in the persistence layer
* @override
* @param {String} query
* @yields {Promise} promise with search query results
* @returns {Object} Object containing component, title, and data to be displayed
*/
@keepLatestTask
*search(query) {
const types = ['report', 'dashboard'];
const promises = [];

types.forEach(type => {
promises.push(this.store.query(type, this._constructSearchQuery(query, type)));
});

const data = yield Promise.all(promises).then(function(values) {
return values.flatMap(value => value.toArray());
});
return {
component: this._displayComponentName,
title: 'Reports & Dashboards',
data
};
}
}
1 change: 1 addition & 0 deletions packages/search/app/components/navi-search-result/asset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'navi-search/components/navi-search-result/asset';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'navi-search/services/navi-search/navi-asset-search-provider';

0 comments on commit 3744eed

Please sign in to comment.