From e58c2ebc1c8255813b04960613ac599855b3f96e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 14 Sep 2018 09:41:37 +0200 Subject: [PATCH] tech: remove all mobx stuff --- package.json | 4 - .../core/components/PageHeader/PageHeader.tsx | 5 +- .../DisabledPermissionListItem.tsx | 4 +- public/app/core/components/grafana_app.ts | 4 +- public/app/core/services/bridge_srv.ts | 33 +-- .../DashboardPermissions.tsx | 3 +- .../features/plugins/ds_dashboards_ctrl.ts | 15 +- public/app/features/plugins/ds_edit_ctrl.ts | 15 +- public/app/features/plugins/state/navModel.ts | 45 +++ public/app/routes/ReactContainer.tsx | 14 +- .../app/{stores => store}/configureStore.ts | 0 public/app/stores/NavStore/NavItem.ts | 19 -- public/app/stores/NavStore/NavStore.test.ts | 47 ---- public/app/stores/NavStore/NavStore.ts | 118 -------- .../PermissionsStore/PermissionsStore.test.ts | 116 -------- .../PermissionsStore/PermissionsStore.ts | 259 ------------------ .../PermissionsStore/PermissionsStoreItem.ts | 29 -- public/app/stores/RootStore/RootStore.ts | 20 -- public/app/stores/ViewStore/ViewStore.test.ts | 33 --- public/app/stores/ViewStore/ViewStore.ts | 55 ---- public/app/stores/store.ts | 16 -- public/app/types/datasources.ts | 7 + public/app/types/{folder.ts => folders.ts} | 0 public/app/types/index.ts | 6 +- public/app/types/plugins.ts | 19 ++ yarn.lock | 20 +- 26 files changed, 105 insertions(+), 801 deletions(-) create mode 100644 public/app/features/plugins/state/navModel.ts rename public/app/{stores => store}/configureStore.ts (100%) delete mode 100644 public/app/stores/NavStore/NavItem.ts delete mode 100644 public/app/stores/NavStore/NavStore.test.ts delete mode 100644 public/app/stores/NavStore/NavStore.ts delete mode 100644 public/app/stores/PermissionsStore/PermissionsStore.test.ts delete mode 100644 public/app/stores/PermissionsStore/PermissionsStore.ts delete mode 100644 public/app/stores/PermissionsStore/PermissionsStoreItem.ts delete mode 100644 public/app/stores/RootStore/RootStore.ts delete mode 100644 public/app/stores/ViewStore/ViewStore.test.ts delete mode 100644 public/app/stores/ViewStore/ViewStore.ts delete mode 100644 public/app/stores/store.ts create mode 100644 public/app/types/datasources.ts rename public/app/types/{folder.ts => folders.ts} (100%) create mode 100644 public/app/types/plugins.ts diff --git a/package.json b/package.json index 071d32992af..e73c644c5bf 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "lint-staged": "^6.0.0", "load-grunt-tasks": "3.5.2", "mini-css-extract-plugin": "^0.4.0", - "mobx-react-devtools": "^4.2.15", "mocha": "^4.0.1", "ng-annotate-loader": "^0.6.1", "ng-annotate-webpack-plugin": "^0.3.0", @@ -146,9 +145,6 @@ "immutable": "^3.8.2", "jquery": "^3.2.1", "lodash": "^4.17.10", - "mobx": "^3.4.1", - "mobx-react": "^4.3.5", - "mobx-state-tree": "^1.3.1", "moment": "^2.22.2", "mousetrap": "^1.6.0", "mousetrap-global-bind": "^1.1.0", diff --git a/public/app/core/components/PageHeader/PageHeader.tsx b/public/app/core/components/PageHeader/PageHeader.tsx index 9feddde68ce..c176095afa4 100644 --- a/public/app/core/components/PageHeader/PageHeader.tsx +++ b/public/app/core/components/PageHeader/PageHeader.tsx @@ -1,9 +1,7 @@ import React from 'react'; -import { observer } from 'mobx-react'; import { NavModel, NavModelItem } from 'app/types'; import classNames from 'classnames'; import appEvents from 'app/core/app_events'; -import { toJS } from 'mobx'; export interface Props { model: NavModel; @@ -81,7 +79,6 @@ const Navigation = ({ main }: { main: NavModelItem }) => { ); }; -@observer export default class PageHeader extends React.Component { constructor(props) { super(props); @@ -148,7 +145,7 @@ export default class PageHeader extends React.Component { return null; } - const main = toJS(model.main); // Convert to JS if its a mobx observable + const main = model.main; return (
diff --git a/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx b/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx index d65595dae66..d648d06e414 100644 --- a/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx +++ b/public/app/core/components/PermissionList/DisabledPermissionListItem.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import DescriptionPicker from 'app/core/components/Picker/DescriptionPicker'; -import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore'; +import { dashboardPermissionLevels } from 'app/types/acl'; export interface Props { item: any; @@ -24,7 +24,7 @@ export default class DisabledPermissionListItem extends Component {
{}} value={item.permission} disabled={true} diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index 926438ffbc9..a0ea0279d30 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -6,11 +6,10 @@ import coreModule from 'app/core/core_module'; import { profiler } from 'app/core/profiler'; import appEvents from 'app/core/app_events'; import Drop from 'tether-drop'; -import { createStore } from 'app/stores/store'; import colors from 'app/core/utils/colors'; import { BackendSrv, setBackendSrv } from 'app/core/services/backend_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; -import { configureStore } from 'app/stores/configureStore'; +import { configureStore } from 'app/store/configureStore'; export class GrafanaCtrl { /** @ngInject */ @@ -28,7 +27,6 @@ export class GrafanaCtrl { // sets singleston instances for angular services so react components can access them configureStore(); setBackendSrv(backendSrv); - createStore({ backendSrv, datasourceSrv }); $scope.init = () => { $scope.contextSrv = contextSrv; diff --git a/public/app/core/services/bridge_srv.ts b/public/app/core/services/bridge_srv.ts index 29326794ac6..ee184c243ac 100644 --- a/public/app/core/services/bridge_srv.ts +++ b/public/app/core/services/bridge_srv.ts @@ -1,8 +1,6 @@ import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; -import { store } from 'app/stores/store'; -import { store as reduxStore } from 'app/stores/configureStore'; -import { reaction } from 'mobx'; +import { store } from 'app/store/configureStore'; import locationUtil from 'app/core/utils/location_util'; import { updateLocation } from 'app/core/actions'; @@ -18,12 +16,9 @@ export class BridgeSrv { init() { this.$rootScope.$on('$routeUpdate', (evt, data) => { const angularUrl = this.$location.url(); - if (store.view.currentUrl !== angularUrl) { - store.view.updatePathAndQuery(this.$location.path(), this.$location.search(), this.$route.current.params); - } - const state = reduxStore.getState(); + const state = store.getState(); if (state.location.url !== angularUrl) { - reduxStore.dispatch( + store.dispatch( updateLocation({ path: this.$location.path(), query: this.$location.search(), @@ -34,8 +29,7 @@ export class BridgeSrv { }); this.$rootScope.$on('$routeChangeSuccess', (evt, data) => { - store.view.updatePathAndQuery(this.$location.path(), this.$location.search(), this.$route.current.params); - reduxStore.dispatch( + store.dispatch( updateLocation({ path: this.$location.path(), query: this.$location.search(), @@ -44,24 +38,9 @@ export class BridgeSrv { ); }); - // listen for mobx store changes and update angular - reaction( - () => store.view.currentUrl, - currentUrl => { - const angularUrl = this.$location.url(); - const url = locationUtil.stripBaseFromUrl(currentUrl); - if (angularUrl !== url) { - this.$timeout(() => { - this.$location.url(url); - }); - console.log('store updating angular $location.url', url); - } - } - ); - // Listen for changes in redux location -> update angular location - reduxStore.subscribe(() => { - const state = reduxStore.getState(); + store.subscribe(() => { + const state = store.getState(); const angularUrl = this.$location.url(); const url = locationUtil.stripBaseFromUrl(state.location.url); if (angularUrl !== url) { diff --git a/public/app/features/dashboard/DashboardPermissions/DashboardPermissions.tsx b/public/app/features/dashboard/DashboardPermissions/DashboardPermissions.tsx index 6ea7ba12721..5651242a485 100644 --- a/public/app/features/dashboard/DashboardPermissions/DashboardPermissions.tsx +++ b/public/app/features/dashboard/DashboardPermissions/DashboardPermissions.tsx @@ -13,7 +13,7 @@ import { import PermissionList from 'app/core/components/PermissionList/PermissionList'; import AddPermission from 'app/core/components/PermissionList/AddPermission'; import PermissionsInfo from 'app/core/components/PermissionList/PermissionsInfo'; -import { store } from 'app/stores/configureStore'; +import { store } from 'app/store/configureStore'; export interface Props { dashboardId: number; @@ -65,7 +65,6 @@ export class DashboardPermissions extends PureComponent { render() { const { permissions, folder } = this.props; const { isAdding } = this.state; - console.log('DashboardPermissions', this.props); return (
diff --git a/public/app/features/plugins/ds_dashboards_ctrl.ts b/public/app/features/plugins/ds_dashboards_ctrl.ts index ed7800698b7..a0324215453 100644 --- a/public/app/features/plugins/ds_dashboards_ctrl.ts +++ b/public/app/features/plugins/ds_dashboards_ctrl.ts @@ -1,6 +1,7 @@ -import { toJS } from 'mobx'; import { coreModule } from 'app/core/core'; -import { store } from 'app/stores/store'; +import { store } from 'app/store/configureStore'; +import { getNavModel } from 'app/core/selectors/navModel'; +import { buildNavModel } from './state/navModel'; export class DataSourceDashboardsCtrl { datasourceMeta: any; @@ -9,11 +10,8 @@ export class DataSourceDashboardsCtrl { /** @ngInject */ constructor(private backendSrv, private $routeParams) { - if (store.nav.main === null) { - store.nav.load('cfg', 'datasources'); - } - - this.navModel = toJS(store.nav); + const state = store.getState(); + this.navModel = getNavModel(state.navIndex, 'datasources'); if (this.$routeParams.id) { this.getDatasourceById(this.$routeParams.id); @@ -30,8 +28,7 @@ export class DataSourceDashboardsCtrl { } updateNav() { - store.nav.initDatasourceEditNav(this.current, this.datasourceMeta, 'datasource-dashboards'); - this.navModel = toJS(store.nav); + this.navModel = buildNavModel(this.current, this.datasourceMeta, 'datasource-dashboards'); } getPluginInfo() { diff --git a/public/app/features/plugins/ds_edit_ctrl.ts b/public/app/features/plugins/ds_edit_ctrl.ts index 19889d3e26e..c223f444ef3 100644 --- a/public/app/features/plugins/ds_edit_ctrl.ts +++ b/public/app/features/plugins/ds_edit_ctrl.ts @@ -1,8 +1,9 @@ import _ from 'lodash'; -import { toJS } from 'mobx'; import config from 'app/core/config'; import { coreModule, appEvents } from 'app/core/core'; -import { store } from 'app/stores/store'; +import { store } from 'app/store/configureStore'; +import { getNavModel } from 'app/core/selectors/navModel'; +import { buildNavModel } from './state/navModel'; let datasourceTypes = []; @@ -31,11 +32,8 @@ export class DataSourceEditCtrl { /** @ngInject */ constructor(private $q, private backendSrv, private $routeParams, private $location, private datasourceSrv) { - if (store.nav.main === null) { - store.nav.load('cfg', 'datasources'); - } - - this.navModel = toJS(store.nav); + const state = store.getState(); + this.navModel = getNavModel(state.navIndex, 'datasources'); this.datasources = []; this.loadDatasourceTypes().then(() => { @@ -101,8 +99,7 @@ export class DataSourceEditCtrl { } updateNav() { - store.nav.initDatasourceEditNav(this.current, this.datasourceMeta, 'datasource-settings'); - this.navModel = toJS(store.nav); + this.navModel = buildNavModel(this.current, this.datasourceMeta, 'datasource-settings'); } typeChanged() { diff --git a/public/app/features/plugins/state/navModel.ts b/public/app/features/plugins/state/navModel.ts new file mode 100644 index 00000000000..852eb2806f9 --- /dev/null +++ b/public/app/features/plugins/state/navModel.ts @@ -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 }), + }; +} diff --git a/public/app/routes/ReactContainer.tsx b/public/app/routes/ReactContainer.tsx index 8a3d7e643f9..ed4d2d21827 100644 --- a/public/app/routes/ReactContainer.tsx +++ b/public/app/routes/ReactContainer.tsx @@ -1,22 +1,18 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Provider } from 'mobx-react'; -import { Provider as ReduxProvider } from 'react-redux'; +import { Provider } from 'react-redux'; import coreModule from 'app/core/core_module'; -import { store } from 'app/stores/store'; -import { store as reduxStore } from 'app/stores/configureStore'; +import { store } from 'app/store/configureStore'; import { BackendSrv } from 'app/core/services/backend_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; import { ContextSrv } from 'app/core/services/context_srv'; function WrapInProvider(store, Component, props) { return ( - - - - - + + + ); } diff --git a/public/app/stores/configureStore.ts b/public/app/store/configureStore.ts similarity index 100% rename from public/app/stores/configureStore.ts rename to public/app/store/configureStore.ts diff --git a/public/app/stores/NavStore/NavItem.ts b/public/app/stores/NavStore/NavItem.ts deleted file mode 100644 index 3e8a2a837b3..00000000000 --- a/public/app/stores/NavStore/NavItem.ts +++ /dev/null @@ -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, -}); diff --git a/public/app/stores/NavStore/NavStore.test.ts b/public/app/stores/NavStore/NavStore.test.ts deleted file mode 100644 index 43d4496c858..00000000000 --- a/public/app/stores/NavStore/NavStore.test.ts +++ /dev/null @@ -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); - }); -}); diff --git a/public/app/stores/NavStore/NavStore.ts b/public/app/stores/NavStore/NavStore.ts deleted file mode 100644 index f87cc486b41..00000000000 --- a/public/app/stores/NavStore/NavStore.ts +++ /dev/null @@ -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); - }, - })); diff --git a/public/app/stores/PermissionsStore/PermissionsStore.test.ts b/public/app/stores/PermissionsStore/PermissionsStore.test.ts deleted file mode 100644 index 6d88401e0d6..00000000000 --- a/public/app/stores/PermissionsStore/PermissionsStore.test.ts +++ /dev/null @@ -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'); - }); - }); -}); diff --git a/public/app/stores/PermissionsStore/PermissionsStore.ts b/public/app/stores/PermissionsStore/PermissionsStore.ts deleted file mode 100644 index d778a09443d..00000000000 --- a/public/app/stores/PermissionsStore/PermissionsStore.ts +++ /dev/null @@ -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) - ); -}; diff --git a/public/app/stores/PermissionsStore/PermissionsStoreItem.ts b/public/app/stores/PermissionsStore/PermissionsStoreItem.ts deleted file mode 100644 index c4873cb9c01..00000000000 --- a/public/app/stores/PermissionsStore/PermissionsStoreItem.ts +++ /dev/null @@ -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; - }, - })); diff --git a/public/app/stores/RootStore/RootStore.ts b/public/app/stores/RootStore/RootStore.ts deleted file mode 100644 index 68125fd1f4c..00000000000 --- a/public/app/stores/RootStore/RootStore.ts +++ /dev/null @@ -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 {} diff --git a/public/app/stores/ViewStore/ViewStore.test.ts b/public/app/stores/ViewStore/ViewStore.test.ts deleted file mode 100644 index 18fddd20fd6..00000000000 --- a/public/app/stores/ViewStore/ViewStore.test.ts +++ /dev/null @@ -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'); - }); -}); diff --git a/public/app/stores/ViewStore/ViewStore.ts b/public/app/stores/ViewStore/ViewStore.ts deleted file mode 100644 index 3af6737209c..00000000000 --- a/public/app/stores/ViewStore/ViewStore.ts +++ /dev/null @@ -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, - }; - }); diff --git a/public/app/stores/store.ts b/public/app/stores/store.ts deleted file mode 100644 index 10acbfe4907..00000000000 --- a/public/app/stores/store.ts +++ /dev/null @@ -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; -} diff --git a/public/app/types/datasources.ts b/public/app/types/datasources.ts new file mode 100644 index 00000000000..78ff7b0724c --- /dev/null +++ b/public/app/types/datasources.ts @@ -0,0 +1,7 @@ +export interface DataSource { + id: number; + orgId: number; + name: string; + typeLogoUrl: string; + type: string; +} diff --git a/public/app/types/folder.ts b/public/app/types/folders.ts similarity index 100% rename from public/app/types/folder.ts rename to public/app/types/folders.ts diff --git a/public/app/types/index.ts b/public/app/types/index.ts index 8fcfcc7e88d..778a1b21b55 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -2,9 +2,11 @@ import { Team, TeamsState, TeamState, TeamGroup, TeamMember } from './teams'; import { AlertRuleDTO, AlertRule, AlertRulesState } from './alerting'; import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './location'; import { NavModel, NavModelItem, NavIndex } from './navModel'; -import { FolderDTO, FolderState, FolderInfo } from './folder'; +import { FolderDTO, FolderState, FolderInfo } from './folders'; import { DashboardState } from './dashboard'; import { DashboardAcl, OrgRole, PermissionLevel } from './acl'; +import { DataSource } from './datasources'; +import { PluginMeta } from './plugins'; export { Team, @@ -29,6 +31,8 @@ export { DashboardAcl, OrgRole, PermissionLevel, + DataSource, + PluginMeta, }; export interface StoreState { diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts new file mode 100644 index 00000000000..d26085f8e73 --- /dev/null +++ b/public/app/types/plugins.ts @@ -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; + }; +} diff --git a/yarn.lock b/yarn.lock index 2b98ff32766..008ebc9a625 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5258,7 +5258,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0: +hoist-non-react-statics@^2.5.0: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" @@ -7593,24 +7593,6 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi dependencies: minimist "0.0.8" -mobx-react-devtools@^4.2.15: - version "4.2.15" - resolved "https://registry.yarnpkg.com/mobx-react-devtools/-/mobx-react-devtools-4.2.15.tgz#881c038fb83db4dffd1e72bbaf5374d26b2fdebb" - -mobx-react@^4.3.5: - version "4.4.3" - resolved "http://registry.npmjs.org/mobx-react/-/mobx-react-4.4.3.tgz#baa9ec41165ee35ae7b9df19bca10190f36f117e" - dependencies: - hoist-non-react-statics "^2.3.1" - -mobx-state-tree@^1.3.1: - version "1.4.0" - resolved "http://registry.npmjs.org/mobx-state-tree/-/mobx-state-tree-1.4.0.tgz#c914c855d5ec5c1c16e4ba6d6925679df42c8110" - -mobx@^3.4.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/mobx/-/mobx-3.6.2.tgz#fb9f5ff5090539a1ad54e75dc4c098b602693320" - mocha@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794"