jasonjoh / node-calendar-sync

This is a sample Node.js app that connects to Office 365 to do calendar sync.
Other
42 stars 35 forks source link

outlook.contacts API #4

Open staminna opened 7 years ago

staminna commented 7 years ago

Hi Jason,

I'm trying to get the contacts from Outlook, and I went ahead and added 'https://outlook.office.com/contacts.readwrite' to the scope variable in authhelper.js and granted permissions in apps.dev.microsoft.com. Then, I have added in app.js

var getContacts = { url: requestUrl, token: token, '$select': 'EmailAddresses,GivenName,Surname', };

and

outlook.base.makeApiCall(getContacts, function(error, response) { if (error) { console.log(JSON.stringify(error)); res.send(JSON.stringify(error)); } else { if (response.statusCode !== 200) { console.log('Contacts API Call returned ' + response.statusCode); res.send('API Call returned ' + response.statusCode); } else { var nextLink = response.body['@odata.nextLink']; if (nextLink !== undefined) { req.session.syncUrl = nextLink; } var deltaLink = response.body['@odata.deltaLink']; if (deltaLink !== undefined) { req.session.syncUrl = deltaLink; } res.send(pages.syncPage(email, response.body.value)); } } });

Though when I click sync, node crashes with

Contacts API Call returned 400 _http_outgoing.js:346 throw new Error('Can\'t set headers after they are sent.'); ^

Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11) at ServerResponse.header (/Users/jorge/webapp/project/AdvGest/website/node_modules/node-calendar-sync/ node_modules/express/lib/response.js:719:10) at ServerResponse.send (/Users/jorge/webapp/project/AdvGest/website/node_modules/node-calendar-sync/no de_modules/express/lib/response.js:164:12) at /Users/jorge/webapp/project/AdvGest/website/node_modules/node-calendar-sync/app.js:181:13 at Request._callback (/Users/jorge/webapp/project/AdvGest/website/node_modules/node-calendar-sync/node _modules/node-outlook/version-2.js:113:9) at Request.self.callback (/Users/jorge/webapp/project/AdvGest/website/node_modules/node-calendar-sync/ node_modules/request/request.js:186:22) at emitTwo (events.js:106:13) at Request.emit (events.js:191:7) at Request. (/Usersbash-3.2$

staminna commented 7 years ago

Went ahead and found out example from your other repository. Thanks!

jasonjoh commented 7 years ago

Awesome! Sorry I didn't respond sooner, I was out for the holidays :)

staminna commented 7 years ago

Hi Jason,

I lost the code where I had this working, and now I can't get more than 10 contacts. Here's the code

var headers = {
    Prefer: [
      'odata.track-changes',
      'odata.maxpagesize=100', // number of contacts.. not working
      'odata.maxpages=30'
    ]
  };

var getContacts = {
    url: requestUrl,
    token: token,
    headers: headers,
    query: params,
    '$select': 'EmailAddresses,GivenName,Surname'
  };

outlook.contacts.getContacts({token: token, odataParams: getContacts},
  function(error, result){
    if (error) {
      console.log('getMessages returned an error: ' + error);
    }
    else if (result) {
      console.log(result);
      console.log('getMessages returned ' + result.value.length + ' messages.');
      result.value.forEach(function(message) {
        console.log(message.EmailAddresses);
      });
    }
  });

Thought this should work. What do you think, am I missing something?

jasonjoh commented 7 years ago

Try removing the 'odata.maxpages=30' line. I don't believe that's a recognized header but it may be interfering there.

staminna commented 7 years ago

I removed the line but It didn't work. Something wrong with the headers variable Prefer not working. I have tried to use this variables on the API function example from dev.outlook.com unless I used $top = 1000 which returns 251 contacts, from A to I and then, error "Error: write after end" but not all of them.

https://msdn.microsoft.com/en-us/office/office365/api/contacts-rest-operations

jasonjoh commented 7 years ago

The only other thing I can see wrong is this:

var getContacts = {
    url: requestUrl,
    token: token,
    headers: headers,
    query: params,
    '$select': 'EmailAddresses,GivenName,Surname'
  };

The select should be part of the params in that case. Not sure if that's causing your issue but doesn't hurt to try it. Maybe change to this (assuming there's nothing already in params):

var getContacts = {
    url: requestUrl,
    token: token,
    headers: headers,
    query: {'$select': 'EmailAddresses,GivenName,Surname'}
  };
staminna commented 7 years ago

Thanks for not closing the issue. I still can't get around to more then 244 contacts. Are you familiar with express? I am getting "Error: Can't set headers after they are sent." trying to pass the data back to angular.

app.get('/sendcontacts', function(req, res) {
 console.log('we are in express route');

  var email = req.session.email;
  var token = req.session.access_token;

var queryParams = {
  '$select': 'GivenName,Surname,EmailAddresses',
  '$orderby': 'GivenName asc',
  '$top': 1000
};

 // Set the required headers for sync
  var headers = {
    Prefer: [ 
      // Enables sync functionality
      'odata.track-changes',
      // Requests only 5 changes per response
      'odata.maxpagesize=100'
    ]
  };

 var requestUrl = req.session.syncUrl;
  if (requestUrl === undefined) {
    // Calendar sync works on the CalendarView endpoint
    requestUrl = outlook.base.apiEndpoint() + '/Me/CalendarView';
  }
  var apiOptions = {
    url: requestUrl,
    token: token,
    headers: headers,
    query: queryParams
  };

// Set the API endpoint to use the v2.0 endpoint
outlook.base.setApiEndpoint('https://outlook.office.com/api/v2.0');
// Set the anchor mailbox to the user's SMTP address
outlook.base.setAnchorMailbox(email);

outlook.contacts.getContacts({token: token, odataParams: queryParams},
  function(error, result){
    if (error) {
      console.log('getContacts returned an error: ' + error);
      console.log(error);
      res.end();
    } else if (result) {
      console.log('getContacts returned ' + result.value.length + ' contacts.');

      result.value.forEach(function(contact) {
      var email = contact.EmailAddresses[0] ? contact.EmailAddresses[0].Address : 'NONE';

      if (email !=='NONE') {

        // req.body.serverMessage = "Express is rendering to Angular" /* adding a new field to send it to the angular Client */  
        res.json(email);
        console.log(req.body);  /* Sending the respone back to the angular Client */
      }
      });

      // res.end();
    }
  });

});

Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:356:11) at ServerResponse.header (/Users/jorge/webapp/project/sitegenie/website/node_modules/node-calendar-sync/node_modules/express/lib/response.js:719:10) at ServerResponse.send (/Users/jorge/webapp/project/sitegenie/website/node_modules/node-calendar-sync/node_modules/express/lib/response.js:164:12) at ServerResponse.json (/Users/jorge/webapp/project/sitegenie/website/node_modules/node-calendar-sync/node_modules/express/lib/response.js:250:15) at /Users/jorge/webapp/project/sitegenie/website/node_modules/node-calendar-sync/app.js:242:13

I think this is because the code in " result.value.forEach(function(contact)" is async and is already sending the headers, so I can't send them twice. Is that right?

The next error in the stack points to res.json(mail), where I try to send the email address in json format to angular.