skip to Main Content

This is my code file where I am trying to add filters on inbox and sent items using Microsoft graph API:

const GraphApiTest = ({ email }) => {
  const emailsPerPage = 5;
  const [inboxPage, setInboxPage] = useState(1);
  const [sentPage, setSentPage] = useState(1);
  const [inboxEmails, setInboxEmails] = useState([]);
  const [selectedEmail, setSelectedEmail] = useState(null);
  const [sentEmails, setSentEmails] = useState([]);
  const [loading, setLoading] = useState("");
  const [emailsData, setEmailsData] = useState({});
  const msalInstance = new PublicClientApplication(msalConfig);
  const users = [
    "[email protected]",
    "[email protected]",
    "[email protected]",
  ]; // Add the user emails here
  useEffect(() => {
    if (email) {
      loginAndFetchEmails();
    }
  }, [email]);
  const loginAndFetchEmails = async () => {
    try {
      await msalInstance.initialize();
      const accounts = msalInstance.getAllAccounts();
      if (accounts.length === 0) {
        await msalInstance.loginPopup({
          scopes: ["Mail.Read", "Mail.Send", "User.Read"],
        });
      }
      const account = msalInstance.getAllAccounts()[0];
      let tokenResponse;
      try {
        tokenResponse = await msalInstance.acquireTokenSilent({
          account,
          scopes: ["Mail.Read", "Mail.Send", "User.Read"],
        });
      } catch (silentError) {
        console.warn("Silent token acquisition failed, fallback to popup.");
        tokenResponse = await msalInstance.acquireTokenPopup({
          account,
          scopes: ["Mail.Read", "Mail.Send", "User.Read"],
        });
      }
      const accessToken = tokenResponse.accessToken;
      setLoading(true);
      // Fetch received emails (Inbox)
      const fetchedEmails = {};
      for (const user of users) {
        const inboxResponse = await axios.get(
         `https://graph.microsoft.com/v1.0/users/${user}/mailFolders('inbox')/messages?$filter=from/emailAddress/address eq '${email}'`,
        {
          headers: {
            Authorization: `Bearer ${accessToken}`,
          },
        }
        );
      // Fetch sent emails
      const sentResponse = await axios.get(
        `https://graph.microsoft.com/v1.0/users/${user}/mailFolders('sentitems')/messages`,
        {
          headers: {
            Authorization: `Bearer ${accessToken}`,
          },
        }
      );
      fetchedEmails[user] = {
        inbox: inboxResponse.data.value,
        sent: sentResponse.data.value,
      };
    }
    // Update state with emails for all users
    setEmailsData(fetchedEmails);
    } catch (error) {
      console.error("Error during login or fetching emails:", error);
    } finally{
      setLoading(false);
    }
  };
  const paginateEmails = (emails, page) => {
    const start = (page - 1) * emailsPerPage;
    const end = start + emailsPerPage;
    return emails.slice(start, end);
  };
  const inboxEmailsPaginated = paginateEmails(inboxEmails, inboxPage);
  const sentEmailsPaginated = paginateEmails(sentEmails, sentPage);
  const totalInboxPages = Math.ceil(inboxEmails.length / emailsPerPage);
  const totalSentPages = Math.ceil(sentEmails.length / emailsPerPage);
  const openModal = (email) => {
    setSelectedEmail(email);
  };
  const closeModal = () => {
    setSelectedEmail(null);
  };
  
export default GraphApiTest;

In this code when I am adding filter on inbox folders its working fine and giving me the filtered emails from that email address but for sent items when I try this:

const sentResponse = await axios.get(
  `https://graph.microsoft.com/v1.0/users/${user}/mailFolders/sentitems/messages?$filter=toRecipients/any(t:t/emailAddress/address eq '${email}')`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  }
);

It gives the error 400 and says invalid uri. How can I add a filter on sent items folder too? I have tried many ways but whenever I add a filter on sent items it always gives error while for inbox it works fine. Here is the error message:

enter image description here

and this error on console:

response
:
"{"error":{"code":"BadRequest","message":"There is an unterminated string literal at position 34 in ‘"recipients:[email protected]’.","innerError":{"date":"2024-12-09T06:49:41","request-id":"61e6203d-db9f-4948-85e8-d41ddf5db99a","client-request-id":"61e6203d-db9f-4948-85e8-d41ddf5db99a"}}}"
responseText
:
"{"error":{"code":"BadRequest","message":"There is an unterminated string literal at position 34 in ‘"recipients:[email protected]’.","innerError":{"date":"2024-12-09T06:49:41",

2

Answers


  1. The toRecipients property doesn’t support filtering, but you can use $search query parameter and search for messages by recipients

    const sentResponse = await axios.get(
      `https://graph.microsoft.com/v1.0/users/${user}/mailFolders/sentitems/messages?$search="recipients:${email}"`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    
    Login or Signup to reply.
  2. I agree with @user2250152, filtering on toRecipients property is not supported. Alternatively, make use of $search query parameter.

    In my case, I used below code files in react app to generate access token and filter retrieved emails:

    GraphApiTest.js:

    import React, { useState, useEffect, useCallback, useMemo } from 'react';
    import axios from 'axios';
    import { PublicClientApplication } from '@azure/msal-browser';
    
    const msalConfig = {
      auth: {
        clientId: 'appId',
        authority: 'https://login.microsoftonline.com/tenantId',
        redirectUri: 'http://localhost:3000',
      },
    };
    
    const GraphApiTest = ({ email }) => {
      const emailsPerPage = 5;
      const [inboxPage, setInboxPage] = useState(1);
      const [sentPage, setSentPage] = useState(1);
      const [inboxEmails, setInboxEmails] = useState([]);
      const [sentEmails, setSentEmails] = useState([]);
      const [loading, setLoading] = useState(false);
    
      const msalInstance = useMemo(() => new PublicClientApplication(msalConfig), []);
      const users = useMemo(() => ['[email protected]'], []);
    
      const paginateEmails = (emails, page) => {
        const start = (page - 1) * emailsPerPage;
        const end = start + emailsPerPage;
        return emails.slice(start, end);
      };
    
      const loginAndFetchEmails = useCallback(async () => {
        try {
          await msalInstance.initialize();
          const accounts = msalInstance.getAllAccounts();
          if (accounts.length === 0 && msalInstance.inProgress !== 'login') {
            await msalInstance.loginPopup({
              scopes: ['Mail.Read', 'Mail.Send', 'User.Read'],
            });
          }
    
          const account = msalInstance.getAllAccounts()[0];
          let tokenResponse;
          try {
            tokenResponse = await msalInstance.acquireTokenSilent({
              account,
              scopes: ['Mail.Read', 'Mail.Send', 'User.Read'],
            });
          } catch (silentError) {
            console.warn('Silent token acquisition failed, fallback to popup.');
            tokenResponse = await msalInstance.acquireTokenPopup({
              account,
              scopes: ['Mail.Read', 'Mail.Send', 'User.Read'],
            });
          }
          const accessToken = tokenResponse.accessToken;
          setLoading(true);
    
          const fetchedInboxEmails = [];
          const fetchedSentEmails = [];
          for (const user of users) {
            const inboxResponse = await axios.get(
              `https://graph.microsoft.com/v1.0/users/${user}/mailFolders('inbox')/messages?$filter=from/emailAddress/address eq '${email}'`,
              {
                headers: {
                  Authorization: `Bearer ${accessToken}`,
                },
              }
            );
    
            const sentResponse = await axios.get(
              `https://graph.microsoft.com/v1.0/users/${user}/mailFolders('sentitems')/messages?$search="recipients:${email}"`,
              {
                headers: {
                  Authorization: `Bearer ${accessToken}`,
                },
              }
            );
    
            fetchedInboxEmails.push(...inboxResponse.data.value);
            fetchedSentEmails.push(...sentResponse.data.value);
          }
    
          setInboxEmails(fetchedInboxEmails);
          setSentEmails(fetchedSentEmails);
        } catch (error) {
          console.error('Error during login or fetching emails:', error);
        } finally {
          setLoading(false);
        }
      }, [email, msalInstance, users]);
    
      useEffect(() => {
        if (email) {
          loginAndFetchEmails();
        }
      }, [email, loginAndFetchEmails]);
    
      const inboxEmailsPaginated = paginateEmails(inboxEmails, inboxPage);
      const sentEmailsPaginated = paginateEmails(sentEmails, sentPage);
      const totalInboxPages = Math.ceil(inboxEmails.length / emailsPerPage);
      const totalSentPages = Math.ceil(sentEmails.length / emailsPerPage);
    
      return (
        <div>
          {loading && <div>Loading...</div>}
          <div>
            <h3>Inbox</h3>
            <ul>
              {inboxEmailsPaginated.map((emailItem) => (
                <li key={emailItem.id}>
                  {emailItem.subject}
                </li>
              ))}
            </ul>
            <button onClick={() => setInboxPage(inboxPage - 1)} disabled={inboxPage === 1}>
              Previous
            </button>
            <button onClick={() => setInboxPage(inboxPage + 1)} disabled={inboxPage === totalInboxPages}>
              Next
            </button>
          </div>
    
          <div>
            <h3>Sent Items</h3>
            <ul>
              {sentEmailsPaginated.map((emailItem) => (
                <li key={emailItem.id}>
                  {emailItem.subject}
                </li>
              ))}
            </ul>
            <button onClick={() => setSentPage(sentPage - 1)} disabled={sentPage === 1}>
              Previous
            </button>
            <button onClick={() => setSentPage(sentPage + 1)} disabled={sentPage === totalSentPages}>
              Next
            </button>
          </div>
        </div>
      );
    };
    
    export default GraphApiTest;
    

    App.js:

    import React, { useState } from 'react';
    import GraphApiTest from './GraphApiTest';
    
    const App = () => {
      const [email, setEmail] = useState('');
      const [submittedEmail, setSubmittedEmail] = useState('');
      const [isSubmitted, setIsSubmitted] = useState(false);
    
      const handleEmailChange = (e) => {
        setEmail(e.target.value);
      };
    
      const handleEmailSubmit = (e) => {
        e.preventDefault();
        if (email && email.includes('@')) {
          setSubmittedEmail(email);
          setIsSubmitted(true);
        } else {
          alert('Please enter a valid email address!');
        }
      };
    
      return (
        <div style={styles.container}>
          <h1 style={styles.header}>Microsoft Graph API - Email Fetcher</h1>
          
          <form onSubmit={handleEmailSubmit} style={styles.form}>
            <div style={styles.inputContainer}>
              <label htmlFor="email" style={styles.label}>Enter email to filter mails:</label>
              <input
                type="email"
                id="email"
                value={email}
                onChange={handleEmailChange}
                placeholder="Enter email to filter mails"
                style={styles.input}
                required
              />
            </div>
    
            <button type="submit" style={styles.button}>Fetch Emails</button>
          </form>
    
          {isSubmitted && submittedEmail && (
            <GraphApiTest email={submittedEmail} />
          )}
        </div>
      );
    };
    
    const styles = {
      container: {
        maxWidth: '800px',
        margin: '0 auto',
        padding: '20px',
        fontFamily: 'Arial, sans-serif',
        backgroundColor: '#f9f9f9',
        borderRadius: '8px',
        boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
      },
      header: {
        textAlign: 'center',
        color: '#333',
        marginBottom: '20px',
      },
      form: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
      },
      inputContainer: {
        marginBottom: '20px',
        width: '100%',
      },
      label: {
        fontSize: '16px',
        fontWeight: 'bold',
        display: 'block',
        marginBottom: '10px',
      },
      input: {
        width: '100%',
        padding: '10px',
        fontSize: '16px',
        borderRadius: '5px',
        border: '1px solid #ccc',
        boxSizing: 'border-box',
      },
      button: {
        backgroundColor: '#0078d4',
        color: 'white',
        border: 'none',
        borderRadius: '5px',
        padding: '10px 20px',
        fontSize: '16px',
        cursor: 'pointer',
        transition: 'background-color 0.3s',
      },
      buttonHover: {
        backgroundColor: '#005a9e',
      },
    };
    
    export default App;
    

    Output:

    enter image description here

    When I visited http://localhost:3000/ in browser and clicked Fetch Emails button after entering email, it asked me to sign in first and retrieved filtered mails successfully as below:

    enter image description here

    "{"error":{"code":"BadRequest","message":"There is an unterminated
    string literal at position 34 in ‘"recipients:[email protected]’."

    In your case, the above error occurred because the $search query value was not properly enclosed with the correct quotes leading to an unterminated string literal error.

    To resolve this, please ensure that the $search query value is correctly framed like this:

    $search="recipients:${email}"
    

    This will properly enclose the value in double quotes and avoid the unterminated string literal error.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search