fix: select_any_in operator - Broken only for attributes (#17148)

* Fix select any_in operator for attributes - Broken only for attributres

* Fix ts error
This commit is contained in:
Hariom Balhara
2024-10-18 10:19:25 +00:00
committed by GitHub
parent 699e08b2ee
commit fd054dfb8d
3 changed files with 146 additions and 16 deletions
+1 -1
View File
@@ -8,7 +8,7 @@
- [x] Connect Form Response to Booking
- [x] Test with non option based attributes and form field input
- [ ] Switching from Select to MultiSelect in attribute
- [x] It immediately makes the Routing logic to not work. Fix it.
- [ ] It immediately makes the Routing logic to not work. Fix it.
- [ ] It removes the selected option from Routing. Fix it.
- [ ] Warnings are needed with recommendation to create new attribute instead in unsupported scenarios. We need to test for more such changes to Attribute types.
- [ ] When choosing 'Value of Field' option in RAQB, show a warning somewhere if all options of the Form field aren't present in the attribute option.
@@ -1,7 +1,6 @@
import type { BaseWidget } from "react-awesome-query-builder";
import { describe, it, expect, vi, beforeEach } from "vitest";
import logger from "@calcom/lib/logger";
import type { AttributeType } from "@calcom/prisma/enums";
import { RoutingFormFieldType } from "../../lib/FieldTypes";
@@ -86,7 +85,7 @@ function buildQueryValue({
}: {
rules: {
raqbFieldId: string;
value: string | number | string[];
value: string | number | string[] | [string[]];
operator: string;
valueSrc: NonNullable<BaseWidget["valueSrc"]>[];
valueType: string[];
@@ -118,8 +117,9 @@ function buildSelectTypeFieldQueryValue({
}: {
rules: {
raqbFieldId: string;
value: string | number | string[];
value: string | number | string[] | [string[]];
operator: string;
valueType?: string[];
}[];
}) {
return buildQueryValue({
@@ -128,7 +128,7 @@ function buildSelectTypeFieldQueryValue({
value: rule.value,
operator: rule.operator,
valueSrc: ["value"],
valueType: ["select"],
valueType: rule.valueType ?? ["select"],
})),
}) as AttributesQueryValue;
}
@@ -336,6 +336,125 @@ describe("findTeamMembersMatchingAttributeLogicOfRoute", () => {
]);
});
it("should return matching team members with a SINGLE_SELECT attribute when 'Any in' option is selected", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfField1HumanReadableValue = Option1OfAttribute1HumanReadableValue;
const Field1Id = "field-1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option2OfAttribute1 = {
id: "attr-1-opt-2",
value: "Option 2",
slug: "option-2",
};
const Option3OfAttribute1 = {
id: "attr-1-opt-3",
value: "Option 3",
slug: "option-3",
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1, Option3OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [[Option1OfAttribute1.id, Option2OfAttribute1.id]],
operator: "select_any_in",
valueType: ["multiselect"],
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogicOfRoute({
form: {
routes: [
buildDefaultCustomPageRoute({
id: "test-route",
attributesQueryValue: attributesQueryValue,
}),
],
fields: [],
},
response: {},
routeId: "test-route",
teamId: 1,
});
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
});
describe("Error handling", () => {
it("should throw an error if the attribute type is not supported", async () => {
const Option1OfAttribute1 = { id: "opt1", value: "Option 1", slug: "option-1" };
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "UNSUPPORTED_ATTRIBUTE_TYPE" as unknown as AttributeType,
slug: "attribute-1",
options: [Option1OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{
userId: 1,
attributes: { [Attribute1.id]: Option1OfAttribute1.value },
},
],
});
await expect(
findTeamMembersMatchingAttributeLogicOfRoute({
form: {
routes: [
buildDefaultCustomPageRoute({
id: "test-route",
attributesQueryValue: buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [Option1OfAttribute1.id],
operator: "select_equals",
},
],
}) as AttributesQueryValue,
}),
],
fields: [],
},
response: {},
routeId: "test-route",
teamId: 1,
})
).rejects.toThrow("Unsupported attribute type");
});
});
describe("Performance testing", () => {
describe("20 attributes, 4000 team members", async () => {
// In tests, the performance is actually really bad than real world. So, skipping this test for now
@@ -346,7 +465,6 @@ describe("findTeamMembersMatchingAttributeLogicOfRoute", () => {
numTeamMembers: 4000,
numAttributesUsedPerTeamMember: 10,
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
@@ -138,6 +138,10 @@ const replaceFieldTemplateVariableWithOptionLabel = ({
* Utilities to handle compatiblity when attribute's type changes
*/
const attributeChangeCompatibility = {
/**
* FIXME: It isn't able to handle a case where for SINGLE_SELECT attribute, the queryValue->valueType is ["multiselect"]. It happens for select_any_in operator
* So, don't use it till that is fixed.
*/
getRaqbFieldTypeCompatibleWithQueryValue: function getRaqbFieldTypeCompatibleWithQueryValue({
attributesQueryValue,
raqbField,
@@ -163,6 +167,8 @@ const attributeChangeCompatibility = {
},
/**
* Ensure the attribute value if of type same as the valueType in queryValue
* FIXME: It isn't able to handle a case where for SINGLE_SELECT attribute, the queryValue->valueType is ["multiselect"]. It happens for select_any_in operator
* So, don't use it till that is fixed.
*/
ensureAttributeValueToBeOfRaqbFieldValueType: function ensureAttributeValueToBeOfRaqbFieldValueType({
attributeValue,
@@ -197,11 +203,14 @@ function getAttributesData({
const compatibleValueForAttributeAndFormFieldMatching = compatibleForAttributeAndFormFieldMatch(value);
// We do this to ensure that correct jsonLogic is generated for an existing route even if the attribute's type changes
acc[attributeId] = attributeChangeCompatibility.ensureAttributeValueToBeOfRaqbFieldValueType({
attributeValue: compatibleValueForAttributeAndFormFieldMatching,
attributesQueryValue,
attributeId,
});
// acc[attributeId] = attributeChangeCompatibility.ensureAttributeValueToBeOfRaqbFieldValueType({
// attributeValue: compatibleValueForAttributeAndFormFieldMatching,
// attributesQueryValue,
// attributeId,
// });
// Right now we can't trust ensureAttributeValueToBeOfRaqbFieldValueType to give us the correct value
acc[attributeId] = compatibleValueForAttributeAndFormFieldMatching;
return acc;
}, {} as Record<string, string | string[]>);
@@ -262,11 +271,14 @@ function getAttributesQueryBuilderConfig({
const attributesQueryBuilderConfigFieldsWithCompatibleListValues = Object.fromEntries(
Object.entries(attributesQueryBuilderConfig.fields).map(([raqbFieldId, raqbField]) => {
const raqbFieldType = attributeChangeCompatibility.getRaqbFieldTypeCompatibleWithQueryValue({
attributesQueryValue,
raqbField,
raqbFieldId,
});
// const raqbFieldType = attributeChangeCompatibility.getRaqbFieldTypeCompatibleWithQueryValue({
// attributesQueryValue,
// raqbField,
// raqbFieldId,
// });
// Right now we can't trust getRaqbFieldTypeCompatibleWithQueryValue to give us the correct type
const raqbFieldType = raqbField.type;
return [
raqbFieldId,