Commit bc81298d by Torkel Ödegaard

worked on search

parent 93d21bec
......@@ -63,6 +63,7 @@ type FindPersistedDashboardsQuery struct {
FolderIds []int64
Tags []string
Limit int
IsBrowse bool
Result HitList
}
......@@ -151,7 +151,7 @@ export class SearchCtrl {
}
toggleFolder(section) {
this.searchSrv.toggleFolder(section);
this.searchSrv.toggleSection(section);
}
}
......
......@@ -2,7 +2,7 @@ import store from 'app/core/store';
import _ from 'lodash';
import config from 'app/core/config';
export class ImpressionsStore {
export class ImpressionSrv {
constructor() {}
addDashboardImpression(dashboardId) {
......@@ -44,8 +44,5 @@ export class ImpressionsStore {
}
}
var impressions = new ImpressionsStore();
export {
impressions
};
const impressionSrv = new ImpressionSrv();
export default impressionSrv;
import _ from 'lodash';
import coreModule from 'app/core/core_module';
import impressionSrv from 'app/core/services/impression_srv';
import store from 'app/core/store';
export class SearchSrv {
recentIsOpen: boolean;
starredIsOpen: boolean;
/** @ngInject */
constructor(private backendSrv) {
constructor(private backendSrv, private $q) {
this.recentIsOpen = store.getBool('search.sections.recent', true);
this.starredIsOpen = store.getBool('search.sections.starred', true);
}
browse() {
private getRecentDashboards(sections) {
return this.queryForRecentDashboards().then(result => {
if (result.length > 0) {
sections['recent'] = {
title: 'Recent Boards',
icon: 'fa fa-clock-o',
score: -1,
expanded: this.recentIsOpen,
toggle: this.toggleRecent.bind(this),
items: result,
};
}
});
}
private queryForRecentDashboards() {
var dashIds = _.take(impressionSrv.getDashboardOpened(), 5);
if (dashIds.length === 0) {
return Promise.resolve([]);
}
return this.backendSrv.search({ dashboardIds: dashIds }).then(result => {
return dashIds.map(orderId => {
return this.transformToViewModel(_.find(result, { id: orderId }));
}).filter(item => !item.isStarred);
});
}
private toggleRecent(section) {
this.recentIsOpen = section.expanded = !section.expanded;
store.set('search.sections.recent', this.recentIsOpen);
if (!section.expanded || section.items.length) {
return;
}
return this.queryForRecentDashboards().then(result => {
section.items = result;
});
}
private toggleStarred(section) {
this.starredIsOpen = section.expanded = !section.expanded;
store.set('search.sections.starred', this.starredIsOpen);
}
private getStarred(sections) {
return this.backendSrv.search({starred: true, limit: 5}).then(result => {
if (result.length > 0) {
sections['starred'] = {
title: 'Starred Boards',
icon: 'fa fa-star-o',
score: -2,
expanded: this.starredIsOpen,
toggle: this.toggleStarred.bind(this),
items: this.transformToViewModel(result),
};
}
});
}
private getDashboardsAndFolders(sections) {
const rootFolderId = 0;
let query = {
folderIds: [rootFolderId]
folderIds: [rootFolderId],
};
return this.backendSrv.search(query).then(results => {
let sections: any = {};
for (let hit of results) {
if (hit.type === 'dash-folder') {
sections[hit.id] = {
......@@ -26,7 +90,8 @@ export class SearchSrv {
items: [],
icon: 'fa fa-folder',
score: _.keys(sections).length,
uri: hit.uri
uri: hit.uri,
toggle: this.toggleFolder.bind(this),
};
}
}
......@@ -37,7 +102,7 @@ export class SearchSrv {
items: [],
icon: 'fa fa-folder-open',
score: _.keys(sections).length,
expanded: true
expanded: true,
};
for (let hit of results) {
......@@ -45,18 +110,36 @@ export class SearchSrv {
continue;
}
let section = sections[hit.folderId || 0];
hit.url = 'dashboard/' + hit.uri;
section.items.push(hit);
if (section) {
section.items.push(this.transformToViewModel(hit));
} else {
console.log('Error: dashboard returned from browse search but not folder', hit.id, hit.folderId);
}
}
});
}
private browse() {
let sections: any = {};
let promises = [
this.getRecentDashboards(sections),
this.getStarred(sections),
this.getDashboardsAndFolders(sections),
];
return this.$q.all(promises).then(() => {
return _.sortBy(_.values(sections), 'score');
});
}
private transformToViewModel(hit) {
hit.url = 'dashboard/' + hit.uri;
return hit;
}
search(options) {
if (!options.query &&
(!options.tag || options.tag.length === 0) &&
!options.starred) {
if (!options.query && (!options.tag || options.tag.length === 0) && !options.starred) {
return this.browse();
}
......@@ -65,7 +148,6 @@ export class SearchSrv {
query.type = 'dash-db';
return this.backendSrv.search(query).then(results => {
let section = {
hideHeader: true,
items: [],
......@@ -76,15 +158,14 @@ export class SearchSrv {
if (hit.type === 'dash-folder') {
continue;
}
hit.url = 'dashboard/' + hit.uri;
section.items.push(hit);
section.items.push(this.transformToViewModel(hit));
}
return [section];
});
}
toggleFolder(section) {
private toggleFolder(section) {
section.expanded = !section.expanded;
section.icon = section.expanded ? 'fa fa-folder-open' : 'fa fa-folder';
......@@ -93,17 +174,18 @@ export class SearchSrv {
}
let query = {
folderIds: [section.id]
folderIds: [section.id],
};
return this.backendSrv.search(query).then(results => {
for (let hit of results) {
hit.url = 'dashboard/' + hit.uri;
section.items.push(hit);
}
section.items = _.map(results, this.transformToViewModel);
});
}
toggleSection(section) {
section.toggle(section);
}
getDashboardTags() {
return this.backendSrv.get('/api/dashboards/tags');
}
......
import { SearchSrv } from 'app/core/services/search_srv';
import { BackendSrvMock } from 'test/mocks/backend_srv';
import impressionSrv from 'app/core/services/impression_srv';
jest.mock('app/core/store', () => {
return {
getBool: jest.fn(),
set: jest.fn(),
};
});
jest.mock('app/core/services/impression_srv', () => {
return {
getDashboardOpened: jest.fn,
};
});
describe('SearchSrv', () => {
let searchSrv, backendSrvMock;
beforeEach(() => {
backendSrvMock = new BackendSrvMock();
searchSrv = new SearchSrv(backendSrvMock);
searchSrv = new SearchSrv(backendSrvMock, Promise);
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([]);
});
describe('With recent dashboards', () => {
let results;
beforeEach(() => {
backendSrvMock.search = jest
.fn()
.mockReturnValueOnce(
Promise.resolve([{ id: 2, title: 'second but first' }, { id: 1, title: 'first but second' }]),
)
.mockReturnValue(Promise.resolve([]));
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([1, 2]);
return searchSrv.search({ query: '' }).then(res => {
results = res;
});
});
it('should include recent dashboards section', () => {
expect(results[0].title).toBe('Recent Boards');
});
it('should return order decided by impressions store not api', () => {
expect(results[0].items[0].title).toBe('first but second');
expect(results[0].items[1].title).toBe('second but first');
});
});
describe('With starred dashboards', () => {
let results;
beforeEach(() => {
backendSrvMock.search = jest
.fn()
.mockReturnValue(Promise.resolve([
{id: 1, title: 'starred'}
]));
return searchSrv.search({ query: '' }).then(res => {
results = res;
});
});
it('should include starred dashboards section', () => {
expect(results[0].title).toBe('Starred Boards');
expect(results[0].items.length).toBe(1);
});
});
describe("with no query string and dashboards with folders returned", () => {
describe('With starred dashboards and recent', () => {
let results;
beforeEach(() => {
backendSrvMock.search = jest.fn().mockReturnValue(Promise.resolve([
{
title: 'folder1',
type: 'dash-folder',
id: 1,
},
{
title: 'dash with no folder',
type: 'dash-db',
id: 2,
},
{
title: 'dash in folder1 1',
type: 'dash-db',
id: 3,
folderId: 1
},
{
title: 'dash in folder1 2',
type: 'dash-db',
id: 4,
folderId: 1
},
]));
return searchSrv.search({query: ''}).then(res => {
backendSrvMock.search = jest
.fn()
.mockReturnValueOnce(Promise.resolve([
{id: 1, title: 'starred and recent', isStarred: true},
{id: 2, title: 'recent'}
]))
.mockReturnValue(Promise.resolve([
{id: 1, title: 'starred and recent'}
]));
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([1,2]);
return searchSrv.search({ query: '' }).then(res => {
results = res;
});
});
it("should create sections for each folder and root", () => {
it('should not show starred in recent', () => {
expect(results[1].title).toBe('Recent Boards');
expect(results[1].items[0].title).toBe('recent');
});
it('should show starred', () => {
expect(results[0].title).toBe('Starred Boards');
expect(results[0].items[0].title).toBe('starred and recent');
});
});
describe('with no query string and dashboards with folders returned', () => {
let results;
beforeEach(() => {
backendSrvMock.search = jest
.fn()
.mockReturnValueOnce(Promise.resolve([]))
.mockReturnValue(
Promise.resolve([
{
title: 'folder1',
type: 'dash-folder',
id: 1,
},
{
title: 'dash with no folder',
type: 'dash-db',
id: 2,
},
{
title: 'dash in folder1 1',
type: 'dash-db',
id: 3,
folderId: 1,
},
{
title: 'dash in folder1 2',
type: 'dash-db',
id: 4,
folderId: 1,
},
]),
);
return searchSrv.search({ query: '' }).then(res => {
results = res;
});
});
it('should create sections for each folder and root', () => {
expect(results).toHaveLength(2);
});
it('should place folders first', () => {
expect(results[0].title).toBe('folder1');
});
});
describe("with query string and dashboards with folders returned", () => {
describe('with query string and dashboards with folders returned', () => {
let results;
beforeEach(() => {
backendSrvMock.search = jest.fn();
backendSrvMock.search.mockReturnValue(Promise.resolve([
{
id: 2,
title: 'dash with no folder',
type: 'dash-db',
},
{
id: 3,
title: 'dash in folder1 1',
type: 'dash-db',
folderId: 1,
folderTitle: 'folder1',
},
]));
return searchSrv.search({query: 'search'}).then(res => {
backendSrvMock.search.mockReturnValue(
Promise.resolve([
{
id: 2,
title: 'dash with no folder',
type: 'dash-db',
},
{
id: 3,
title: 'dash in folder1 1',
type: 'dash-db',
folderId: 1,
folderTitle: 'folder1',
},
]),
);
return searchSrv.search({ query: 'search' }).then(res => {
results = res;
});
});
it("should not specify folder ids", () => {
it('should not specify folder ids', () => {
expect(backendSrvMock.search.mock.calls[0][0].folderIds).toHaveLength(0);
});
......@@ -87,33 +190,31 @@ describe('SearchSrv', () => {
expect(results).toHaveLength(1);
expect(results[0].hideHeader).toBe(true);
});
});
describe("with tags", () => {
describe('with tags', () => {
beforeEach(() => {
backendSrvMock.search = jest.fn();
backendSrvMock.search.mockReturnValue(Promise.resolve([]));
return searchSrv.search({tag: ['atag']}).then(() => {});
return searchSrv.search({ tag: ['atag'] }).then(() => {});
});
it("should send tags query to backend search", () => {
it('should send tags query to backend search', () => {
expect(backendSrvMock.search.mock.calls[0][0].tag).toHaveLength(1);
});
});
describe("with starred", () => {
describe('with starred', () => {
beforeEach(() => {
backendSrvMock.search = jest.fn();
backendSrvMock.search.mockReturnValue(Promise.resolve([]));
return searchSrv.search({starred: true}).then(() => {});
return searchSrv.search({ starred: true }).then(() => {});
});
it("should send starred query to backend search", () => {
it('should send starred query to backend search', () => {
expect(backendSrvMock.search.mock.calls[0][0].starred).toEqual(true);
});
});
});
import './dashboard_ctrl';
import './alerting_srv';
import './history/history';
......@@ -15,7 +14,6 @@ import './time_srv';
import './unsavedChangesSrv';
import './unsaved_changes_modal';
import './timepicker/timepicker';
import './impression_store';
import './upload';
import './import/dash_import';
import './export/export_modal';
......
......@@ -5,12 +5,13 @@ define([
'jquery',
'app/core/utils/kbn',
'app/core/utils/datemath',
'./impression_store'
'app/core/services/impression_srv'
],
function (angular, moment, _, $, kbn, dateMath, impressionStore) {
function (angular, moment, _, $, kbn, dateMath, impressionSrv) {
'use strict';
kbn = kbn.default;
impressionSrv = impressionSrv.default;
var module = angular.module('grafana.services');
......@@ -50,7 +51,7 @@ function (angular, moment, _, $, kbn, dateMath, impressionStore) {
promise.then(function(result) {
if (result.meta.dashboardNotFound !== true) {
impressionStore.impressions.addDashboardImpression(result.dashboard.id);
impressionSrv.addDashboardImpression(result.dashboard.id);
}
return result;
......
......@@ -26,14 +26,6 @@ export class DashboardListCtrl {
}
getDashboards() {
if (this.query.query.length === 0 &&
this.query.tag.length === 0 &&
!this.query.starred) {
return this.searchSrv.browse().then((result) => {
return this.initDashboardList(result);
});
}
return this.searchSrv.search(this.query).then((result) => {
return this.initDashboardList(result);
});
......@@ -144,7 +136,7 @@ export class DashboardListCtrl {
}
toggleFolder(section) {
return this.searchSrv.toggleFolder(section);
return this.searchSrv.toggleSection(section);
}
getTags() {
......
......@@ -93,7 +93,7 @@ describe('DashboardListCtrl', () => {
}
];
ctrl = createCtrlWithStubs([], response);
ctrl = createCtrlWithStubs(response);
});
describe('with query filter', () => {
......@@ -490,15 +490,12 @@ describe('DashboardListCtrl', () => {
});
});
function createCtrlWithStubs(browseResponse: any, searchResponse?: any, tags?: any) {
function createCtrlWithStubs(searchResponse: any, tags?: any) {
const searchSrvStub = {
browse: () => {
return q.resolve(browseResponse);
},
search: (options: any) => {
return q.resolve(searchResponse);
},
toggleFolder: (section) => {
toggleSection: (section) => {
return;
},
getDashboardTags: () => {
......
......@@ -13,7 +13,7 @@ import * as datemath from 'app/core/utils/datemath';
import * as fileExport from 'app/core/utils/file_export';
import * as flatten from 'app/core/utils/flatten';
import * as ticks from 'app/core/utils/ticks';
import {impressions} from 'app/features/dashboard/impression_store';
import impressionSrv from 'app/core/services/impression_srv';
import builtInPlugins from './built_in_plugins';
import * as d3 from 'd3';
......@@ -78,7 +78,7 @@ exposeToPlugin('vendor/npm/rxjs/Rx', {
});
exposeToPlugin('app/features/dashboard/impression_store', {
impressions: impressions,
impressions: impressionSrv,
__esModule: true
});
......
import _ from 'lodash';
import {PanelCtrl} from 'app/plugins/sdk';
import {impressions} from 'app/features/dashboard/impression_store';
import impressionSrv from 'app/core/services/impression_srv';
class DashListCtrl extends PanelCtrl {
static templateUrl = 'module.html';
......@@ -123,7 +123,7 @@ class DashListCtrl extends PanelCtrl {
return Promise.resolve();
}
var dashIds = _.take(impressions.getDashboardOpened(), this.panel.limit);
var dashIds = _.take(impressionSrv.getDashboardOpened(), this.panel.limit);
return this.backendSrv.search({dashboardIds: dashIds, limit: this.panel.limit}).then(result => {
this.groups[1].list = dashIds.map(orderId => {
return _.find(result, dashboard => {
......
......@@ -592,8 +592,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
if (!ctrl.data) { return; }
data = ctrl.data;
console.log('singlestat', elem.html());
// get thresholds
data.thresholds = panel.thresholds.split(',').map(function(strVale) {
return Number(strVale.trim());
......
......@@ -123,7 +123,7 @@
.page-breadcrumbs {
display: flex;
padding: 3px 1.5rem 1.5rem 1.5rem;
padding: 10px 25px;
line-height: 0.5;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment