mirror of https://github.com/grafana/grafana
parent
331be7d47a
commit
e58c2ebc1c
@ -0,0 +1,45 @@ |
||||
import _ from 'lodash'; |
||||
import { DataSource, PluginMeta, NavModel } from 'app/types'; |
||||
|
||||
export function buildNavModel(ds: DataSource, plugin: PluginMeta, currentPage: string): NavModel { |
||||
let title = 'New'; |
||||
const subTitle = `Type: ${plugin.name}`; |
||||
|
||||
if (ds.id) { |
||||
title = ds.name; |
||||
} |
||||
|
||||
const main = { |
||||
img: plugin.info.logos.large, |
||||
id: 'ds-edit-' + plugin.id, |
||||
subTitle: subTitle, |
||||
url: '', |
||||
text: title, |
||||
breadcrumbs: [{ title: 'Data Sources', url: 'datasources' }], |
||||
children: [ |
||||
{ |
||||
active: currentPage === 'datasource-settings', |
||||
icon: 'fa fa-fw fa-sliders', |
||||
id: 'datasource-settings', |
||||
text: 'Settings', |
||||
url: `datasources/edit/${ds.id}`, |
||||
}, |
||||
], |
||||
}; |
||||
|
||||
const hasDashboards = _.find(plugin.includes, { type: 'dashboard' }) !== undefined; |
||||
if (hasDashboards && ds.id) { |
||||
main.children.push({ |
||||
active: currentPage === 'datasource-dashboards', |
||||
icon: 'fa fa-fw fa-th-large', |
||||
id: 'datasource-dashboards', |
||||
text: 'Dashboards', |
||||
url: `datasources/edit/${ds.id}/dashboards`, |
||||
}); |
||||
} |
||||
|
||||
return { |
||||
main: main, |
||||
node: _.find(main.children, { active: true }), |
||||
}; |
||||
} |
||||
@ -1,19 +0,0 @@ |
||||
import { types } from 'mobx-state-tree'; |
||||
|
||||
export const NavItem = types.model('NavItem', { |
||||
id: types.identifier(types.string), |
||||
text: types.string, |
||||
url: types.optional(types.string, ''), |
||||
subTitle: types.optional(types.string, ''), |
||||
icon: types.optional(types.string, ''), |
||||
img: types.optional(types.string, ''), |
||||
active: types.optional(types.boolean, false), |
||||
hideFromTabs: types.optional(types.boolean, false), |
||||
breadcrumbs: types.optional(types.array(types.late(() => Breadcrumb)), []), |
||||
children: types.optional(types.array(types.late(() => NavItem)), []), |
||||
}); |
||||
|
||||
export const Breadcrumb = types.model('Breadcrumb', { |
||||
title: types.string, |
||||
url: types.string, |
||||
}); |
||||
@ -1,47 +0,0 @@ |
||||
import { NavStore } from './NavStore'; |
||||
|
||||
describe('NavStore', () => { |
||||
const folderId = 1; |
||||
const folderTitle = 'Folder Name'; |
||||
const folderUrl = '/dashboards/f/uid/folder-name'; |
||||
const canAdmin = true; |
||||
|
||||
const folder = { |
||||
id: folderId, |
||||
url: folderUrl, |
||||
title: folderTitle, |
||||
canAdmin: canAdmin, |
||||
}; |
||||
|
||||
let store; |
||||
|
||||
beforeEach(() => { |
||||
store = NavStore.create(); |
||||
store.initFolderNav(folder, 'manage-folder-settings'); |
||||
}); |
||||
|
||||
it('Should set text', () => { |
||||
expect(store.main.text).toBe(folderTitle); |
||||
}); |
||||
|
||||
it('Should load nav with tabs', () => { |
||||
expect(store.main.children.length).toBe(3); |
||||
expect(store.main.children[0].id).toBe('manage-folder-dashboards'); |
||||
expect(store.main.children[1].id).toBe('manage-folder-permissions'); |
||||
expect(store.main.children[2].id).toBe('manage-folder-settings'); |
||||
}); |
||||
|
||||
it('Should set correct urls for each tab', () => { |
||||
expect(store.main.children.length).toBe(3); |
||||
expect(store.main.children[0].url).toBe(folderUrl); |
||||
expect(store.main.children[1].url).toBe(`${folderUrl}/permissions`); |
||||
expect(store.main.children[2].url).toBe(`${folderUrl}/settings`); |
||||
}); |
||||
|
||||
it('Should set active tab', () => { |
||||
expect(store.main.children.length).toBe(3); |
||||
expect(store.main.children[0].active).toBe(false); |
||||
expect(store.main.children[1].active).toBe(false); |
||||
expect(store.main.children[2].active).toBe(true); |
||||
}); |
||||
}); |
||||
@ -1,118 +0,0 @@ |
||||
import _ from 'lodash'; |
||||
import { types, getEnv } from 'mobx-state-tree'; |
||||
import { NavItem } from './NavItem'; |
||||
|
||||
export const NavStore = types |
||||
.model('NavStore', { |
||||
main: types.maybe(NavItem), |
||||
node: types.maybe(NavItem), |
||||
}) |
||||
.actions(self => ({ |
||||
load(...args) { |
||||
let children = getEnv(self).navTree; |
||||
let main, node; |
||||
const parents = []; |
||||
|
||||
for (const id of args) { |
||||
node = children.find(el => el.id === id); |
||||
|
||||
if (!node) { |
||||
throw new Error(`NavItem with id ${id} not found`); |
||||
} |
||||
|
||||
children = node.children; |
||||
parents.push(node); |
||||
} |
||||
|
||||
main = parents[parents.length - 2]; |
||||
|
||||
if (main.children) { |
||||
for (const item of main.children) { |
||||
item.active = false; |
||||
|
||||
if (item.url === node.url) { |
||||
item.active = true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
self.main = NavItem.create(main); |
||||
self.node = NavItem.create(node); |
||||
}, |
||||
|
||||
initFolderNav(folder: any, activeChildId: string) { |
||||
const main = { |
||||
icon: 'fa fa-folder-open', |
||||
id: 'manage-folder', |
||||
subTitle: 'Manage folder dashboards & permissions', |
||||
url: '', |
||||
text: folder.title, |
||||
breadcrumbs: [{ title: 'Dashboards', url: 'dashboards' }], |
||||
children: [ |
||||
{ |
||||
active: activeChildId === 'manage-folder-dashboards', |
||||
icon: 'fa fa-fw fa-th-large', |
||||
id: 'manage-folder-dashboards', |
||||
text: 'Dashboards', |
||||
url: folder.url, |
||||
}, |
||||
{ |
||||
active: activeChildId === 'manage-folder-permissions', |
||||
icon: 'fa fa-fw fa-lock', |
||||
id: 'manage-folder-permissions', |
||||
text: 'Permissions', |
||||
url: `${folder.url}/permissions`, |
||||
}, |
||||
{ |
||||
active: activeChildId === 'manage-folder-settings', |
||||
icon: 'fa fa-fw fa-cog', |
||||
id: 'manage-folder-settings', |
||||
text: 'Settings', |
||||
url: `${folder.url}/settings`, |
||||
}, |
||||
], |
||||
}; |
||||
|
||||
self.main = NavItem.create(main); |
||||
}, |
||||
|
||||
initDatasourceEditNav(ds: any, plugin: any, currentPage: string) { |
||||
let title = 'New'; |
||||
const subTitle = `Type: ${plugin.name}`; |
||||
|
||||
if (ds.id) { |
||||
title = ds.name; |
||||
} |
||||
|
||||
const main = { |
||||
img: plugin.info.logos.large, |
||||
id: 'ds-edit-' + plugin.id, |
||||
subTitle: subTitle, |
||||
url: '', |
||||
text: title, |
||||
breadcrumbs: [{ title: 'Data Sources', url: 'datasources' }], |
||||
children: [ |
||||
{ |
||||
active: currentPage === 'datasource-settings', |
||||
icon: 'fa fa-fw fa-sliders', |
||||
id: 'datasource-settings', |
||||
text: 'Settings', |
||||
url: `datasources/edit/${ds.id}`, |
||||
}, |
||||
], |
||||
}; |
||||
|
||||
const hasDashboards = _.find(plugin.includes, { type: 'dashboard' }) !== undefined; |
||||
if (hasDashboards && ds.id) { |
||||
main.children.push({ |
||||
active: currentPage === 'datasource-dashboards', |
||||
icon: 'fa fa-fw fa-th-large', |
||||
id: 'datasource-dashboards', |
||||
text: 'Dashboards', |
||||
url: `datasources/edit/${ds.id}/dashboards`, |
||||
}); |
||||
} |
||||
|
||||
self.main = NavItem.create(main); |
||||
}, |
||||
})); |
||||
@ -1,116 +0,0 @@ |
||||
import { PermissionsStore } from './PermissionsStore'; |
||||
import { backendSrv } from 'test/mocks/common'; |
||||
|
||||
describe('PermissionsStore', () => { |
||||
let store; |
||||
|
||||
beforeEach(async () => { |
||||
backendSrv.get.mockReturnValue( |
||||
Promise.resolve([ |
||||
{ id: 2, dashboardId: 1, role: 'Viewer', permission: 1, permissionName: 'View' }, |
||||
{ id: 3, dashboardId: 1, role: 'Editor', permission: 1, permissionName: 'Edit' }, |
||||
{ |
||||
id: 4, |
||||
dashboardId: 10, |
||||
permission: 1, |
||||
permissionName: 'View', |
||||
teamId: 1, |
||||
team: 'MyTestTeam', |
||||
inherited: true, |
||||
}, |
||||
{ |
||||
id: 5, |
||||
dashboardId: 1, |
||||
permission: 1, |
||||
permissionName: 'View', |
||||
userId: 1, |
||||
userLogin: 'MyTestUser', |
||||
}, |
||||
{ |
||||
id: 6, |
||||
dashboardId: 1, |
||||
permission: 1, |
||||
permissionName: 'Edit', |
||||
teamId: 2, |
||||
team: 'MyTestTeam2', |
||||
}, |
||||
]) |
||||
); |
||||
|
||||
backendSrv.post = jest.fn(() => Promise.resolve({})); |
||||
|
||||
store = PermissionsStore.create( |
||||
{ |
||||
fetching: false, |
||||
items: [], |
||||
}, |
||||
{ |
||||
backendSrv: backendSrv, |
||||
} |
||||
); |
||||
|
||||
await store.load(1, false, false); |
||||
}); |
||||
|
||||
it('should save update on permission change', async () => { |
||||
expect(store.items[0].permission).toBe(1); |
||||
expect(store.items[0].permissionName).toBe('View'); |
||||
|
||||
await store.updatePermissionOnIndex(0, 2, 'Edit'); |
||||
|
||||
expect(store.items[0].permission).toBe(2); |
||||
expect(store.items[0].permissionName).toBe('Edit'); |
||||
expect(backendSrv.post.mock.calls.length).toBe(1); |
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/permissions'); |
||||
}); |
||||
|
||||
it('should save removed permissions automatically', async () => { |
||||
expect(store.items.length).toBe(5); |
||||
|
||||
await store.removeStoreItem(2); |
||||
|
||||
expect(store.items.length).toBe(4); |
||||
expect(backendSrv.post.mock.calls.length).toBe(1); |
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/permissions'); |
||||
}); |
||||
|
||||
it('should be sorted by sort rank and alphabetically', async () => { |
||||
expect(store.items[0].name).toBe('MyTestTeam'); |
||||
expect(store.items[0].dashboardId).toBe(10); |
||||
expect(store.items[1].name).toBe('Editor'); |
||||
expect(store.items[2].name).toBe('Viewer'); |
||||
expect(store.items[3].name).toBe('MyTestTeam2'); |
||||
expect(store.items[4].name).toBe('MyTestUser'); |
||||
}); |
||||
|
||||
describe('when one inherited and one not inherited team permission are added', () => { |
||||
beforeEach(async () => { |
||||
const overridingItemForChildDashboard = { |
||||
team: 'MyTestTeam', |
||||
dashboardId: 1, |
||||
teamId: 1, |
||||
permission: 2, |
||||
}; |
||||
|
||||
store.resetNewType(); |
||||
store.newItem.setTeam(overridingItemForChildDashboard.teamId, overridingItemForChildDashboard.team); |
||||
store.newItem.setPermission(overridingItemForChildDashboard.permission); |
||||
await store.addStoreItem(); |
||||
}); |
||||
|
||||
it('should add new overriding permission', () => { |
||||
expect(store.items.length).toBe(6); |
||||
}); |
||||
|
||||
it('should be sorted by sort rank and alphabetically', async () => { |
||||
expect(store.items[0].name).toBe('MyTestTeam'); |
||||
expect(store.items[0].dashboardId).toBe(10); |
||||
expect(store.items[1].name).toBe('Editor'); |
||||
expect(store.items[2].name).toBe('Viewer'); |
||||
expect(store.items[3].name).toBe('MyTestTeam'); |
||||
expect(store.items[3].dashboardId).toBe(1); |
||||
expect(store.items[4].name).toBe('MyTestTeam2'); |
||||
expect(store.items[5].name).toBe('MyTestUser'); |
||||
}); |
||||
}); |
||||
}); |
||||
@ -1,259 +0,0 @@ |
||||
import { types, getEnv, flow } from 'mobx-state-tree'; |
||||
import { PermissionsStoreItem } from './PermissionsStoreItem'; |
||||
|
||||
export const permissionOptions = [ |
||||
{ value: 1, label: 'View', description: 'Can view dashboards.' }, |
||||
{ value: 2, label: 'Edit', description: 'Can add, edit and delete dashboards.' }, |
||||
{ |
||||
value: 4, |
||||
label: 'Admin', |
||||
description: 'Can add/remove permissions and can add, edit and delete dashboards.', |
||||
}, |
||||
]; |
||||
|
||||
export const aclTypeValues = { |
||||
GROUP: { value: 'Group', text: 'Team' }, |
||||
USER: { value: 'User', text: 'User' }, |
||||
VIEWER: { value: 'Viewer', text: 'Everyone With Viewer Role' }, |
||||
EDITOR: { value: 'Editor', text: 'Everyone With Editor Role' }, |
||||
}; |
||||
|
||||
export const aclTypes = Object.keys(aclTypeValues).map(item => aclTypeValues[item]); |
||||
|
||||
const defaultNewType = aclTypes[0].value; |
||||
|
||||
export const NewPermissionsItem = types |
||||
.model('NewPermissionsItem', { |
||||
type: types.optional( |
||||
types.enumeration(Object.keys(aclTypeValues).map(item => aclTypeValues[item].value)), |
||||
defaultNewType |
||||
), |
||||
userId: types.maybe(types.number), |
||||
userLogin: types.maybe(types.string), |
||||
userAvatarUrl: types.maybe(types.string), |
||||
teamAvatarUrl: types.maybe(types.string), |
||||
teamId: types.maybe(types.number), |
||||
team: types.maybe(types.string), |
||||
permission: types.optional(types.number, 1), |
||||
}) |
||||
.views(self => ({ |
||||
isValid: () => { |
||||
switch (self.type) { |
||||
case aclTypeValues.GROUP.value: |
||||
return self.teamId && self.team; |
||||
case aclTypeValues.USER.value: |
||||
return !!self.userId && !!self.userLogin; |
||||
case aclTypeValues.VIEWER.value: |
||||
case aclTypeValues.EDITOR.value: |
||||
return true; |
||||
default: |
||||
return false; |
||||
} |
||||
}, |
||||
})) |
||||
.actions(self => ({ |
||||
setUser(userId: number, userLogin: string, userAvatarUrl: string) { |
||||
self.userId = userId; |
||||
self.userLogin = userLogin; |
||||
self.userAvatarUrl = userAvatarUrl; |
||||
self.teamId = null; |
||||
self.team = null; |
||||
}, |
||||
setTeam(teamId: number, team: string, teamAvatarUrl: string) { |
||||
self.userId = null; |
||||
self.userLogin = null; |
||||
self.teamId = teamId; |
||||
self.team = team; |
||||
self.teamAvatarUrl = teamAvatarUrl; |
||||
}, |
||||
setPermission(permission: number) { |
||||
self.permission = permission; |
||||
}, |
||||
})); |
||||
|
||||
export const PermissionsStore = types |
||||
.model('PermissionsStore', { |
||||
fetching: types.boolean, |
||||
isFolder: types.maybe(types.boolean), |
||||
dashboardId: types.maybe(types.number), |
||||
items: types.optional(types.array(PermissionsStoreItem), []), |
||||
originalItems: types.optional(types.array(PermissionsStoreItem), []), |
||||
newType: types.optional(types.string, defaultNewType), |
||||
newItem: types.maybe(NewPermissionsItem), |
||||
isAddPermissionsVisible: types.optional(types.boolean, false), |
||||
isInRoot: types.maybe(types.boolean), |
||||
}) |
||||
.views(self => ({ |
||||
isValid: item => { |
||||
const dupe = self.items.find(it => { |
||||
return isDuplicate(it, item); |
||||
}); |
||||
if (dupe) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
}, |
||||
})) |
||||
.actions(self => { |
||||
const resetNewTypeInternal = () => { |
||||
self.newItem = NewPermissionsItem.create(); |
||||
}; |
||||
|
||||
return { |
||||
load: flow(function* load(dashboardId: number, isFolder: boolean, isInRoot: boolean) { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
self.fetching = true; |
||||
self.isFolder = isFolder; |
||||
self.isInRoot = isInRoot; |
||||
self.dashboardId = dashboardId; |
||||
self.items.clear(); |
||||
|
||||
const res = yield backendSrv.get(`/api/dashboards/id/${dashboardId}/permissions`); |
||||
const items = prepareServerResponse(res, dashboardId, isFolder, isInRoot); |
||||
self.items = items; |
||||
self.originalItems = items; |
||||
self.fetching = false; |
||||
}), |
||||
|
||||
addStoreItem: flow(function* addStoreItem() { |
||||
const item = { |
||||
type: self.newItem.type, |
||||
permission: self.newItem.permission, |
||||
dashboardId: self.dashboardId, |
||||
team: undefined, |
||||
teamId: undefined, |
||||
userLogin: undefined, |
||||
userId: undefined, |
||||
userAvatarUrl: undefined, |
||||
teamAvatarUrl: undefined, |
||||
role: undefined, |
||||
}; |
||||
switch (self.newItem.type) { |
||||
case aclTypeValues.GROUP.value: |
||||
item.team = self.newItem.team; |
||||
item.teamId = self.newItem.teamId; |
||||
item.teamAvatarUrl = self.newItem.teamAvatarUrl; |
||||
break; |
||||
case aclTypeValues.USER.value: |
||||
item.userLogin = self.newItem.userLogin; |
||||
item.userId = self.newItem.userId; |
||||
item.userAvatarUrl = self.newItem.userAvatarUrl; |
||||
break; |
||||
case aclTypeValues.VIEWER.value: |
||||
case aclTypeValues.EDITOR.value: |
||||
item.role = self.newItem.type; |
||||
break; |
||||
default: |
||||
throw Error('Unknown type: ' + self.newItem.type); |
||||
} |
||||
|
||||
const updatedItems = self.items.peek(); |
||||
const newItem = prepareItem(item, self.dashboardId, self.isFolder, self.isInRoot); |
||||
updatedItems.push(newItem); |
||||
|
||||
try { |
||||
yield updateItems(self, updatedItems); |
||||
self.items.push(newItem); |
||||
const sortedItems = self.items.sort((a, b) => b.sortRank - a.sortRank || a.name.localeCompare(b.name)); |
||||
self.items = sortedItems; |
||||
resetNewTypeInternal(); |
||||
} catch {} |
||||
yield Promise.resolve(); |
||||
}), |
||||
|
||||
removeStoreItem: flow(function* removeStoreItem(idx: number) { |
||||
self.items.splice(idx, 1); |
||||
yield updateItems(self, self.items.peek()); |
||||
}), |
||||
|
||||
updatePermissionOnIndex: flow(function* updatePermissionOnIndex( |
||||
idx: number, |
||||
permission: number, |
||||
permissionName: string |
||||
) { |
||||
self.items[idx].updatePermission(permission, permissionName); |
||||
yield updateItems(self, self.items.peek()); |
||||
}), |
||||
|
||||
setNewType(newType: string) { |
||||
self.newItem = NewPermissionsItem.create({ type: newType }); |
||||
}, |
||||
|
||||
resetNewType() { |
||||
resetNewTypeInternal(); |
||||
}, |
||||
|
||||
toggleAddPermissions() { |
||||
self.isAddPermissionsVisible = !self.isAddPermissionsVisible; |
||||
}, |
||||
|
||||
hideAddPermissions() { |
||||
self.isAddPermissionsVisible = false; |
||||
}, |
||||
}; |
||||
}); |
||||
|
||||
const updateItems = (self, items) => { |
||||
const backendSrv = getEnv(self).backendSrv; |
||||
const updated = []; |
||||
for (const item of items) { |
||||
if (item.inherited) { |
||||
continue; |
||||
} |
||||
updated.push({ |
||||
id: item.id, |
||||
userId: item.userId, |
||||
teamId: item.teamId, |
||||
role: item.role, |
||||
permission: item.permission, |
||||
}); |
||||
} |
||||
|
||||
return backendSrv.post(`/api/dashboards/id/${self.dashboardId}/permissions`, { |
||||
items: updated, |
||||
}); |
||||
}; |
||||
|
||||
const prepareServerResponse = (response, dashboardId: number, isFolder: boolean, isInRoot: boolean) => { |
||||
return response |
||||
.map(item => { |
||||
return prepareItem(item, dashboardId, isFolder, isInRoot); |
||||
}) |
||||
.sort((a, b) => b.sortRank - a.sortRank || a.name.localeCompare(b.name)); |
||||
}; |
||||
|
||||
const prepareItem = (item, dashboardId: number, isFolder: boolean, isInRoot: boolean) => { |
||||
item.sortRank = 0; |
||||
if (item.userId > 0) { |
||||
item.name = item.userLogin; |
||||
item.sortRank = 10; |
||||
} else if (item.teamId > 0) { |
||||
item.name = item.team; |
||||
item.sortRank = 20; |
||||
} else if (item.role) { |
||||
item.icon = 'fa fa-fw fa-street-view'; |
||||
item.name = item.role; |
||||
item.sortRank = 30; |
||||
if (item.role === 'Editor') { |
||||
item.sortRank += 1; |
||||
} |
||||
} |
||||
|
||||
if (item.inherited) { |
||||
item.sortRank += 100; |
||||
} |
||||
return item; |
||||
}; |
||||
|
||||
const isDuplicate = (origItem, newItem) => { |
||||
if (origItem.inherited) { |
||||
return false; |
||||
} |
||||
|
||||
return ( |
||||
(origItem.role && newItem.role && origItem.role === newItem.role) || |
||||
(origItem.userId && newItem.userId && origItem.userId === newItem.userId) || |
||||
(origItem.teamId && newItem.teamId && origItem.teamId === newItem.teamId) |
||||
); |
||||
}; |
||||
@ -1,29 +0,0 @@ |
||||
import { types } from 'mobx-state-tree'; |
||||
|
||||
export const PermissionsStoreItem = types |
||||
.model('PermissionsStoreItem', { |
||||
dashboardId: types.optional(types.number, -1), |
||||
permission: types.number, |
||||
permissionName: types.maybe(types.string), |
||||
role: types.maybe(types.string), |
||||
team: types.optional(types.string, ''), |
||||
teamId: types.optional(types.number, 0), |
||||
userEmail: types.optional(types.string, ''), |
||||
userId: types.optional(types.number, 0), |
||||
userLogin: types.optional(types.string, ''), |
||||
inherited: types.maybe(types.boolean), |
||||
sortRank: types.maybe(types.number), |
||||
icon: types.maybe(types.string), |
||||
name: types.maybe(types.string), |
||||
teamAvatarUrl: types.maybe(types.string), |
||||
userAvatarUrl: types.maybe(types.string), |
||||
}) |
||||
.actions(self => ({ |
||||
updateRole: role => { |
||||
self.role = role; |
||||
}, |
||||
updatePermission(permission: number, permissionName: string) { |
||||
self.permission = permission; |
||||
self.permissionName = permissionName; |
||||
}, |
||||
})); |
||||
@ -1,20 +0,0 @@ |
||||
import { types } from 'mobx-state-tree'; |
||||
import { NavStore } from './../NavStore/NavStore'; |
||||
import { ViewStore } from './../ViewStore/ViewStore'; |
||||
import { PermissionsStore } from './../PermissionsStore/PermissionsStore'; |
||||
|
||||
export const RootStore = types.model({ |
||||
nav: types.optional(NavStore, {}), |
||||
permissions: types.optional(PermissionsStore, { |
||||
fetching: false, |
||||
items: [], |
||||
}), |
||||
view: types.optional(ViewStore, { |
||||
path: '', |
||||
query: {}, |
||||
routeParams: {}, |
||||
}), |
||||
}); |
||||
|
||||
type RootStoreType = typeof RootStore.Type; |
||||
export interface RootStoreInterface extends RootStoreType {} |
||||
@ -1,33 +0,0 @@ |
||||
import { ViewStore } from './ViewStore'; |
||||
import { toJS } from 'mobx'; |
||||
|
||||
describe('ViewStore', () => { |
||||
let store; |
||||
|
||||
beforeAll(() => { |
||||
store = ViewStore.create({ |
||||
path: '', |
||||
query: {}, |
||||
routeParams: {}, |
||||
}); |
||||
}); |
||||
|
||||
it('Can update path and query', () => { |
||||
store.updatePathAndQuery('/hello', { key: 1, otherParam: 'asd' }, { key: 1, otherParam: 'asd' }); |
||||
expect(store.path).toBe('/hello'); |
||||
expect(store.query.get('key')).toBe(1); |
||||
expect(store.currentUrl).toBe('/hello?key=1&otherParam=asd'); |
||||
}); |
||||
|
||||
it('Query can contain arrays', () => { |
||||
store.updatePathAndQuery('/hello', { values: ['A', 'B'] }, { key: 1, otherParam: 'asd' }); |
||||
expect(toJS(store.query.get('values'))).toMatchObject(['A', 'B']); |
||||
expect(store.currentUrl).toBe('/hello?values=A&values=B'); |
||||
}); |
||||
|
||||
it('Query can contain boolean', () => { |
||||
store.updatePathAndQuery('/hello', { abool: true }, { abool: true }); |
||||
expect(toJS(store.query.get('abool'))).toBe(true); |
||||
expect(store.currentUrl).toBe('/hello?abool'); |
||||
}); |
||||
}); |
||||
@ -1,55 +0,0 @@ |
||||
import { types } from 'mobx-state-tree'; |
||||
import { toJS } from 'mobx'; |
||||
import { toUrlParams } from 'app/core/utils/url'; |
||||
|
||||
const QueryInnerValueType = types.union(types.string, types.boolean, types.number); |
||||
const QueryValueType = types.union(QueryInnerValueType, types.array(QueryInnerValueType)); |
||||
|
||||
export const ViewStore = types |
||||
.model({ |
||||
path: types.string, |
||||
query: types.map(QueryValueType), |
||||
routeParams: types.map(QueryValueType), |
||||
}) |
||||
.views(self => ({ |
||||
get currentUrl() { |
||||
let path = self.path; |
||||
|
||||
if (self.query.size) { |
||||
path += '?' + toUrlParams(toJS(self.query)); |
||||
} |
||||
return path; |
||||
}, |
||||
})) |
||||
.actions(self => { |
||||
// querystring only
|
||||
function updateQuery(query: any) { |
||||
self.query.clear(); |
||||
for (const key of Object.keys(query)) { |
||||
if (query[key]) { |
||||
self.query.set(key, query[key]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// needed to get route parameters like slug from the url
|
||||
function updateRouteParams(routeParams: any) { |
||||
self.routeParams.clear(); |
||||
for (const key of Object.keys(routeParams)) { |
||||
if (routeParams[key]) { |
||||
self.routeParams.set(key, routeParams[key]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
function updatePathAndQuery(path: string, query: any, routeParams: any) { |
||||
self.path = path; |
||||
updateQuery(query); |
||||
updateRouteParams(routeParams); |
||||
} |
||||
|
||||
return { |
||||
updateQuery, |
||||
updatePathAndQuery, |
||||
}; |
||||
}); |
||||
@ -1,16 +0,0 @@ |
||||
import { RootStore, RootStoreInterface } from './RootStore/RootStore'; |
||||
import config from 'app/core/config'; |
||||
|
||||
export let store: RootStoreInterface; |
||||
|
||||
export function createStore(services) { |
||||
store = RootStore.create( |
||||
{}, |
||||
{ |
||||
...services, |
||||
navTree: config.bootData.navTree, |
||||
} |
||||
); |
||||
|
||||
return store; |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
export interface DataSource { |
||||
id: number; |
||||
orgId: number; |
||||
name: string; |
||||
typeLogoUrl: string; |
||||
type: string; |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
export interface PluginMeta { |
||||
id: string; |
||||
name: string; |
||||
info: PluginMetaInfo; |
||||
includes: PluginInclude[]; |
||||
} |
||||
|
||||
export interface PluginInclude { |
||||
type: string; |
||||
name: string; |
||||
path: string; |
||||
} |
||||
|
||||
export interface PluginMetaInfo { |
||||
logos: { |
||||
large: string; |
||||
small: string; |
||||
}; |
||||
} |
||||
Loading…
Reference in new issue