Example #1
0
 return async (dispatch, getState) => {
     if (favorited) {
         await CardApi.favorite({ cardId });
     } else {
         await CardApi.unfavorite({ cardId });
     }
     MetabaseAnalytics.trackEvent("Questions", favorited ? "Favorite" : "Unfavorite");
     return { id: cardId, favorite: favorited };
 }
Example #2
0
  afterAll(async () => {
    PulseApi.form_input = normalFormInput

    await CardApi.delete({ cardId: questionCount.id() })
    await CardApi.delete({ cardId: questionRaw.id() })

    for (const pulse of await PulseApi.list()) {
      await PulseApi.delete({ pulseId: pulse.id })
    }
  })
            it("Should see a newly asked question in its questions list", async () => {
                var card = await CardApi.create(segmentCardDef)

                expect(card.name).toBe(segmentCardDef.name);
                
                await CardApi.delete({cardId: card.id})

                const store = await createTestStore()    
                store.pushPath("/reference/segments/"+segmentIds[0]+'/questions');
                mount(store.connectContainer(<SegmentQuestionsContainer />));
                await store.waitForActions([FETCH_SEGMENT_TABLE, LOAD_ENTITIES])
            })
Example #4
0
 return async (dispatch, getState) => {
     if (cardId == null) {
         // bulk archive
         let selected = getSelectedEntities(getState()).filter(item => item.archived !== archived);
         selected.map(item => dispatch(setArchived(item.id, archived)));
         // TODO: errors
         if (undoable) {
             dispatch(addUndo(createUndo(
                 archived ? "archived" : "unarchived",
                 selected.map(item => setArchived(item.id, !archived))
             )));
             MetabaseAnalytics.trackEvent("Questions", archived ? "Bulk Archive" : "Bulk Unarchive", selected.length);
         }
     } else {
         let card = {
             ...getState().questions.entities.cards[cardId],
             archived: archived
         };
         let response = await CardApi.update(card);
         if (undoable) {
             dispatch(addUndo(createUndo(
                 archived ? "archived" : "unarchived",
                 [setArchived(cardId, !archived)],
                 !archived && card.collection
             )));
             MetabaseAnalytics.trackEvent("Questions", archived ? "Archive" : "Unarchive");
         }
         return response;
     }
 }
Example #5
0
 return async function(dispatch, getState) {
     let cards = await CardApi.list({ f: filterMode });
     for (var c of cards) {
         c.updated_at = moment(c.updated_at);
     }
     return normalize(cards, [card]);
 };
Example #6
0
 return async (dispatch, getState) => {
     if (cardId == null) {
         // bulk label
         let selected = getSelectedEntities(getState());
         selected.map(item => dispatch(setLabeled(item.id, labelId, labeled)));
         // TODO: errors
         if (undoable) {
             dispatch(addUndo(createUndo(
                 labeled ? "labeled" : "unlabeled",
                 selected.map(item => setLabeled(item.id, labelId, !labeled))
             )));
             MetabaseAnalytics.trackEvent("Questions", labeled ? "Bulk Apply Label" : "Bulk Remove Label", selected.length);
         }
     } else {
         const state = getState();
         const labelSlug = getIn(state.questions, ["entities", "labels", labelId, "slug"]);
         const labels = getIn(state.questions, ["entities", "cards", cardId, "labels"]);
         const newLabels = labels.filter(id => id !== labelId);
         if (labeled) {
             newLabels.push(labelId);
         }
         if (labels.length !== newLabels.length) {
             await CardApi.updateLabels({ cardId, label_ids: newLabels });
             if (undoable) {
                 dispatch(addUndo(createUndo(
                     labeled ? "labeled" : "unlabeled",
                     [setLabeled(cardId, labelId, !labeled)]
                 )));
                 MetabaseAnalytics.trackEvent("Questions", labeled ? "Apply Label" : "Remove Label");
             }
             return { id: cardId, labels: newLabels, _changedLabelSlug: labelSlug, _changedLabeled: labeled };
         }
     }
 }
      it("Should see a newly asked question in its questions list", async () => {
        let card = await CardApi.create(metricCardDef);
        expect(card.name).toBe(metricCardDef.name);

        try {
          // see that there is a new question on the metric's questions page
          const store = await createTestStore();

          store.pushPath("/reference/metrics/" + metricIds[0] + "/questions");
          mount(store.connectContainer(<MetricQuestionsContainer />));
          await store.waitForActions([FETCH_METRICS, FETCH_METRIC_TABLE]);
        } finally {
          // even if the code above results in an exception, try to delete the question
          await CardApi.delete({ cardId: card.id });
        }
      });
Example #8
0
 return async (dispatch, getState) => {
     const state = getState();
     if (cardId == null) {
         // bulk move
         let selected = getSelectedEntities(getState());
         if (undoable) {
             dispatch(addUndo(createUndo(
                 "moved",
                 selected.map(item => setCollection(item.id, getCardCollectionId(state, item.id)))
             )));
             MetabaseAnalytics.trackEvent("Questions", "Bulk Move to Collection");
         }
         selected.map(item => dispatch(setCollection(item.id, collectionId)));
     } else {
         const collection = _.findWhere(state.collections.collections, { id: collectionId });
         if (undoable) {
             dispatch(addUndo(createUndo(
                 "moved",
                 [setCollection(cardId, getCardCollectionId(state, cardId))]
             )));
             MetabaseAnalytics.trackEvent("Questions", "Move to Collection");
         }
         const card = await CardApi.update({ id: cardId, collection_id: collectionId });
         return {
             ...card,
             _changedSectionSlug: collection && collection.slug
         }
     }
 }
Example #9
0
export async function loadCard(cardId) {
    try {
        return await CardApi.get({ "cardId": cardId });
    } catch (error) {
        console.log("error loading card", error);
        throw error;
    }
}
Example #10
0
    return async (dispatch, getState) => {
        const state = getState();
        const parameters = getParameters(state);

        // if we got a query directly on the action call then use it, otherwise take whatever is in our current state
        card = card || state.qb.card;
        parameterValues = parameterValues || state.qb.parameterValues || {};

        const cardIsDirty = isCardDirty(card, state.qb.originalCard);

        card = {
            ...card,
            dataset_query: applyParameters(card, parameters, parameterValues)
        };

        if (shouldUpdateUrl) {
            dispatch(updateUrl(card, { dirty: cardIsDirty }));
        }

        let cancelQueryDeferred = defer();
        const startTime = new Date();

        // make our api call
        function onQuerySuccess(queryResult) {
            dispatch(queryCompleted(card, queryResult));
        }

        function onQueryError(error) {
            dispatch(queryErrored(startTime, error));
        }

        // use the CardApi.query if the query is saved and not dirty so users with view but not create permissions can see it.
        if (card && card.id && !isDirty(state) && dirty !== true) {
            CardApi.query({ cardId: card.id, parameters: card.dataset_query.parameters }, { cancelled: cancelQueryDeferred.promise }).then(onQuerySuccess, onQueryError);
        } else {
            MetabaseApi.dataset(card.dataset_query, { cancelled: cancelQueryDeferred.promise }).then(onQuerySuccess, onQueryError);
        }

        MetabaseAnalytics.trackEvent("QueryBuilder", "Run Query", card.dataset_query.type);

        // HACK: prevent SQL editor from losing focus
        try { ace.edit("id_sql").focus() } catch (e) {}

        return cancelQueryDeferred;
    };
Example #11
0
    async (dispatch, getState) => {
        let card = {
            ...getState().qb.card, // grab the current card
            archived
        }
        let response = await CardApi.update(card)

        const type = archived ? "archived" : "unarchived"

        dispatch(addUndo(createUndo({
            type,
            // eslint-disable-next-line react/display-name
            message: () => <div> { "Question  was " + type + "."} </div>,
            action: archiveQuestion(card.id, !archived)
        })));

        dispatch(push('/questions'))
        return response
    }
Example #12
0
    it("should be possible to view a embedded question", async () => {
      useSharedAdminLogin();
      await CardApi.update({
        id: question.id(),
        embedding_params: {
          id: "enabled",
          name: "enabled",
          source: "enabled",
          user_id: "enabled",
        },
        enable_embedding: true,
      });

      logout();

      const token = jwt.sign(
        {
          resource: { question: question.id() },
          params: {},
        },
        METABASE_SECRET_KEY,
      );

      store = await createTestStore({ embedApp: true });
      store.pushPath(Urls.embedCard(token) + "?id=1");
      app = mount(store.getAppContainer());

      await waitForRequestToComplete("GET", /\/card\/[^\/]+/);

      expect(app.find(".EmbedFrame-header .h4").text()).toEqual(
        "Test Question",
      );

      // wait for the query to load
      await waitForRequestToComplete(
        "GET",
        /^\/api\/embed\/card\/[^\/]+\/query/,
      );
    });
Example #13
0
    it("should be possible to view a public question", async () => {
      useSharedAdminLogin();
      const publicQuestion = await CardApi.createPublicLink({
        id: question.id(),
      });

      logout();

      store = await createTestStore({ publicApp: true });
      store.pushPath(Urls.publicQuestion(publicQuestion.uuid) + "?id=1");
      app = mount(store.getAppContainer());

      await waitForRequestToComplete("GET", /^\/api\/[^\/]*\/card/);
      expect(app.find(".EmbedFrame-header .h4").text()).toEqual(
        "Test Question",
      );

      // wait for the query to load
      await waitForRequestToComplete(
        "GET",
        /^\/api\/public\/card\/[^\/]+\/query/,
      );
    });
Example #14
0
 return async function(dispatch, getState) {
     let cards = await CardApi.list({ f: filterMode });
     return normalize(cards, [card]);
 };
Example #15
0
export const createPublicLink = createAction(CREATE_PUBLIC_LINK, ({ id }) =>
  CardApi.createPublicLink({ id }),
Example #16
0
 ({ id }, embedding_params) => CardApi.update({ id, embedding_params }),
Example #17
0
 ({ id }, enable_embedding) => CardApi.update({ id, enable_embedding }),
Example #18
0
 .map(async dc => CardApi.update(dc.card)));
Example #19
0
export const updateEmbeddingParams = createAction(UPDATE_EMBEDDING_PARAMS, ({ id }, embedding_params) =>
    CardApi.update({ id, embedding_params })
Example #20
0
 return async (dispatch, getState) => {
     let entityQuery = JSON.stringify(entityQueryObject);
     try {
         let result;
         dispatch(setRequestState({ statePath: ['questions', 'fetch'], state: "LOADING" }));
         if (entityType === "cards") {
             result = { entityType, entityQuery, ...normalize(momentifyArraysTimestamps(await CardApi.list(entityQueryObject)), [card]) };
         } else if (entityType === "collections") {
             result = { entityType, entityQuery, ...normalize(momentifyArraysTimestamps(await CollectionsApi.list(entityQueryObject)), [collection]) };
         } else {
             throw "Unknown entity type " + entityType;
         }
         dispatch(setRequestState({ statePath: ['questions', 'fetch'], state: "LOADED" }));
         return result;
     } catch (error) {
         throw { entityType, entityQuery, error };
     }
 }
Example #21
0
 return async function(dispatch, getState) {
     await CardApi.delete({ cardId });
     return cardId;
 };
Example #22
0
    return async function(dispatch, getState) {
        // If the dataset_query was filtered then we don't have permisison to view this card, so
        // shortcircuit and return a fake 403
        if (!card.dataset_query) {
            return {
                dashcard_id: dashcard.id,
                card_id: card.id,
                result: { error: { status: 403 }}
            };
        }

        const isPublicLink = Utils.isUUID(dashcard.dashboard_id);
        const { dashboardId, dashboards, parameterValues, dashcardData } = getState().dashboard;
        const dashboard = dashboards[dashboardId];

        // if we have a parameter, apply it to the card query before we execute
        const datasetQuery = applyParameters(card, dashboard.parameters, parameterValues, dashcard && dashcard.parameter_mappings);

        if (!reload) {
            // if reload not set, check to see if the last result has the same query dict and return that
            const lastResult = getIn(dashcardData, [dashcard.id, card.id]);
            // "constraints" is added by the backend, remove it when comparing
            if (lastResult && Utils.equals(_.omit(lastResult.json_query, "constraints"), datasetQuery)) {
                return {
                    dashcard_id: dashcard.id,
                    card_id: card.id,
                    result: lastResult
                };
            }
        }

        if (clear) {
            // clears the card data to indicate the card is reloading
            dispatch(clearCardData(card.id, dashcard.id));
        }

        let result = null;

        // start a timer that will fetch the expected card duration if the query takes too long
        let slowCardTimer = setTimeout(() => {
            if (result === null) {
                dispatch(fetchCardDuration(card, datasetQuery));
            }
        }, DATASET_SLOW_TIMEOUT);

        // make the actual request
        if (isPublicLink) {
            result = await fetchDataOrError(PublicApi.dashboardCardQuery({
                uuid: dashcard.dashboard_id,
                cardId: card.id,
                parameters: datasetQuery.parameters ? JSON.stringify(datasetQuery.parameters) : undefined
            }));
        } else {
            result = await fetchDataOrError(CardApi.query({cardId: card.id, parameters: datasetQuery.parameters}));
        }

        clearTimeout(slowCardTimer);

        return {
            dashcard_id: dashcard.id,
            card_id: card.id,
            result: result
        };
    };
Example #23
0
export const deletePublicLink = createAction(DELETE_PUBLIC_LINK, ({ id }) =>
  CardApi.deletePublicLink({ id }),
Example #24
0
export const updateEnableEmbedding = createAction(UPDATE_ENABLE_EMBEDDING, ({ id }, enable_embedding) =>
    CardApi.update({ id, enable_embedding })
Example #25
0
 cleanup.fn(() =>
   CardApi.update({
     id: question.id(),
     archived: true,
   }),