Skip to content

Commit

Permalink
Merge branch 'master' of github.com:metabase/metabase into pulse-cond…
Browse files Browse the repository at this point in the history
…itional-formatting
  • Loading branch information
tlrobinson committed Jul 18, 2018
2 parents 8d000dc + c0d3239 commit 0af5ad5
Show file tree
Hide file tree
Showing 171 changed files with 3,407 additions and 2,023 deletions.
20 changes: 18 additions & 2 deletions frontend/src/metabase-lib/lib/Question.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import type {
} from "metabase/meta/types/Card";

import { MetabaseApi, CardApi } from "metabase/services";
import Questions from "metabase/entities/questions";

import AtomicQuery from "metabase-lib/lib/queries/AtomicQuery";

import type { Dataset } from "metabase/meta/types/Dataset";
Expand Down Expand Up @@ -471,16 +473,30 @@ export default class Question {
}
}
// NOTE: prefer `reduxCreate` so the store is automatically updated
async apiCreate() {
const createdCard = await CardApi.create(this.card());
const createdCard = await Questions.api.create(this.card());
return this.setCard(createdCard);
}
// NOTE: prefer `reduxUpdate` so the store is automatically updated
async apiUpdate() {
const updatedCard = await CardApi.update(this.card());
const updatedCard = await Questions.api.update(this.card());
return this.setCard(updatedCard);
}

async reduxCreate(dispatch) {
const { payload } = await dispatch(Questions.actions.create(this.card()));
return this.setCard(payload.entities.questions[payload.result]);
}

async reduxUpdate(dispatch) {
const { payload } = await dispatch(
Questions.actions.update({ id: this.id() }, this.card()),
);
return this.setCard(payload.entities.questions[payload.result]);
}

// TODO: Fix incorrect Flow signature
parameters(): ParameterObject[] {
return getParametersWithExtras(this.card(), this._parameterValues);
Expand Down
43 changes: 30 additions & 13 deletions frontend/src/metabase-lib/lib/queries/StructuredQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -635,33 +635,40 @@ export default class StructuredQuery extends AtomicQuery {
return this._updateQuery(Q.removeExpression, arguments);
}

// FIELD OPTIONS
// FIELDS
/**
* Returns dimension options that can appear in the `fields` clause
*/
fieldsOptions(dimensionFilter = () => true): DimensionOptions {
if (this.isBareRows() && this.breakouts().length === 0) {
return this.dimensionOptions(dimensionFilter);
}
// TODO: allow adding fields connected by broken out PKs?
return { count: 0, dimensions: [], fks: [] };
}

// DIMENSION OPTIONS

// TODO Atte Keinänen 6/18/17: Refactor to dimensionOptions which takes a dimensionFilter
// See aggregationFieldOptions for an explanation why that covers more use cases
fieldOptions(fieldFilter = () => true): DimensionOptions {
const fieldOptions = {
dimensionOptions(dimensionFilter = () => true): DimensionOptions {
const dimensionOptions = {
count: 0,
fks: [],
dimensions: [],
};

const table = this.tableMetadata();
if (table) {
const dimensionFilter = dimension => {
const field = dimension.field && dimension.field();
return !field || (field.isDimension() && fieldFilter(field));
};

const dimensionIsFKReference = dimension =>
dimension.field && dimension.field() && dimension.field().isFK();

const filteredNonFKDimensions = this.dimensions().filter(dimensionFilter);
// .filter(d => !dimensionIsFKReference(d));

for (const dimension of filteredNonFKDimensions) {
fieldOptions.count++;
fieldOptions.dimensions.push(dimension);
dimensionOptions.count++;
dimensionOptions.dimensions.push(dimension);
}

const fkDimensions = this.dimensions().filter(dimensionIsFKReference);
Expand All @@ -671,8 +678,8 @@ export default class StructuredQuery extends AtomicQuery {
.filter(dimensionFilter);

if (fkDimensions.length > 0) {
fieldOptions.count += fkDimensions.length;
fieldOptions.fks.push({
dimensionOptions.count += fkDimensions.length;
dimensionOptions.fks.push({
field: dimension.field(),
dimension: dimension,
dimensions: fkDimensions,
Expand All @@ -681,7 +688,17 @@ export default class StructuredQuery extends AtomicQuery {
}
}

return fieldOptions;
return dimensionOptions;
}

// FIELD OPTIONS

fieldOptions(fieldFilter = () => true) {
const dimensionFilter = dimension => {
const field = dimension.field && dimension.field();
return !field || (field.isDimension() && fieldFilter(field));
};
return this.dimensionOptions(dimensionFilter);
}

// DIMENSIONS
Expand Down
14 changes: 8 additions & 6 deletions frontend/src/metabase/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React, { Component } from "react";
import { connect } from "react-redux";

import ScrollToTop from "metabase/hoc/ScrollToTop";
import Navbar from "metabase/nav/containers/Navbar.jsx";

import UndoListing from "metabase/containers/UndoListing";
Expand Down Expand Up @@ -57,11 +57,13 @@ export default class App extends Component {
}

return (
<div className="relative">
<Navbar location={location} />
{errorPage ? getErrorComponent(errorPage) : children}
<UndoListing />
</div>
<ScrollToTop>
<div className="relative">
<Navbar location={location} />
{errorPage ? getErrorComponent(errorPage) : children}
<UndoListing />
</div>
</ScrollToTop>
);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/* @flow weak */

import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import title from "metabase/hoc/Title";
import cx from "classnames";
import { t } from "c-3po";

import MetabaseSettings from "metabase/lib/settings";
import DeleteDatabaseModal from "../components/DeleteDatabaseModal.jsx";
import DatabaseEditForms from "../components/DatabaseEditForms.jsx";
import DatabaseSchedulingForm from "../components/DatabaseSchedulingForm";
import { t } from "c-3po";
import ActionButton from "metabase/components/ActionButton.jsx";
import Breadcrumbs from "metabase/components/Breadcrumbs.jsx";
import ModalWithTrigger from "metabase/components/ModalWithTrigger.jsx";
Expand Down Expand Up @@ -85,6 +87,10 @@ const mapDispatchToProps = {
@connect(mapStateToProps, mapDispatchToProps)
@title(({ database }) => database && database.name)
export default class DatabaseEditApp extends Component {
state: {
currentTab: "connection" | "scheduling",
};

constructor(props, context) {
super(props, context);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
/* @flow weak */

import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { Link } from "react-router";
import { t } from "c-3po";

import cx from "classnames";
import MetabaseSettings from "metabase/lib/settings";

import ModalWithTrigger from "metabase/components/ModalWithTrigger.jsx";
import LoadingSpinner from "metabase/components/LoadingSpinner.jsx";
import { t } from "c-3po";
import FormMessage from "metabase/components/form/FormMessage";

import CreatedDatabaseModal from "../components/CreatedDatabaseModal.jsx";
import DeleteDatabaseModal from "../components/DeleteDatabaseModal.jsx";

import {
getDatabasesSorted,
hasSampleDataset,
getDeletes,
getDeletionError,
} from "../selectors";
import * as databaseActions from "../database";
import FormMessage from "metabase/components/form/FormMessage";
import Databases from "metabase/entities/databases";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";

const mapStateToProps = (state, props) => {
return {
created: props.location.query.created,
databases: getDatabasesSorted(state),
hasSampleDataset: hasSampleDataset(state),
engines: MetabaseSettings.get("engines"),
deletes: getDeletes(state),
deletionError: getDeletionError(state),
};
};
import { getDeletes, getDeletionError } from "../selectors";
import { deleteDatabase, addSampleDataset } from "../database";

const mapStateToProps = (state, props) => ({
hasSampleDataset: Databases.selectors.getHasSampleDataset(state),

created: props.location.query.created,
engines: MetabaseSettings.get("engines"),

deletes: getDeletes(state),
deletionError: getDeletionError(state),
});

const mapDispatchToProps = {
...databaseActions,
fetchDatabases: Databases.actions.fetchList,
// NOTE: still uses deleteDatabase from metabaseadmin/databases/databases.js
// rather than metabase/entities/databases since it updates deletes/deletionError
deleteDatabase: deleteDatabase,
addSampleDataset: addSampleDataset,
};

@entityListLoader({ entityType: "databases" })
@connect(mapStateToProps, mapDispatchToProps)
export default class DatabaseList extends Component {
static propTypes = {
Expand All @@ -45,10 +51,6 @@ export default class DatabaseList extends Component {
deletionError: PropTypes.object,
};

componentWillMount() {
this.props.fetchDatabases();
}

componentWillReceiveProps(newProps) {
if (!this.props.created && newProps.created) {
this.refs.createdDatabaseModal.open();
Expand Down
46 changes: 13 additions & 33 deletions frontend/src/metabase/admin/databases/database.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _ from "underscore";
/* @flow weak */

import { createAction } from "redux-actions";
import {
Expand All @@ -12,6 +12,7 @@ import MetabaseAnalytics from "metabase/lib/analytics";
import MetabaseSettings from "metabase/lib/settings";

import { MetabaseApi } from "metabase/services";
import Databases from "metabase/entities/databases";

// Default schedules for db sync and deep analysis
export const DEFAULT_SCHEDULES = {
Expand Down Expand Up @@ -69,22 +70,13 @@ export const CLEAR_FORM_STATE = "metabase/admin/databases/CLEAR_FORM_STATE";
export const MIGRATE_TO_NEW_SCHEDULING_SETTINGS =
"metabase/admin/databases/MIGRATE_TO_NEW_SCHEDULING_SETTINGS";

// NOTE: some but not all of these actions have been migrated to use metabase/entities/databases

export const reset = createAction(RESET);

// selectEngine (uiControl)
export const selectEngine = createAction(SELECT_ENGINE);

// fetchDatabases
export const fetchDatabases = createThunkAction(FETCH_DATABASES, function() {
return async function(dispatch, getState) {
try {
return await MetabaseApi.db_list();
} catch (error) {
console.error("error fetching databases", error);
}
};
});

// Migrates old "Enable in-depth database analysis" option to new "Let me choose when Metabase syncs and scans" option
// Migration is run as a separate action because that makes it easy to track in tests
const migrateDatabaseToNewSchedulingSettings = database => {
Expand Down Expand Up @@ -112,7 +104,10 @@ export const initializeDatabase = function(databaseId) {
return async function(dispatch, getState) {
if (databaseId) {
try {
const database = await MetabaseApi.db_get({ dbId: databaseId });
const { payload } = await dispatch(
Databases.actions.fetch({ id: databaseId }, { reload: true }),
);
const database = payload.entities.databases[databaseId];
dispatch.action(INITIALIZE_DATABASE, database);

// If the new scheduling toggle isn't set, run the migration
Expand Down Expand Up @@ -196,13 +191,10 @@ export const createDatabase = function(database) {
return async function(dispatch, getState) {
try {
dispatch.action(CREATE_DATABASE_STARTED, {});
const createdDatabase = await MetabaseApi.db_create(database);
const { payload } = await dispatch(Databases.actions.create(database));
const createdDatabase = payload.entities.databases[payload.result];
MetabaseAnalytics.trackEvent("Databases", "Create", database.engine);

// update the db metadata already here because otherwise there will be a gap between "Adding..." status
// and seeing the db that was just added
await dispatch(fetchDatabases());

dispatch.action(CREATE_DATABASE);
dispatch(push("/admin/databases?created=" + createdDatabase.id));
} catch (error) {
Expand All @@ -221,7 +213,8 @@ export const updateDatabase = function(database) {
return async function(dispatch, getState) {
try {
dispatch.action(UPDATE_DATABASE_STARTED, { database });
const savedDatabase = await MetabaseApi.db_update(database);
const { payload } = await dispatch(Databases.actions.update(database));
const savedDatabase = payload.entities.databases[payload.result];
MetabaseAnalytics.trackEvent("Databases", "Update", database.engine);

dispatch.action(UPDATE_DATABASE, { database: savedDatabase });
Expand Down Expand Up @@ -270,7 +263,7 @@ export const deleteDatabase = function(databaseId, isDetailView = true) {
try {
dispatch.action(DELETE_DATABASE_STARTED, { databaseId });
dispatch(push("/admin/databases/"));
await MetabaseApi.db_delete({ dbId: databaseId });
await dispatch(Databases.actions.delete({ id: databaseId }));
MetabaseAnalytics.trackEvent(
"Databases",
"Delete",
Expand Down Expand Up @@ -334,18 +327,6 @@ export const discardSavedFieldValues = createThunkAction(

// reducers

const databases = handleActions(
{
[FETCH_DATABASES]: { next: (state, { payload }) => payload },
[ADD_SAMPLE_DATASET]: {
next: (state, { payload }) => (payload ? [...state, payload] : state),
},
[DELETE_DATABASE]: (state, { payload: { databaseId } }) =>
databaseId ? _.reject(state, d => d.id === databaseId) : state,
},
null,
);

const editingDatabase = handleActions(
{
[RESET]: () => null,
Expand Down Expand Up @@ -420,7 +401,6 @@ const formState = handleActions(
);

export default combineReducers({
databases,
editingDatabase,
deletionError,
databaseCreationStep,
Expand Down
15 changes: 1 addition & 14 deletions frontend/src/metabase/admin/databases/selectors.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
/* @flow weak */

import _ from "underscore";
import { createSelector } from "reselect";

// Database List
export const databases = state => state.admin.databases.databases;

export const getDatabasesSorted = createSelector([databases], databases =>
_.sortBy(databases, "name"),
);

export const hasSampleDataset = createSelector([databases], databases =>
_.some(databases, d => d.is_sample),
);

// Database Edit
export const getEditingDatabase = state =>
state.admin.databases.editingDatabase;
export const getFormState = state => state.admin.databases.formState;
export const getDatabaseCreationStep = state =>
state.admin.databases.databaseCreationStep;

// Database List
export const getDeletes = state => state.admin.databases.deletes;
export const getDeletionError = state => state.admin.databases.deletionError;
Loading

0 comments on commit 0af5ad5

Please sign in to comment.