diff --git a/frontend/src/metabase/actions/containers/ActionCreator/ActionCreator.tsx b/frontend/src/metabase/actions/containers/ActionCreator/ActionCreator.tsx index 92ca86adb233783ed161caa4380ec735ce1f58ca..86d38cb618b701888998434b09118f4e171727b1 100644 --- a/frontend/src/metabase/actions/containers/ActionCreator/ActionCreator.tsx +++ b/frontend/src/metabase/actions/containers/ActionCreator/ActionCreator.tsx @@ -2,9 +2,14 @@ import React, { useState, useMemo, useEffect, useCallback } from "react"; import { t } from "ttag"; import _ from "underscore"; import { connect } from "react-redux"; -import { push } from "react-router-redux"; +import type { LocationDescriptor } from "history"; -import Actions, { ActionParams } from "metabase/entities/actions"; +import Modal from "metabase/components/Modal"; + +import Actions, { + CreateQueryActionParams, + UpdateQueryActionParams, +} from "metabase/entities/actions"; import Database from "metabase/entities/databases"; import { getMetadata } from "metabase/selectors/metadata"; @@ -18,7 +23,6 @@ import type { import type { State } from "metabase-types/store"; import type { SavedCard } from "metabase-types/types/Card"; -import Modal from "metabase/components/Modal"; import { getUserIsAdmin } from "metabase/selectors/user"; import { getSetting } from "metabase/selectors/settings"; import type NativeQuery from "metabase-lib/queries/NativeQuery"; @@ -26,9 +30,12 @@ import type Metadata from "metabase-lib/metadata/Metadata"; import type Question from "metabase-lib/Question"; import { getTemplateTagParametersFromCard } from "metabase-lib/parameters/utils/template-tags"; -import CreateActionForm from "../CreateActionForm"; -import { newQuestion, convertActionToQuestionCard } from "./utils"; + +import { newQuestion, convertQuestionToAction } from "./utils"; import ActionCreatorView from "./ActionCreatorView"; +import CreateActionForm, { + FormValues as CreateActionFormValues, +} from "./CreateActionForm"; const mapStateToProps = ( state: State, @@ -42,8 +49,8 @@ const mapStateToProps = ( }); const mapDispatchToProps = { - push, - update: Actions.actions.update, + onCreateAction: Actions.actions.create, + onUpdateAction: Actions.actions.update, }; const EXAMPLE_QUERY = @@ -64,8 +71,9 @@ interface StateProps { } interface DispatchProps { - push: (url: string) => void; - update: (action: ActionParams) => void; + onCreateAction: (params: CreateQueryActionParams) => void; + onUpdateAction: (params: UpdateQueryActionParams) => void; + onChangeLocation: (nextLocation: LocationDescriptor) => void; } type ActionCreatorProps = OwnProps & StateProps & DispatchProps; @@ -76,7 +84,8 @@ function ActionCreatorComponent({ actionId, modelId, databaseId, - update, + onCreateAction, + onUpdateAction, onClose, isAdmin, isPublicSharingEnabled, @@ -84,9 +93,10 @@ function ActionCreatorComponent({ const [question, setQuestion] = useState( passedQuestion ?? newQuestion(metadata, databaseId), ); - const [formSettings, setFormSettings] = useState< - ActionFormSettings | undefined - >(undefined); + const [formSettings, setFormSettings] = useState<ActionFormSettings>({ + type: "button", + fields: {}, + }); const [showSaveModal, setShowSaveModal] = useState(false); useEffect(() => { @@ -125,22 +135,26 @@ function ActionCreatorComponent({ if (isNew) { setShowSaveModal(true); } else { - update({ - id: question.id(), - name: question.displayName() ?? "", - description: question.description() ?? null, - model_id: defaultModelId as number, - formSettings: formSettings as ActionFormSettings, - question, - }); + const action = convertQuestionToAction(question, formSettings); + onUpdateAction({ ...action, model_id: defaultModelId as number }); onClose?.(); } }; - const handleSave = (action: WritebackQueryAction) => { - const actionCard = convertActionToQuestionCard(action); - setQuestion(question.setCard(actionCard)); - setTimeout(() => setShowSaveModal(false), 1000); + const handleCreate = async (values: CreateActionFormValues) => { + const action = convertQuestionToAction(question, formSettings); + await onCreateAction({ + ...action, + ...values, + type: "query", + }); + + const nextQuestion = question + .setDisplayName(values.name) + .setDescription(values.description); + setQuestion(nextQuestion); + + setShowSaveModal(false); onClose?.(); }; @@ -172,10 +186,12 @@ function ActionCreatorComponent({ {showSaveModal && ( <Modal title={t`New Action`} onClose={handleCloseNewActionModal}> <CreateActionForm - question={question} - formSettings={formSettings as ActionFormSettings} - modelId={defaultModelId} - onCreate={handleSave} + initialValues={{ + name: question.displayName() ?? "", + description: question.description(), + model_id: defaultModelId, + }} + onCreate={handleCreate} onCancel={handleCloseNewActionModal} /> </Modal> diff --git a/frontend/src/metabase/actions/containers/CreateActionForm/CreateActionForm.tsx b/frontend/src/metabase/actions/containers/ActionCreator/CreateActionForm/CreateActionForm.tsx similarity index 58% rename from frontend/src/metabase/actions/containers/CreateActionForm/CreateActionForm.tsx rename to frontend/src/metabase/actions/containers/ActionCreator/CreateActionForm/CreateActionForm.tsx index 90737e25e489dd0dbaafef636511aa733fb4a928..b612d0bf8af5596606fae66a7a9ac4f6558f9633 100644 --- a/frontend/src/metabase/actions/containers/CreateActionForm/CreateActionForm.tsx +++ b/frontend/src/metabase/actions/containers/ActionCreator/CreateActionForm/CreateActionForm.tsx @@ -1,7 +1,6 @@ -import React, { useCallback, useMemo } from "react"; +import React, { useMemo } from "react"; import { t } from "ttag"; import * as Yup from "yup"; -import { connect } from "react-redux"; import Button from "metabase/core/components/Button"; import Form from "metabase/core/components/Form"; @@ -14,18 +13,10 @@ import FormErrorMessage from "metabase/core/components/FormErrorMessage"; import * as Errors from "metabase/core/utils/errors"; -import Actions, { CreateQueryActionOptions } from "metabase/entities/actions"; +import type { CreateQueryActionParams } from "metabase/entities/actions"; import FormModelPicker from "metabase/models/containers/FormModelPicker"; -import type { - ActionFormSettings, - CardId, - WritebackQueryAction, -} from "metabase-types/api"; -import type { State } from "metabase-types/store"; -import type Question from "metabase-lib/Question"; - const ACTION_SCHEMA = Yup.object({ name: Yup.string() .required(Errors.required) @@ -35,67 +26,37 @@ const ACTION_SCHEMA = Yup.object({ model_id: Yup.number().required(Errors.required), }); -type FormValues = Pick< - CreateQueryActionOptions, +export type FormValues = Pick< + CreateQueryActionParams, "name" | "description" | "model_id" >; interface OwnProps { - question: Question; - formSettings: ActionFormSettings; - modelId?: CardId; - onCreate?: (values: WritebackQueryAction) => void; + initialValues?: Partial<FormValues>; + onCreate: (values: FormValues) => void; onCancel?: () => void; } -interface DispatchProps { - handleCreateAction: ( - action: CreateQueryActionOptions, - ) => Promise<WritebackQueryAction>; -} - -type Props = OwnProps & DispatchProps; - -const mapDispatchToProps = { - handleCreateAction: Actions.actions.create, -}; +type CreateActionFormProps = OwnProps; function CreateActionForm({ - question, - formSettings, - modelId, - handleCreateAction, + initialValues: initialValuesProp, onCreate, onCancel, -}: Props) { +}: CreateActionFormProps) { const initialValues = useMemo( () => ({ ...ACTION_SCHEMA.getDefault(), - name: question.displayName(), - description: question.description(), - model_id: modelId, + ...initialValuesProp, }), - [question, modelId], - ); - - const handleCreate = useCallback( - async (values: FormValues) => { - const reduxAction = await handleCreateAction({ - ...values, - question, - formSettings, - }); - const action = Actions.HACK_getObjectFromAction(reduxAction); - onCreate?.(action); - }, - [question, formSettings, handleCreateAction, onCreate], + [initialValuesProp], ); return ( <FormProvider initialValues={initialValues as FormValues} validationSchema={ACTION_SCHEMA} - onSubmit={handleCreate} + onSubmit={onCreate} > {({ dirty }) => ( <Form disabled={!dirty}> @@ -125,7 +86,4 @@ function CreateActionForm({ ); } -export default connect<unknown, DispatchProps, OwnProps, State>( - null, - mapDispatchToProps, -)(CreateActionForm); +export default CreateActionForm; diff --git a/frontend/src/metabase/actions/containers/CreateActionForm/index.ts b/frontend/src/metabase/actions/containers/ActionCreator/CreateActionForm/index.ts similarity index 56% rename from frontend/src/metabase/actions/containers/CreateActionForm/index.ts rename to frontend/src/metabase/actions/containers/ActionCreator/CreateActionForm/index.ts index c344120d4a3d3a8145276d2601360b4f9d7965a1..fd88da91cffa467f1e39cc2862a938a39c682c81 100644 --- a/frontend/src/metabase/actions/containers/CreateActionForm/index.ts +++ b/frontend/src/metabase/actions/containers/ActionCreator/CreateActionForm/index.ts @@ -1 +1,2 @@ export { default } from "./CreateActionForm"; +export * from "./CreateActionForm"; diff --git a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx index a26ff27308c4778e67f4c386051b4c71a1f5e8d2..2dad46d577a64500a09fee66593af571ea66b786 100644 --- a/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx +++ b/frontend/src/metabase/actions/containers/ActionCreator/FormCreator/FormCreator.tsx @@ -3,11 +3,10 @@ import _ from "underscore"; import { ActionForm } from "metabase/actions/components/ActionForm"; -import { addMissingSettings } from "metabase/entities/actions/utils"; - import type { ActionFormSettings, Parameter } from "metabase-types/api"; import { getDefaultFormSettings, sortActionParams } from "../../../utils"; +import { addMissingSettings } from "../utils"; import { hasNewParams } from "./utils"; import { EmptyFormPlaceholder } from "./EmptyFormPlaceholder"; diff --git a/frontend/src/metabase/actions/containers/ActionCreator/utils.ts b/frontend/src/metabase/actions/containers/ActionCreator/utils.ts index ee99bf1b7f19a0ceeb218b22ed7be029b7908075..693ba5575f1b19e4bec287ded32a3ed6fea0c7b9 100644 --- a/frontend/src/metabase/actions/containers/ActionCreator/utils.ts +++ b/frontend/src/metabase/actions/containers/ActionCreator/utils.ts @@ -1,14 +1,46 @@ +import _ from "underscore"; + +import { getDefaultFieldSettings } from "metabase/actions/utils"; + import type { - WritebackQueryAction, - VisualizationSettings, -} from "metabase-types/api"; -import type { - Card as LegacyCard, + ActionFormSettings, + FieldType, + InputSettingType, NativeDatasetQuery, -} from "metabase-types/types/Card"; + Parameter, + ParameterType, + WritebackParameter, +} from "metabase-types/api"; +import type { TemplateTag, TemplateTagType } from "metabase-types/types/Query"; + import type Metadata from "metabase-lib/metadata/Metadata"; +import type NativeQuery from "metabase-lib/queries/NativeQuery"; import Question from "metabase-lib/Question"; +type FieldTypeMap = Record<string, ParameterType>; + +type TagTypeMap = Record<string, TemplateTagType>; + +const fieldTypeToParameterTypeMap: FieldTypeMap = { + string: "string/=", + category: "string/=", + number: "number/=", +}; + +const dateTypeToParameterTypeMap: FieldTypeMap = { + date: "date/single", + datetime: "date/single", + monthyear: "date/month-year", + quarteryear: "date/quarter-year", +}; + +const fieldTypeToTagTypeMap: TagTypeMap = { + string: "text", + category: "text", + number: "number", + date: "date", +}; + // ActionCreator uses the NativeQueryEditor, which expects a Question object // This utilities help us to work with the WritebackQueryAction as with a Question @@ -27,15 +59,117 @@ export const newQuestion = (metadata: Metadata, databaseId?: number) => { ); }; -export const convertActionToQuestionCard = ( - action: WritebackQueryAction, -): LegacyCard<NativeDatasetQuery> => { +const getTagTypeFromFieldSettings = (fieldType: FieldType): TemplateTagType => { + return fieldTypeToTagTypeMap[fieldType] ?? "text"; +}; + +export const setTemplateTagTypesFromFieldSettings = ( + question: Question, + settings: ActionFormSettings, +): Question => { + const fields = settings.fields; + const query = question.query() as NativeQuery; + let tempQuestion = question.clone(); + + query.templateTagsWithoutSnippets().forEach((tag: TemplateTag) => { + const currentQuery = tempQuestion.query() as NativeQuery; + const fieldType = fields[tag.id]?.fieldType ?? "string"; + const nextTag = { + ...tag, + type: getTagTypeFromFieldSettings(fieldType), + }; + tempQuestion = tempQuestion.setQuery( + currentQuery.setTemplateTag(tag.name, nextTag), + ); + }); + + return tempQuestion; +}; + +const getParameterTypeFromFieldSettings = ( + fieldType: FieldType, + inputType: InputSettingType, +): ParameterType => { + if (fieldType === "date") { + return dateTypeToParameterTypeMap[inputType] ?? "date/single"; + } + + return fieldTypeToParameterTypeMap[fieldType] ?? "string/="; +}; + +export const setParameterTypesFromFieldSettings = ( + settings: ActionFormSettings, + parameters: Parameter[], +): Parameter[] => { + const fields = settings.fields; + return parameters.map(parameter => { + const field = fields[parameter.id]; + return { + ...parameter, + type: field + ? getParameterTypeFromFieldSettings(field.fieldType, field.inputType) + : "string/=", + }; + }); +}; + +export const removeOrphanSettings = ( + formSettings: ActionFormSettings, + parameters: Parameter[], +): ActionFormSettings => { + const parameterIds = parameters.map(parameter => parameter.id); + return { + ...formSettings, + fields: _.pick(formSettings.fields, parameterIds), + }; +}; + +export const addMissingSettings = ( + settings: ActionFormSettings, + parameters: Parameter[], +): ActionFormSettings => { + const parameterIds = parameters.map(parameter => parameter.id); + const fieldIds = Object.keys(settings.fields); + const missingIds = _.difference(parameterIds, fieldIds); + + if (!missingIds.length) { + return settings; + } + + return { + ...settings, + fields: { + ...settings.fields, + ...Object.fromEntries( + missingIds.map(id => [id, getDefaultFieldSettings({ id })]), + ), + }, + }; +}; + +export const convertQuestionToAction = ( + question: Question, + formSettings: ActionFormSettings, +) => { + const cleanQuestion = setTemplateTagTypesFromFieldSettings( + question, + formSettings, + ); + const parameters = setParameterTypesFromFieldSettings( + formSettings, + cleanQuestion.parameters(), + ); + const visualization_settings = removeOrphanSettings( + addMissingSettings(formSettings, parameters), + parameters, + ); return { - name: action.name, - description: action.description, - dataset_query: action.dataset_query as NativeDatasetQuery, - display: "action", - visualization_settings: - action.visualization_settings as VisualizationSettings, + id: question.id(), + name: question.displayName() as string, + description: question.description(), + dataset_query: question.datasetQuery() as NativeDatasetQuery, + database_id: question.databaseId(), + parameters: parameters as WritebackParameter[], + visualization_settings, }; }; diff --git a/frontend/src/metabase/entities/actions/utils.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/utils.unit.spec.ts similarity index 100% rename from frontend/src/metabase/entities/actions/utils.unit.spec.ts rename to frontend/src/metabase/actions/containers/ActionCreator/utils.unit.spec.ts index 9f87fa68285dd0aca3d29db4b1061e7bf079254c..5bed4757481d57fe7d3cab9c370c578ed4568255 100644 --- a/frontend/src/metabase/entities/actions/utils.unit.spec.ts +++ b/frontend/src/metabase/actions/containers/ActionCreator/utils.unit.spec.ts @@ -177,8 +177,8 @@ describe("entities > actions > utils", () => { }); const newQuestion = setTemplateTagTypesFromFieldSettings( - formSettings, question, + formSettings, ); const tags = (newQuestion.card().dataset_query as NativeDatasetQuery) @@ -200,8 +200,8 @@ describe("entities > actions > utils", () => { }); const newQuestion = setTemplateTagTypesFromFieldSettings( - formSettings, question, + formSettings, ); const tags = (newQuestion.card().dataset_query as NativeDatasetQuery) diff --git a/frontend/src/metabase/entities/actions/actions.ts b/frontend/src/metabase/entities/actions/actions.ts index 2247e994d955024d7e5fbc12e663ceda96d81976..5031d919e183e2aa553ac0cd62307a802344bb51 100644 --- a/frontend/src/metabase/entities/actions/actions.ts +++ b/frontend/src/metabase/entities/actions/actions.ts @@ -1,134 +1,41 @@ import { t } from "ttag"; -import { createAction } from "redux-actions"; import { updateIn } from "icepick"; +import { createAction } from "redux-actions"; + import { createEntity } from "metabase/lib/entities"; +import { ActionsApi } from "metabase/services"; import type { - ActionFormSettings, - ImplicitQueryAction, - WritebackActionBase, WritebackAction, WritebackActionId, + WritebackQueryAction, + WritebackImplicitQueryAction, } from "metabase-types/api"; import type { Dispatch } from "metabase-types/store"; -import { ActionsApi } from "metabase/services"; - -import { - removeOrphanSettings, - addMissingSettings, - setParameterTypesFromFieldSettings, - setTemplateTagTypesFromFieldSettings, -} from "metabase/entities/actions/utils"; -import type Question from "metabase-lib/Question"; +type BaseCreateActionParams = Pick< + WritebackAction, + "name" | "description" | "model_id" | "parameters" | "visualization_settings" +>; -export type ActionParams = { - id?: WritebackAction["id"]; - name: WritebackAction["name"]; - type?: WritebackAction["type"]; - kind?: ImplicitQueryAction["kind"]; - description?: WritebackAction["description"]; - model_id: WritebackAction["model_id"]; - question?: Question; - formSettings?: ActionFormSettings; +type BaseUpdateActionParams = { + id: WritebackAction["id"]; }; -interface BaseCreateActionParams { - model_id: WritebackActionBase["model_id"]; - name: WritebackActionBase["name"]; - description: WritebackActionBase["description"]; - parameters?: WritebackActionBase["parameters"]; -} - -interface UpdateActionParams { - id: WritebackActionBase["id"]; -} - -export interface CreateQueryActionOptions extends BaseCreateActionParams { - question: Question; - formSettings: ActionFormSettings; -} - -export type UpdateQueryActionOptions = CreateQueryActionOptions & - UpdateActionParams; - -export interface CreateImplicitActionOptions extends BaseCreateActionParams { - kind: ImplicitQueryAction["kind"]; -} +export type CreateQueryActionParams = BaseCreateActionParams & + Pick<WritebackQueryAction, "type" | "dataset_query">; -export type UpdateImplicitActionOptions = CreateImplicitActionOptions & - UpdateActionParams; +export type UpdateQueryActionParams = Partial<CreateQueryActionParams> & + BaseUpdateActionParams; -function cleanUpQueryAction( - question: Question, - formSettings: ActionFormSettings, -) { - question = setTemplateTagTypesFromFieldSettings(formSettings, question); +export type CreateImplicitActionParams = BaseCreateActionParams & + Pick<WritebackImplicitQueryAction, "type" | "kind">; - const parameters = setParameterTypesFromFieldSettings( - formSettings, - question.parameters(), - ); - - const visualization_settings = removeOrphanSettings( - addMissingSettings(formSettings, parameters), - parameters, - ); - - return { - dataset_query: question.datasetQuery(), - parameters, - visualization_settings, - }; -} - -function createQueryAction({ - question, - formSettings, - ...action -}: CreateQueryActionOptions) { - const { dataset_query, parameters, visualization_settings } = - cleanUpQueryAction(question, formSettings); - - return ActionsApi.create({ - ...action, - type: "query", - dataset_query, - database_id: dataset_query.database, - parameters, - visualization_settings, - }); -} - -function updateQueryAction({ - question, - formSettings, - ...action -}: UpdateQueryActionOptions) { - const { dataset_query, parameters, visualization_settings } = - cleanUpQueryAction(question, formSettings); - - return ActionsApi.update({ - ...action, - dataset_query, - parameters, - visualization_settings, - }); -} - -function createImplicitAction(action: CreateImplicitActionOptions) { - return Actions.actions.create({ - ...action, - type: "implicit", - }); -} - -function updateImplicitAction(action: UpdateImplicitActionOptions) { - return Actions.actions.update({ - ...action, - type: "implicit", - }); -} +export type UpdateImplicitActionParams = Omit< + Partial<CreateImplicitActionParams>, + "type" +> & + BaseUpdateActionParams; const defaultImplicitActionCreateOptions = { insert: true, @@ -187,22 +94,10 @@ const Actions = createEntity({ nameOne: "action", path: "/api/action", api: { - create: ( - params: CreateQueryActionOptions | CreateImplicitActionOptions, - ) => { - if ("question" in params) { - return createQueryAction(params); - } - return createImplicitAction(params); - }, - update: ( - params: UpdateQueryActionOptions | UpdateImplicitActionOptions, - ) => { - if ("question" in params) { - return updateQueryAction(params); - } - return updateImplicitAction(params); - }, + create: (params: CreateQueryActionParams | CreateImplicitActionParams) => + ActionsApi.create(params), + update: (params: UpdateQueryActionParams | UpdateImplicitActionParams) => + ActionsApi.update(params), }, actions: { enableImplicitActionsForModel, diff --git a/frontend/src/metabase/entities/actions/utils.ts b/frontend/src/metabase/entities/actions/utils.ts deleted file mode 100644 index e705dfcddaecfb17f4d28076c46dd91be1ceaadb..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/entities/actions/utils.ts +++ /dev/null @@ -1,107 +0,0 @@ -import _ from "underscore"; - -import { getDefaultFieldSettings } from "metabase/actions/utils"; - -import type { - ActionFormSettings, - FieldType, - InputSettingType, - Parameter, - ParameterType, -} from "metabase-types/api"; -import type { TemplateTag, TemplateTagType } from "metabase-types/types/Query"; -import type NativeQuery from "metabase-lib/queries/NativeQuery"; -import type Question from "metabase-lib/Question"; - -import { - fieldTypeToParameterTypeMap, - dateTypeToParameterTypeMap, - fieldTypeToTagTypeMap, -} from "./constants"; - -export const removeOrphanSettings = ( - settings: ActionFormSettings, - parameters: Parameter[], -): ActionFormSettings => { - const parameterIds = parameters.map(p => p.id); - return { - ...settings, - fields: _.pick(settings.fields, parameterIds), - }; -}; - -export const addMissingSettings = ( - settings: ActionFormSettings, - parameters: Parameter[], -): ActionFormSettings => { - const parameterIds = parameters.map(p => p.id); - const fieldIds = Object.keys(settings.fields); - const missingIds = _.difference(parameterIds, fieldIds); - - if (!missingIds.length) { - return settings; - } - - return { - ...settings, - fields: { - ...settings.fields, - ...Object.fromEntries( - missingIds.map(id => [id, getDefaultFieldSettings({ id })]), - ), - }, - }; -}; - -const getParameterTypeFromFieldSettings = ( - fieldType: FieldType, - inputType: InputSettingType, -): ParameterType => { - if (fieldType === "date") { - return dateTypeToParameterTypeMap[inputType] ?? "date/single"; - } - - return fieldTypeToParameterTypeMap[fieldType] ?? "string/="; -}; - -const getTagTypeFromFieldSettings = (fieldType: FieldType): TemplateTagType => { - return fieldTypeToTagTypeMap[fieldType] ?? "text"; -}; - -export const setParameterTypesFromFieldSettings = ( - settings: ActionFormSettings, - parameters: Parameter[], -): Parameter[] => { - const fields = settings.fields; - return parameters.map(parameter => { - const field = fields[parameter.id]; - return { - ...parameter, - type: field - ? getParameterTypeFromFieldSettings(field.fieldType, field.inputType) - : "string/=", - }; - }); -}; - -export const setTemplateTagTypesFromFieldSettings = ( - settings: ActionFormSettings, - question: Question, -): Question => { - const fields = settings.fields; - - (question.query() as NativeQuery) - .templateTagsWithoutSnippets() - .forEach((tag: TemplateTag) => { - question = question.setQuery( - (question.query() as NativeQuery).setTemplateTag(tag.name, { - ...tag, - type: getTagTypeFromFieldSettings( - fields[tag.id]?.fieldType ?? "string", - ), - }), - ); - }); - - return question; -};