Description
What is the bug?
Given a user is making a POST
request to the /contactfilter
endpoint (Source Line | API Reference Docs | Developer Guide Docs),
and the user is using Mailjet JS to make the request,
and the request's Expression
body parameter contains OR
operators (e.g., "Expression": "(Contains(tags_subscribed_to, \"all\")) OR (Contains(tags_subscribed_to, \"math\")) OR (Contains(tags_subscribed_to, \"programming\"))"
) (Source Line),
when the user submits the request,
Expected result:
then the created contact filter as viewed in the Mailjet web application should OR
the conditions which were joined by the OR
operator in the request's Expression
parameter.
Actual result:
then the created contact filter as viewed in the Mailjet web application AND
s the conditions which were joined by the OR
operator in the request's Expression
parameter.
Note: The returned Segmentation.PostContactFilterResponse
's Expression
reflects what was sent exactly, as does retrieving the contact filter with a GET
request.
How did you produce it?
For some context, when I add a new post to my blog, I'm wanting to automatically notify those subscribers who are subscribed to the post's tags. My flow, then, is to either reuse a preexisting contact filter for segmenting my contact list based on the post's tags and my contacts' tags_subscribed_to
string
property (an example for one contact could be "math, programming, career"
), or in the case that a contact filter doesn't already exist for the given tags, to create a new one. Here is some code following that flow, using hard-coded values for the post tags:
// playground.ts
import { Client, LibraryResponse, Segmentation } from "node-mailjet";
const executeNewPostNotificationFlow = async (newPostId: string) => {
const postTags = ["math", "programming"];
const mailjet = new Client({
apiKey: getLocalVariableValue("MAILJET_API_KEY"),
apiSecret: getLocalVariableValue("MAILJET_SECRET_KEY"),
});
const filterId = await getContactFilterIdFromPostTags(postTags , mailjet);
console.log(filterId);
};
const getContactFilterIdFromPostTags = async (
postTags: string[],
mailjet: Client
): Promise<number> => {
const desiredFilterExpression = getFilterExpressionFromPostTags(postTags);
console.log(desiredFilterExpression);
const getFiltersRequest: Promise<LibraryResponse<Segmentation.GetContactFilterResponse>> = mailjet
.get("contactfilter", { version: "v3" })
.request();
return getFiltersRequest
.then((result) => {
const preExistingFilters = result.body.Data.filter((contactFilter) => {
return contactFilter.Expression === desiredFilterExpression;
});
if (preExistingFilters.length) {
return preExistingFilters[0].ID;
}
const createFilterRequest: Promise<LibraryResponse<Segmentation.PostContactFilterResponse>> =
mailjet.post("contactfilter", { version: "v3" }).request({
Description: "Will send only to contacts subscribed to the tags: " + postTags.toString(),
Expression: desiredFilterExpression,
Name: "Tags: " + postTags.toString(),
});
return createFilterRequest
.then((result) => {
return result.body.Data[0].ID;
})
.catch((err) => {
console.log(err);
return 0;
});
})
.catch((err) => {
console.log(err);
return 0;
});
};
const getFilterExpressionFromPostTags = (postTags: string[]) => {
return postTags.reduce((prev, cur) => {
return prev + " OR " + `(Contains(tags_subscribed_to, "${cur}"))`;
// eslint-disable-next-line quotes
}, '(Contains(tags_subscribed_to, "all"))');
};
// Keep the code below here
const main = async (): Promise<void> => {
await executeNewPostNotificationFlow("test-post");
};
const getLocalVariableValue = (variableName: string): string | undefined => {
...
};
main();
Running this code via Nodemon, I'll console-log (Contains(tags_subscribed_to, "all")) OR (Contains(tags_subscribed_to, "math")) OR (Contains(tags_subscribed_to, "programming"))
, and I'll successfully create (and on subsequent executions retrieve) the ID of the contact filter defined by the postTags
array. When I view the details of the created segment in the Mailjet web application, however, I'm seeing the following:
What else have you tried?
- I tried
AND
ing the conditions to see if that resulted inOR
s in the result (in case there was a mix-up or something), but that gave me the same outcome asOR
ing. - I tried adding an extra set of parentheses around each condition (just in case this line was pertinent — I think this would only apply in ambiguous distributive-property situations that arise when using both
OR
andAND
operators), but this also didn't help. - I manually created the desired segment with
OR
ed predicates, and retrieving it looked like:(Contains(tags_subscribed_to,"all") OR Contains(tags_subscribed_to,"math") OR Contains(tags_subscribed_to,"programming"))
. This caused me to think that maybe I just had to remove the spaces between the contact property and the value whose containment in the property we're checking, that is to try the following:
const getFilterExpressionFromPostTags = (postTags: string[]) => {
return postTags.reduce((prev, cur) => {
return prev + " OR " + `(Contains(tags_subscribed_to,"${cur}"))`;
// eslint-disable-next-line quotes
}, '(Contains(tags_subscribed_to,"all"))');
};
That also didn't work, though. Oddly, it caused the quotations that can be seen in the above screenshot to disappear. That is, the web application now displayed the created segment as follows (after deleting the other one):
I'm not sure what's going on here. I haven't tried actually using any of these segments; I'm just going strictly off what I see in the web application.