mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
worked on search
This commit is contained in:
parent
93d21bec75
commit
bc81298d4c
@ -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;
|
@ -1,23 +1,87 @@
|
||||
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');
|
||||
}
|
||||
|
@ -1,85 +1,188 @@
|
||||
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 no query string and dashboards with folders returned", () => {
|
||||
describe('With recent dashboards', () => {
|
||||
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
|
||||
},
|
||||
]));
|
||||
backendSrvMock.search = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(
|
||||
Promise.resolve([{ id: 2, title: 'second but first' }, { id: 1, title: 'first but second' }]),
|
||||
)
|
||||
.mockReturnValue(Promise.resolve([]));
|
||||
|
||||
return searchSrv.search({query: ''}).then(res => {
|
||||
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 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 starred dashboards and recent', () => {
|
||||
let results;
|
||||
|
||||
beforeEach(() => {
|
||||
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 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',
|
||||
},
|
||||
]));
|
||||
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 => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user