You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Bug | POST /contactfilter | "OR" Operators in "Expression" Parameter are Converted into "AND" Operators in the Mailjet Web Application When Creating Contact Filters via the API
#42
Open
ThomasLatham opened this issue
Nov 15, 2023
· 2 comments
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 ANDs 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_tostring 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.tsimport{Client,LibraryResponse,Segmentation}from"node-mailjet";constexecuteNewPostNotificationFlow=async(newPostId: string)=>{constpostTags=["math","programming"];constmailjet=newClient({apiKey: getLocalVariableValue("MAILJET_API_KEY"),apiSecret: getLocalVariableValue("MAILJET_SECRET_KEY"),});constfilterId=awaitgetContactFilterIdFromPostTags(postTags,mailjet);console.log(filterId);};constgetContactFilterIdFromPostTags=async(postTags: string[],mailjet: Client): Promise<number>=>{constdesiredFilterExpression=getFilterExpressionFromPostTags(postTags);console.log(desiredFilterExpression);constgetFiltersRequest: Promise<LibraryResponse<Segmentation.GetContactFilterResponse>>=mailjet.get("contactfilter",{version: "v3"}).request();returngetFiltersRequest.then((result)=>{constpreExistingFilters=result.body.Data.filter((contactFilter)=>{returncontactFilter.Expression===desiredFilterExpression;});if(preExistingFilters.length){returnpreExistingFilters[0].ID;}constcreateFilterRequest: 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(),});returncreateFilterRequest.then((result)=>{returnresult.body.Data[0].ID;}).catch((err)=>{console.log(err);return0;});}).catch((err)=>{console.log(err);return0;});};constgetFilterExpressionFromPostTags=(postTags: string[])=>{returnpostTags.reduce((prev,cur)=>{returnprev+" OR "+`(Contains(tags_subscribed_to, "${cur}"))`;// eslint-disable-next-line quotes},'(Contains(tags_subscribed_to, "all"))');};// Keep the code below hereconstmain=async(): Promise<void>=>{awaitexecuteNewPostNotificationFlow("test-post");};constgetLocalVariableValue=(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 ANDing the conditions to see if that resulted in ORs in the result (in case there was a mix-up or something), but that gave me the same outcome as ORing.
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 and AND operators), but this also didn't help.
I manually created the desired segment with ORed 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:
constgetFilterExpressionFromPostTags=(postTags: string[])=>{returnpostTags.reduce((prev,cur)=>{returnprev+" 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.
The text was updated successfully, but these errors were encountered:
I didn't do a very good job at examining the difference between the Expression returned from the manually-created segment and my own attempts. The correct filter-creating function looks like:
constgetFilterExpressionFromPostTags=(postTags: string[])=>{returnpostTags.reduce((prev,cur,curIdx,arr)=>{return(prev+" OR "+`Contains(tags_subscribed_to,"${cur}")`+(curIdx===arr.length-1 ? ")" : ""));// eslint-disable-next-line quotes},'(Contains(tags_subscribed_to,"all")');};
I needed to wrap the whole complex filter in a single set of parentheses rather than wrapping each predicate in its own set of parentheses.
Well actually, I want to reopen this, even though I got it working. I'm reopening because (a.) it seems to me that the docs don't make it clear that the entire Expression must be wrapped in a single set of parentheses, and further (b.) because it seems that the POST/contactfilter endpoint doesn't behave predictably when its Expression isn't formatted correctly. To explain what I mean:
(a.) I feel that the docs should indicate clearly that, if the user wants to OR together 3 predicates, the following expression will not accomplish that:
"Expression": "(PredicateA) OR (PredicateB) OR (PredicateC)"
but that it should rather be formatted as follows:
"Expression": "(PredicateA OR PredicateB OR PredicateC)"
(b.) I feel that in the first case above, the POST/contactfilter endpoint should either return an error without creating a segment (a suggestion which maybe is outside of the scope of this repo), or at least that these docs should explain that such an Expression will result in the predicates being ANDed together, rather than ORed.
I also think the quotation-mark behavior with the Contains operator in Expressions could use some better understanding/documentation. For example, when there isn't a space between the given contact property and the given value in a Contains operator:
mailjet.post("contactfilter",{version: "v3"}).request({Description: "Will send only to contacts subscribed to the tags: math",Expression: `Contains(tags_subscribed_to,"math")`,Name: "Tags: math",});
then the value doesn't have quotations around it in the web application:
However, when there is a space between the given contact property and the given value in a Contains operator:
mailjet.post("contactfilter",{version: "v3"}).request({Description: "Will send only to contacts subscribed to the tags: math",Expression: `Contains(tags_subscribed_to, "math")`,Name: "Tags: math",});
then the value does have quotations around it in the web application:
Are these points that should be taken up in a PR? Or are they actually quite minor?
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 containsOR
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 theOR
operator in the request'sExpression
parameter.Actual result:
then the created contact filter as viewed in the Mailjet web application
AND
s the conditions which were joined by theOR
operator in the request'sExpression
parameter.Note: The returned
Segmentation.PostContactFilterResponse
'sExpression
reflects what was sent exactly, as does retrieving the contact filter with aGET
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: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 thepostTags
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?
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.OR
andAND
operators), but this also didn't help.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: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.
The text was updated successfully, but these errors were encountered: