diff --git a/frontend/src/activity/activity.services.js b/frontend/src/activity/activity.services.js deleted file mode 100644 index 6e9c7e735b713052b7266ec2553986b6cf2130ce..0000000000000000000000000000000000000000 --- a/frontend/src/activity/activity.services.js +++ /dev/null @@ -1,16 +0,0 @@ -var ActivityServices = angular.module('metabase.activity.services', ['ngResource', 'ngCookies']); - -ActivityServices.factory('Activity', ['$resource', '$cookies', function($resource, $cookies) { - return $resource('/api/activity', {}, { - list: { - method: 'GET', - isArray: true - }, - - recent_views: { - url: '/api/activity/recent_views', - method: 'GET', - isArray: true - } - }); -}]); diff --git a/frontend/src/admin/databases/databases.controllers.js b/frontend/src/admin/databases/databases.controllers.js index 180f8bd72137951d2c778aa7562529cad321ec34..4fbc74c322437297a9fd75c82d5308512aeee1b1 100644 --- a/frontend/src/admin/databases/databases.controllers.js +++ b/frontend/src/admin/databases/databases.controllers.js @@ -5,7 +5,7 @@ import DatabaseList from "./components/DatabaseList.jsx"; import DatabaseEdit from "./components/DatabaseEdit.jsx"; -var DatabasesControllers = angular.module('metabaseadmin.databases.controllers', ['metabase.metabase.services']); +var DatabasesControllers = angular.module('metabaseadmin.databases.controllers', ['metabase.services']); DatabasesControllers.controller('DatabaseList', ['$scope', '$routeParams', 'Metabase', function($scope, $routeParams, Metabase) { diff --git a/frontend/src/admin/metadata/components/MetadataEditor.jsx b/frontend/src/admin/metadata/components/MetadataEditor.jsx index 9ac0f8991cda5376ecf707b29c439bed46467748..b6e00133d2fde93525fffe1a2740fc33e3733eda 100644 --- a/frontend/src/admin/metadata/components/MetadataEditor.jsx +++ b/frontend/src/admin/metadata/components/MetadataEditor.jsx @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from "react"; +import _ from "underscore"; import MetadataHeader from './MetadataHeader.jsx'; import MetadataTableList from './MetadataTableList.jsx'; @@ -23,6 +24,7 @@ export default class MetadataEditor extends Component { databaseId: PropTypes.number, databases: PropTypes.array.isRequired, selectDatabase: PropTypes.func.isRequired, + databaseMetadata: PropTypes.object, tableId: PropTypes.number, tables: PropTypes.object.isRequired, selectTable: PropTypes.func.isRequired, @@ -63,7 +65,7 @@ export default class MetadataEditor extends Component { } render() { - var table = this.props.tables[this.props.tableId]; + var table = (this.props.databaseMetadata) ? _.findWhere(this.props.databaseMetadata.tables, {id: this.props.tableId}) : null; var content; if (table) { if (this.state.isShowingSchema) { @@ -100,7 +102,7 @@ export default class MetadataEditor extends Component { <div className="MetadataEditor-main flex flex-row flex-full mt2"> <MetadataTableList tableId={this.props.tableId} - tables={this.props.tables} + tables={(this.props.databaseMetadata) ? this.props.databaseMetadata.tables : []} selectTable={this.props.selectTable} /> {content} diff --git a/frontend/src/admin/metadata/metadata.controllers.js b/frontend/src/admin/metadata/metadata.controllers.js index 0c7e7480e1e2c2ca040d3c46d78a7b6ebc84cb6b..f8d8acaedbd3a02de5d9ce6d37df17afcf1c39a5 100644 --- a/frontend/src/admin/metadata/metadata.controllers.js +++ b/frontend/src/admin/metadata/metadata.controllers.js @@ -51,8 +51,8 @@ function($scope, $route, $routeParams, $location, $q, $timeout, databases, Metab $scope.tables = {}; if ($scope.databaseId != null) { try { - await loadTableMetadata(); await loadIdFields(); + await loadDatabaseMetadata(); $timeout(() => $scope.$digest()); } catch (error) { console.warn("error loading tables", error) @@ -60,14 +60,10 @@ function($scope, $route, $routeParams, $location, $q, $timeout, databases, Metab } }, true); - async function loadTableMetadata() { - var tables = await Metabase.db_tables({ 'dbId': $scope.databaseId }).$promise; - await* tables.map(async function(table) { - $scope.tables[table.id] = await Metabase.table_query_metadata({ - 'tableId': table.id, - 'include_sensitive_fields': true - }).$promise; - computeMetadataStrength($scope.tables[table.id]); + async function loadDatabaseMetadata() { + $scope.databaseMetadata = await Metabase.db_metadata({ 'dbId': $scope.databaseId }).$promise; + $scope.databaseMetadata.tables.map(function(table, index) { + table.metadataStrength = computeMetadataStrength(table); }); } @@ -94,7 +90,7 @@ function($scope, $route, $routeParams, $location, $q, $timeout, databases, Metab $scope.updateTable = function(table) { return Metabase.table_update(table).$promise.then(function(result) { _.each(result, (value, key) => { if (key.charAt(0) !== "$") { table[key] = value } }); - computeMetadataStrength($scope.tables[table.id]); + table.metadataStrength = computeMetadataStrength(table); $timeout(() => $scope.$digest()); }); }; @@ -102,7 +98,8 @@ function($scope, $route, $routeParams, $location, $q, $timeout, databases, Metab $scope.updateField = function(field) { return Metabase.field_update(field).$promise.then(function(result) { _.each(result, (value, key) => { if (key.charAt(0) !== "$") { field[key] = value } }); - computeMetadataStrength($scope.tables[field.table_id]); + let table = _.findWhere($scope.databaseMetadata.tables, {id: field.table_id}); + table.metadataStrength = computeMetadataStrength(table); return loadIdFields(); }).then(function() { $timeout(() => $scope.$digest()); @@ -126,7 +123,7 @@ function($scope, $route, $routeParams, $location, $q, $timeout, databases, Metab } }); - table.metadataStrength = completed / total; + return (completed / total); } $scope.updateFieldSpecialType = async function(field) { diff --git a/frontend/src/app.js b/frontend/src/app.js index 47906c94bcaef820b3ce2e6972d7ee94ab591586..8bccc0487b5c5184113fd6aed3357e506f20df40 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -3,7 +3,6 @@ var Metabase = angular.module('metabase', [ 'ngRoute', 'ngCookies', 'ui.bootstrap', // bootstrap LIKE widgets via angular directives - 'metabase.activity.services', 'metabase.auth', 'metabase.filters', 'metabase.directives', diff --git a/frontend/src/card/card.services.js b/frontend/src/card/card.services.js index d69e850f86ceede870d935e2ff141224b5ec23c0..537e0fb8566f089aef32f7735cffad02b48bd806 100644 --- a/frontend/src/card/card.services.js +++ b/frontend/src/card/card.services.js @@ -5,84 +5,6 @@ import { normal, harmony } from 'metabase/lib/colors' // Card Services var CardServices = angular.module('metabase.card.services', ['ngResource', 'ngCookies']); -CardServices.factory('Card', ['$resource', '$cookies', function($resource, $cookies) { - return $resource('/api/card/:cardId', {}, { - list: { - url: '/api/card/?f=:filterMode', - method: 'GET', - isArray: true - }, - create: { - url: '/api/card', - method: 'POST', - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - get: { - method: 'GET', - params: { - cardId: '@cardId' - } - }, - update: { - method: 'PUT', - params: { - cardId: '@id' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - delete: { - method: 'DELETE', - params: { - cardId: '@cardId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - isfavorite: { - url: '/api/card/:cardId/favorite', - method: 'GET', - params: { - cardId: '@cardId' - } - }, - favorite: { - url: '/api/card/:cardId/favorite', - method: 'POST', - params: { - cardId: '@cardId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - unfavorite: { - url: '/api/card/:cardId/favorite', - method: 'DELETE', - params: { - cardId: '@cardId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - } - }); -}]); - CardServices.service('VisualizationUtils', [function() { this.visualizationTypes = { scalar: { diff --git a/frontend/src/dashboard/dashboard.module.js b/frontend/src/dashboard/dashboard.module.js index 1f2b6ec3fd8481a0f7a79527fe8c02dcca6d4271..2f543640283a2dae68800079070a0710f27ff7a2 100644 --- a/frontend/src/dashboard/dashboard.module.js +++ b/frontend/src/dashboard/dashboard.module.js @@ -3,7 +3,6 @@ var Dashboard = angular.module('metabase.dashboard', [ 'ngRoute', 'metabase.directives', 'metabase.services', - 'metabase.dashboard.services', 'metabase.dashboard.controllers', 'metabase.card.services' ]); diff --git a/frontend/src/dashboard/dashboard.services.js b/frontend/src/dashboard/dashboard.services.js deleted file mode 100644 index a573847f7eac5bf122c01aeb6f6aae9e17752ac9..0000000000000000000000000000000000000000 --- a/frontend/src/dashboard/dashboard.services.js +++ /dev/null @@ -1,49 +0,0 @@ -// Dashboard Services -var DashboardServices = angular.module('metabase.dashboard.services', ['ngResource', 'ngCookies']); - -DashboardServices.factory('Dashboard', ['$resource', '$cookies', function($resource, $cookies) { - return $resource('/api/dashboard/:dashId', {}, { - list: { - url:'/api/dashboard?org=:orgId&f=:filterMode', - method:'GET', - isArray:true - }, - create: { - url:'/api/dashboard', - method:'POST', - headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}, - }, - get: { - method:'GET', - params:{dashId:'@dashId'}, - }, - update: { - method:'PUT', - params:{dashId:'@id'}, - headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}, - }, - delete: { - method:'DELETE', - params:{dashId:'@dashId'}, - headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}, - }, - addcard: { - url:'/api/dashboard/:dashId/cards', - method:'POST', - params:{dashId:'@dashId'}, - headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}, - }, - removecard: { - url:'/api/dashboard/:dashId/cards', - method:'DELETE', - params:{dashId:'@dashId'}, - headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}, - }, - reposition_cards: { - url:'/api/dashboard/:dashId/reposition', - method:'POST', - params:{dashId:'@dashId'}, - headers: {'X-CSRFToken': function() { return $cookies.csrftoken; }}, - } - }); -}]); diff --git a/frontend/src/metabase/metabase.services.js b/frontend/src/metabase/metabase.services.js deleted file mode 100644 index 4bf55f2b0dfcd010e8ca698606eb7c897ba71f7e..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/metabase.services.js +++ /dev/null @@ -1,271 +0,0 @@ -// Metabase Services -var MetabaseServices = angular.module('metabase.metabase.services', [ - 'ngResource', - 'ngCookies', - 'metabase.services' -]); - -MetabaseServices.factory('Metabase', ['$resource', '$cookies', 'MetabaseCore', function($resource, $cookies, MetabaseCore) { - return $resource('/api/meta', {}, { - db_list: { - url: '/api/database/?org=:orgId', - method: 'GET', - isArray: true - }, - db_create: { - url: '/api/database/', - method: 'POST', - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - db_add_sample_dataset: { - url: '/api/database/sample_dataset', - method: 'POST' - }, - db_get: { - url: '/api/database/:dbId', - method: 'GET', - params: { - dbId: '@dbId' - } - }, - db_update: { - url: '/api/database/:dbId', - method: 'PUT', - params: { - dbId: '@id' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - db_delete: { - url: '/api/database/:dbId', - method: 'DELETE', - params: { - dbId: '@dbId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - db_metadata: { - url: '/api/database/:dbId/metadata', - method: 'GET', - params: { - dbId: '@dbId' - } - }, - db_tables: { - url: '/api/database/:dbId/tables', - method: 'GET', - params: { - dbId: '@dbId' - }, - isArray: true - }, - db_idfields: { - url: '/api/database/:dbId/idfields', - method: 'GET', - params: { - dbId: '@dbId' - }, - isArray: true - }, - db_autocomplete_suggestions: { - url: '/api/database/:dbId/autocomplete_suggestions?prefix=:prefix', - method: 'GET', - params: { - dbId: '@dbId' - }, - isArray: true - }, - db_sync_metadata: { - url: '/api/database/:dbId/sync', - method: 'POST', - params: { - dbId: '@dbId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - table_list: { - url: '/api/table/', - method: 'GET', - params: { - tableId: '@tableId' - }, - isArray: true - }, - table_get: { - url: '/api/table/:tableId', - method: 'GET', - params: { - tableId: '@tableId' - } - }, - table_update: { - url: '/api/table/:tableId', - method: 'PUT', - params: { - tableId: '@id' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - table_fields: { - url: '/api/table/:tableId/fields', - method: 'GET', - params: { - tableId: '@tableId' - }, - isArray: true - }, - table_fks: { - url: '/api/table/:tableId/fks', - method: 'GET', - params: { - tableId: '@tableId' - }, - isArray: true - }, - table_reorder_fields: { - url: '/api/table/:tableId/reorder', - method: 'POST', - params: { - tableId: '@tableId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - }, - }, - table_query_metadata: { - url: '/api/table/:tableId/query_metadata', - method: 'GET', - params: { - dbId: '@tableId' - } - }, - table_sync_metadata: { - url: '/api/table/:tableId/sync', - method: 'POST', - params: { - tableId: '@tableId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - field_get: { - url: '/api/field/:fieldId', - method: 'GET', - params: { - fieldId: '@fieldId' - } - }, - field_summary: { - url: '/api/field/:fieldId/summary', - method: 'GET', - params: { - fieldId: '@fieldId' - }, - isArray: true - }, - field_values: { - url: '/api/field/:fieldId/values', - method: 'GET', - params: { - fieldId: '@fieldId' - } - }, - field_value_map_update: { - url: '/api/field/:fieldId/value_map_update', - method: 'POST', - params: { - fieldId: '@fieldId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - field_update: { - url: '/api/field/:fieldId', - method: 'PUT', - params: { - fieldId: '@id' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - field_foreignkeys: { - url: '/api/field/:fieldId/foreignkeys', - method: 'GET', - params: { - fieldId: '@fieldId' - }, - isArray: true - }, - field_addfk: { - url: '/api/field/:fieldId/foreignkeys', - method: 'POST', - params: { - fieldId: '@fieldId' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - }, - dataset: { - url: '/api/dataset', - method: 'POST', - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - } - } - }); -}]); - - - -MetabaseServices.factory('ForeignKey', ['$resource', '$cookies', function($resource, $cookies) { - return $resource('/api/foreignkey/:fkID', {}, { - delete: { - method: 'DELETE', - params: { - fkID: '@fkID' - }, - headers: { - 'X-CSRFToken': function() { - return $cookies.csrftoken; - } - }, - }, - - }); -}]); diff --git a/frontend/src/services.js b/frontend/src/services.js index 4d8bc5626b7fb8de203dc849666de224985450ba..71f8b817a0a7874dbffcd4b6a309d827fad93734 100644 --- a/frontend/src/services.js +++ b/frontend/src/services.js @@ -259,9 +259,342 @@ MetabaseServices.service('MetabaseCore', ['User', function(User) { }]); -// User Services +// API Services var CoreServices = angular.module('metabase.core.services', ['ngResource', 'ngCookies']); +CoreServices.factory('Activity', ['$resource', '$cookies', function($resource, $cookies) { + return $resource('/api/activity', {}, { + list: { + method: 'GET', + isArray: true + }, + recent_views: { + url: '/api/activity/recent_views', + method: 'GET', + isArray: true + } + }); +}]); + +CoreServices.factory('Card', ['$resource', '$cookies', function($resource, $cookies) { + return $resource('/api/card/:cardId', {}, { + list: { + url: '/api/card/?f=:filterMode', + method: 'GET', + isArray: true + }, + create: { + url: '/api/card', + method: 'POST' + }, + get: { + method: 'GET', + params: { + cardId: '@cardId' + } + }, + update: { + method: 'PUT', + params: { + cardId: '@id' + } + }, + delete: { + method: 'DELETE', + params: { + cardId: '@cardId' + } + }, + isfavorite: { + url: '/api/card/:cardId/favorite', + method: 'GET', + params: { + cardId: '@cardId' + } + }, + favorite: { + url: '/api/card/:cardId/favorite', + method: 'POST', + params: { + cardId: '@cardId' + } + }, + unfavorite: { + url: '/api/card/:cardId/favorite', + method: 'DELETE', + params: { + cardId: '@cardId' + } + } + }); +}]); + +CoreServices.factory('Dashboard', ['$resource', '$cookies', function($resource, $cookies) { + return $resource('/api/dashboard/:dashId', {}, { + list: { + url:'/api/dashboard?org=:orgId&f=:filterMode', + method:'GET', + isArray:true + }, + create: { + url:'/api/dashboard', + method:'POST' + }, + get: { + method:'GET', + params:{dashId:'@dashId'}, + }, + update: { + method:'PUT', + params:{dashId:'@id'} + }, + delete: { + method:'DELETE', + params:{dashId:'@dashId'} + }, + addcard: { + url:'/api/dashboard/:dashId/cards', + method:'POST', + params:{dashId:'@dashId'} + }, + removecard: { + url:'/api/dashboard/:dashId/cards', + method:'DELETE', + params:{dashId:'@dashId'} + }, + reposition_cards: { + url:'/api/dashboard/:dashId/reposition', + method:'POST', + params:{dashId:'@dashId'} + } + }); +}]); + +CoreServices.factory('ForeignKey', ['$resource', '$cookies', function($resource, $cookies) { + return $resource('/api/foreignkey/:fkID', {}, { + delete: { + method: 'DELETE', + params: { + fkID: '@fkID' + } + } + }); +}]); + +CoreServices.factory('Metabase', ['$resource', '$cookies', 'MetabaseCore', function($resource, $cookies, MetabaseCore) { + return $resource('/api/meta', {}, { + db_list: { + url: '/api/database/?org=:orgId', + method: 'GET', + isArray: true + }, + db_create: { + url: '/api/database/', + method: 'POST' + }, + db_add_sample_dataset: { + url: '/api/database/sample_dataset', + method: 'POST' + }, + db_get: { + url: '/api/database/:dbId', + method: 'GET', + params: { + dbId: '@dbId' + } + }, + db_update: { + url: '/api/database/:dbId', + method: 'PUT', + params: { + dbId: '@id' + } + }, + db_delete: { + url: '/api/database/:dbId', + method: 'DELETE', + params: { + dbId: '@dbId' + } + }, + db_metadata: { + url: '/api/database/:dbId/metadata', + method: 'GET', + params: { + dbId: '@dbId' + } + }, + db_tables: { + url: '/api/database/:dbId/tables', + method: 'GET', + params: { + dbId: '@dbId' + }, + isArray: true + }, + db_idfields: { + url: '/api/database/:dbId/idfields', + method: 'GET', + params: { + dbId: '@dbId' + }, + isArray: true + }, + db_autocomplete_suggestions: { + url: '/api/database/:dbId/autocomplete_suggestions?prefix=:prefix', + method: 'GET', + params: { + dbId: '@dbId' + }, + isArray: true + }, + db_sync_metadata: { + url: '/api/database/:dbId/sync', + method: 'POST', + params: { + dbId: '@dbId' + } + }, + table_list: { + url: '/api/table/', + method: 'GET', + params: { + tableId: '@tableId' + }, + isArray: true + }, + table_get: { + url: '/api/table/:tableId', + method: 'GET', + params: { + tableId: '@tableId' + } + }, + table_update: { + url: '/api/table/:tableId', + method: 'PUT', + params: { + tableId: '@id' + } + }, + table_fields: { + url: '/api/table/:tableId/fields', + method: 'GET', + params: { + tableId: '@tableId' + }, + isArray: true + }, + table_fks: { + url: '/api/table/:tableId/fks', + method: 'GET', + params: { + tableId: '@tableId' + }, + isArray: true + }, + table_reorder_fields: { + url: '/api/table/:tableId/reorder', + method: 'POST', + params: { + tableId: '@tableId' + } + }, + table_query_metadata: { + url: '/api/table/:tableId/query_metadata', + method: 'GET', + params: { + dbId: '@tableId' + } + }, + table_sync_metadata: { + url: '/api/table/:tableId/sync', + method: 'POST', + params: { + tableId: '@tableId' + } + }, + field_get: { + url: '/api/field/:fieldId', + method: 'GET', + params: { + fieldId: '@fieldId' + } + }, + field_summary: { + url: '/api/field/:fieldId/summary', + method: 'GET', + params: { + fieldId: '@fieldId' + }, + isArray: true + }, + field_values: { + url: '/api/field/:fieldId/values', + method: 'GET', + params: { + fieldId: '@fieldId' + } + }, + field_value_map_update: { + url: '/api/field/:fieldId/value_map_update', + method: 'POST', + params: { + fieldId: '@fieldId' + } + }, + field_update: { + url: '/api/field/:fieldId', + method: 'PUT', + params: { + fieldId: '@id' + } + }, + field_foreignkeys: { + url: '/api/field/:fieldId/foreignkeys', + method: 'GET', + params: { + fieldId: '@fieldId' + }, + isArray: true + }, + field_addfk: { + url: '/api/field/:fieldId/foreignkeys', + method: 'POST', + params: { + fieldId: '@fieldId' + } + }, + dataset: { + url: '/api/dataset', + method: 'POST' + } + }); +}]); + +CoreServices.factory('Revision', ['$resource', function($resource) { + return $resource('/api/revision', {}, { + list: { + url: '/api/revision', + method: 'GET', + isArray: true, + params: { + 'entity': '@entity', + 'id': '@id' + } + }, + revert: { + url: '/api/revision/revert', + method: 'POST', + params: { + 'entity': '@entity', + 'id': '@id', + 'revision_id': '@revision_id' + } + } + }); +}]); + CoreServices.factory('Session', ['$resource', '$cookies', function($resource, $cookies) { return $resource('/api/session/', {}, { create: { @@ -290,6 +623,43 @@ CoreServices.factory('Session', ['$resource', '$cookies', function($resource, $c }); }]); +CoreServices.factory('Settings', ['$resource', function($resource) { + return $resource('/api/setting', {}, { + list: { + url: '/api/setting', + method: 'GET', + isArray: true, + }, + // POST endpoint handles create + update in this case + put: { + url: '/api/setting/:key', + method: 'PUT', + params: { + key: '@key' + } + }, + delete: { + url: '/api/setting/:key', + method: 'DELETE', + params: { + key: '@key' + } + } + }); +}]); + +CoreServices.factory('Setup', ['$resource', '$cookies', function($resource, $cookies) { + return $resource('/api/setup/', {}, { + create: { + method: 'POST' + }, + validate_db: { + url: '/api/setup/validate', + method: 'POST' + } + }); +}]); + CoreServices.factory('User', ['$resource', '$cookies', function($resource, $cookies) { return $resource('/api/user/:userId', {}, { create: { @@ -343,57 +713,6 @@ CoreServices.factory('User', ['$resource', '$cookies', function($resource, $cook }); }]); -CoreServices.factory('Settings', ['$resource', function($resource) { - return $resource('/api/setting', {}, { - list: { - url: '/api/setting', - method: 'GET', - isArray: true, - }, - - // POST endpoint handles create + update in this case - put: { - url: '/api/setting/:key', - method: 'PUT', - params: { - key: '@key' - } - }, - - delete: { - url: '/api/setting/:key', - method: 'DELETE', - params: { - key: '@key' - } - } - }); -}]); - -CoreServices.factory('Revision', ['$resource', function($resource) { - return $resource('/api/revision', {}, { - list: { - url: '/api/revision', - method: 'GET', - isArray: true, - params: { - 'entity': '@entity', - 'id': '@id' - } - }, - - revert: { - url: '/api/revision/revert', - method: 'POST', - params: { - 'entity': '@entity', - 'id': '@id', - 'revision_id': '@revision_id' - } - } - }); -}]); - CoreServices.factory('Util', ['$resource', '$cookies', function($resource, $cookies) { return $resource('/api/util/', {}, { password_check: { diff --git a/frontend/src/setup/setup.controllers.js b/frontend/src/setup/setup.controllers.js index d3e68ff42ac1d3ac276a862bdb9b708fb03a38b1..f1e5a4dde42a59222bceeaa3a0d3c42443be1a35 100644 --- a/frontend/src/setup/setup.controllers.js +++ b/frontend/src/setup/setup.controllers.js @@ -16,7 +16,7 @@ const finalCreateStore = compose( const reducer = combineReducers(reducers); -var SetupControllers = angular.module('metabase.setup.controllers', ['metabase.setup.services']); +var SetupControllers = angular.module('metabase.setup.controllers', ['metabase.services']); SetupControllers.controller('SetupController', ['$scope', '$location', '$timeout', 'ipCookie', function($scope, $location, $timeout, ipCookie) { $scope.Component = SetupApp; $scope.props = { diff --git a/frontend/src/setup/setup.services.js b/frontend/src/setup/setup.services.js deleted file mode 100644 index d5e5297614f63061841ea1f978da5c99cbe456f8..0000000000000000000000000000000000000000 --- a/frontend/src/setup/setup.services.js +++ /dev/null @@ -1,15 +0,0 @@ -var SetupServices = angular.module('metabase.setup.services', ['ngResource', 'ngCookies']); - -SetupServices.factory('Setup', ['$resource', '$cookies', function($resource, $cookies) { - return $resource('/api/setup/', {}, { - create: { - method: 'POST' - }, - - validate_db: { - url: '/api/setup/validate', - method: 'POST' - } - }); -}]); - diff --git a/frontend/test/unit/services.spec.js b/frontend/test/unit/services.spec.js index 940edfdd053cfcf82adefaec9ff75aaecb5b6201..a62fbb60749b120022a044140259d4d80cbb3dc8 100644 --- a/frontend/test/unit/services.spec.js +++ b/frontend/test/unit/services.spec.js @@ -1,8 +1,7 @@ import 'metabase/services'; -import 'metabase/metabase/metabase.services'; -describe('metabase.metabase.services', function() { - beforeEach(angular.mock.module('metabase.metabase.services')); +describe('metabase.services', function() { + beforeEach(angular.mock.module('metabase.services')); describe('Metabase', function() { it('should return empty list of databases', inject(function(Metabase, $httpBackend) { diff --git a/src/metabase/driver/sqlserver.clj b/src/metabase/driver/sqlserver.clj index 75bb8492850eebb19b740f898e21eeb3ba6e7fcb..acb9b9ef27897f876d3ad9a0ef6d5288827e13a3 100644 --- a/src/metabase/driver/sqlserver.clj +++ b/src/metabase/driver/sqlserver.clj @@ -47,13 +47,15 @@ :xml :UnknownField (keyword "int identity") :IntegerField}) ; auto-incrementing integer (ie pk) field -(defn- connection-details->spec [details] +(defn- connection-details->spec [{:keys [instance], :as details}] (-> (kdb/mssql details) ;; swap out Microsoft Driver details for jTDS ones (assoc :classname "net.sourceforge.jtds.jdbc.Driver" :subprotocol "jtds:sqlserver") ;; adjust the connection URL to match up with the jTDS format (see http://jtds.sourceforge.net/faq.html#urlFormat) - (update :subname #(s/replace % #";database=" "/")))) + ;; and add the ;instance= option if applicable + (update :subname #(cond-> (s/replace % #";database=" "/") + (seq instance) (str ";instance=" instance))))) ;; See also the [jTDS SQL <-> Java types table](http://jtds.sourceforge.net/typemap.html) (defn- date [unit field-or-value] @@ -114,15 +116,18 @@ (-> (sql-driver {:driver-name "SQL Server" :details-fields [{:name "host" :display-name "Host" - :default "localhost"} + :default "localhost"} {:name "port" :display-name "Port" :type :integer :default 1433} - {:name "dbname" + {:name "db" :display-name "Database name" - :placeholder "birds_of_the_word" + :placeholder "BirdsOfTheWorld" :required true} + {:name "instance" + :display-name "Database instance name" + :placeholder "N/A"} {:name "user" :display-name "Database username" :placeholder "What username do you use to login to the database?" diff --git a/src/metabase/models/field_values.clj b/src/metabase/models/field_values.clj index 2cffb9b11342db9984d477b2951e83ee71260530..bca255ecb3e14577929741536e76731c56ab70c6 100644 --- a/src/metabase/models/field_values.clj +++ b/src/metabase/models/field_values.clj @@ -33,6 +33,7 @@ (contains? field :base_type) (contains? field :special_type)]} (and (not= (keyword field_type) :sensitive) + (not (contains? #{:DateField :DateTimeField :TimeField} (keyword base_type))) (or (contains? #{:category :city :state :country} (keyword special_type)) (= (keyword base_type) :BooleanField)))) diff --git a/test/metabase/api/table_test.clj b/test/metabase/api/table_test.clj index 9d5462b85286f11b96880c62d6fb9d3af7b16685..30d2ca68e19ce4def613ccc884e393c76c542873 100644 --- a/test/metabase/api/table_test.clj +++ b/test/metabase/api/table_test.clj @@ -292,10 +292,7 @@ :active true :id (id :users) :db_id (id) - :field_values {(keyword (str (id :users :last_login))) - user-last-login-date-strs - - (keyword (str (id :users :name))) + :field_values {(keyword (str (id :users :name))) ["Broen Olujimi" "Conchúr Tihomir" "Dwight Gresham" @@ -390,10 +387,7 @@ :active true :id (id :users) :db_id (id) - :field_values {(keyword (str (id :users :last_login))) - user-last-login-date-strs - - (keyword (str (id :users :name))) + :field_values {(keyword (str (id :users :name))) ["Broen Olujimi" "Conchúr Tihomir" "Dwight Gresham" diff --git a/test/metabase/models/field_test.clj b/test/metabase/models/field_test.clj index 05bb383ab55ad1cf11b88e23d78484e4f178e88c..15f299d48d19859fd412d14f2c2aec81588e746d 100644 --- a/test/metabase/models/field_test.clj +++ b/test/metabase/models/field_test.clj @@ -5,6 +5,60 @@ [field-values :refer :all]) [metabase.test.data :refer :all])) + +;; field-should-have-field-values? + +;; sensitive fields should always be excluded +(expect false (field-should-have-field-values? {:base_type :BooleanField + :special_type :category + :field_type :sensitive})) +;; date/time based fields should always be excluded +(expect false (field-should-have-field-values? {:base_type :DateField + :special_type :category + :field_type :dimension})) +(expect false (field-should-have-field-values? {:base_type :DateTimeField + :special_type :category + :field_type :dimension})) +(expect false (field-should-have-field-values? {:base_type :TimeField + :special_type :category + :field_type :dimension})) +;; most special types should be excluded +(expect false (field-should-have-field-values? {:base_type :CharField + :special_type :image + :field_type :dimension})) +(expect false (field-should-have-field-values? {:base_type :CharField + :special_type :id + :field_type :dimension})) +(expect false (field-should-have-field-values? {:base_type :CharField + :special_type :fk + :field_type :dimension})) +(expect false (field-should-have-field-values? {:base_type :CharField + :special_type :latitude + :field_type :dimension})) +(expect false (field-should-have-field-values? {:base_type :CharField + :special_type :number + :field_type :dimension})) +(expect false (field-should-have-field-values? {:base_type :CharField + :special_type :timestamp_milliseconds + :field_type :dimension})) +;; boolean fields + category/city/state/country fields are g2g +(expect true (field-should-have-field-values? {:base_type :BooleanField + :special_type :number + :field_type :dimension})) +(expect true (field-should-have-field-values? {:base_type :CharField + :special_type :category + :field_type :dimension})) +(expect true (field-should-have-field-values? {:base_type :TextField + :special_type :city + :field_type :dimension})) +(expect true (field-should-have-field-values? {:base_type :TextField + :special_type :state + :field_type :dimension})) +(expect true (field-should-have-field-values? {:base_type :TextField + :special_type :country + :field_type :dimension})) + + ;; Check that setting a Field's special_type to :category will cause a corresponding FieldValues to be created asynchronously (expect [nil