## What does this PR do? Converts a `console.log` statement to use the proper `@calcom/lib/logger` with debug level in the raqbUtils.server.ts file. This ensures the log message only appears when debug logging is enabled, reducing noise in production logs. ## Mandatory Tasks (DO NOT REMOVE) - [x] I have self-reviewed the code (A decent size PR without self-review might be rejected). - [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - no documentation changes needed. - [x] I confirm automated tests are in place that prove my fix is effective or that my feature works. N/A - logging change only. ## How should this be tested? This is a simple logging change. No specific testing required beyond CI passing. The log message "A group option found. Using all its sub-options instead" will now only appear when the logger level is set to debug (level 2 or lower via `NEXT_PUBLIC_LOGGER_LEVEL`). ## Checklist - [x] My code follows the style guidelines of this project - [x] I have checked if my changes generate no new warnings <!-- Link to Devin run: https://app.devin.ai/sessions/45d95cd7127541ffac96764f251ae1c9 --> <!-- Requested by: @hariombalhara -->
205 lines
6.7 KiB
TypeScript
205 lines
6.7 KiB
TypeScript
/**
|
|
* Server-safe RAQB utilities that don't require the react-awesome-query-builder runtime.
|
|
* These functions can be safely imported in server-side code (cron jobs, API routes, etc.)
|
|
* without pulling in the client-only RAQB library.
|
|
*/
|
|
import type { JsonGroup, JsonItem, JsonRule, JsonTree } from "react-awesome-query-builder";
|
|
|
|
import { getQueryBuilderConfigForAttributes } from "@calcom/app-store/routing-forms/lib/getQueryBuilderConfig";
|
|
import { resolveQueryValue } from "@calcom/app-store/routing-forms/lib/resolveQueryValue";
|
|
import type {
|
|
LocalRoute,
|
|
Attribute,
|
|
AttributeOptionValueWithType,
|
|
AttributeOptionValue,
|
|
} from "@calcom/app-store/routing-forms/types/types";
|
|
import type { dynamicFieldValueOperands } from "@calcom/lib/raqb/types";
|
|
import logger from "@calcom/lib/logger";
|
|
import { caseInsensitive } from "@calcom/lib/raqb/utils";
|
|
import { safeStringify } from "@calcom/lib/safeStringify";
|
|
import { AttributeType } from "@calcom/prisma/enums";
|
|
|
|
const log = logger.getSubLogger({ prefix: ["raqbUtils"] });
|
|
|
|
function ensureArray(value: string | string[]) {
|
|
return typeof value === "string" ? [value] : value;
|
|
}
|
|
|
|
export const raqbQueryValueUtils = {
|
|
isQueryValueARuleGroup: function isQueryValueARuleGroup(queryValue: JsonTree): queryValue is JsonGroup {
|
|
return queryValue.type === "group";
|
|
},
|
|
isARule: function isARule(rule: JsonItem): rule is JsonRule {
|
|
return rule.type === "rule";
|
|
},
|
|
getValueTypeFromAttributesQueryValueForRaqbField:
|
|
function getValueTypeFromAttributesQueryValueForRaqbField({
|
|
attributesQueryValue,
|
|
raqbFieldId,
|
|
}: {
|
|
attributesQueryValue: JsonTree;
|
|
raqbFieldId: string;
|
|
}) {
|
|
if (!raqbQueryValueUtils.isQueryValueARuleGroup(attributesQueryValue)) {
|
|
return null;
|
|
}
|
|
|
|
if (!attributesQueryValue.children1) {
|
|
return null;
|
|
}
|
|
|
|
let raqbFieldValueType = null;
|
|
|
|
Object.values(attributesQueryValue.children1).reduce((acc, rule) => {
|
|
if (!raqbQueryValueUtils.isARule(rule)) {
|
|
return acc;
|
|
}
|
|
const ruleProperties = rule?.properties;
|
|
if (!ruleProperties) {
|
|
return acc;
|
|
}
|
|
if (ruleProperties.field === raqbFieldId) {
|
|
raqbFieldValueType = ruleProperties.valueType?.[0];
|
|
}
|
|
return acc;
|
|
}, null);
|
|
|
|
return raqbFieldValueType;
|
|
},
|
|
isQueryValueEmpty: function isQueryValueEmpty(queryValue: JsonTree | null): queryValue is null {
|
|
if (!queryValue) {
|
|
return true;
|
|
}
|
|
return !queryValue.children1;
|
|
},
|
|
};
|
|
|
|
export function getValueOfAttributeOption(
|
|
attributeOptions:
|
|
| Pick<AttributeOptionValue, "isGroup" | "contains" | "value">
|
|
| Pick<AttributeOptionValue, "isGroup" | "contains" | "value">[]
|
|
) {
|
|
if (!(attributeOptions instanceof Array)) {
|
|
return transformAttributeOption(attributeOptions);
|
|
}
|
|
return attributeOptions
|
|
.map(transformAttributeOption)
|
|
.flat()
|
|
.filter((value, index, self) => self.indexOf(value) === index);
|
|
|
|
function transformAttributeOption(
|
|
attributeOption: Pick<AttributeOptionValue, "isGroup" | "contains" | "value">
|
|
) {
|
|
if (attributeOption.isGroup) {
|
|
const subOptions = attributeOption.contains.map((option) => option.value);
|
|
log.debug("A group option found. Using all its sub-options instead", safeStringify(subOptions));
|
|
return subOptions;
|
|
}
|
|
return attributeOption.value;
|
|
}
|
|
}
|
|
|
|
function getAttributesData({
|
|
attributesData,
|
|
}: {
|
|
attributesData: Record<string, AttributeOptionValueWithType>;
|
|
attributesQueryValue: NonNullable<LocalRoute["attributesQueryValue"]>;
|
|
}) {
|
|
return Object.entries(attributesData).reduce(
|
|
(acc, [attributeId, { type: attributeType, attributeOption }]) => {
|
|
const compatibleValueForAttributeAndFormFieldMatching = caseInsensitive(
|
|
getValueOfAttributeOption(attributeOption)
|
|
);
|
|
|
|
acc[attributeId] =
|
|
// multiselect attribute's value must be an array as all the operators multiselect_some_in, multiselect_all_in and their respective not operators expect an array
|
|
// If we add an operator that doesn't expect an array, we need to somehow make it operator based.
|
|
attributeType === AttributeType.MULTI_SELECT
|
|
? ensureArray(compatibleValueForAttributeAndFormFieldMatching)
|
|
: compatibleValueForAttributeAndFormFieldMatching;
|
|
|
|
return acc;
|
|
},
|
|
{} as Record<string, string | string[]>
|
|
);
|
|
}
|
|
/** Gets the attributes that were used to generate the chosen route and their values */
|
|
function getAttributesQueryValue({
|
|
attributesQueryValue,
|
|
attributes,
|
|
dynamicFieldValueOperands,
|
|
}: {
|
|
attributesQueryValue: LocalRoute["attributesQueryValue"] | null;
|
|
attributes: Attribute[];
|
|
dynamicFieldValueOperands?: dynamicFieldValueOperands;
|
|
}) {
|
|
if (!attributesQueryValue) {
|
|
return null;
|
|
}
|
|
|
|
const resolvedQueryValue = resolveQueryValue({
|
|
queryValue: attributesQueryValue,
|
|
attributes,
|
|
dynamicFieldValueOperands,
|
|
});
|
|
|
|
return resolvedQueryValue;
|
|
}
|
|
|
|
/**
|
|
* Returns attributesQueryBuilderConfig with the list of labels instead of list of ids.
|
|
*/
|
|
export function getAttributesQueryBuilderConfigHavingListofLabels({
|
|
dynamicFieldValueOperands,
|
|
attributes,
|
|
}: {
|
|
dynamicFieldValueOperands?: dynamicFieldValueOperands;
|
|
attributes: Attribute[];
|
|
}) {
|
|
const attributesQueryBuilderConfig = getQueryBuilderConfigForAttributes({
|
|
attributes,
|
|
dynamicOperandFields: dynamicFieldValueOperands?.fields,
|
|
});
|
|
|
|
const attributesQueryBuilderConfigFieldsWithCompatibleListValues = Object.fromEntries(
|
|
Object.entries(attributesQueryBuilderConfig.fields).map(([raqbFieldId, raqbField]) => {
|
|
const raqbFieldType = raqbField.type;
|
|
|
|
return [
|
|
raqbFieldId,
|
|
{
|
|
...raqbField,
|
|
type: raqbFieldType,
|
|
fieldSettings: {
|
|
...raqbField.fieldSettings,
|
|
listValues: raqbField.fieldSettings.listValues?.map((option) => {
|
|
return {
|
|
...option,
|
|
// Use the title(which is the attributeOption.value) as the value of the raqb field so that it can be compatible for matching with the form field value
|
|
value: caseInsensitive(option.title),
|
|
};
|
|
}),
|
|
},
|
|
},
|
|
];
|
|
})
|
|
);
|
|
|
|
const attributesQueryBuilderConfigWithCompatibleListValues = {
|
|
...attributesQueryBuilderConfig,
|
|
fields: attributesQueryBuilderConfigFieldsWithCompatibleListValues,
|
|
};
|
|
|
|
return attributesQueryBuilderConfigWithCompatibleListValues;
|
|
}
|
|
|
|
/**
|
|
* Utilities to establish compatibility between formFieldQueryValue and attributeQueryValue
|
|
*/
|
|
export const acrossQueryValueCompatiblity = {
|
|
getAttributesQueryBuilderConfigHavingListofLabels,
|
|
getAttributesQueryValue,
|
|
getAttributesData,
|
|
resolveQueryValue,
|
|
};
|