percolatestudio / meteor-google-api

A simple API encapsulating some common patterns regarding Google's APIs
https://atmospherejs.com/percolate/google-api
MIT License
48 stars 30 forks source link

GoogleApi.post() - creating blank contact #23

Closed petr24 closed 9 years ago

petr24 commented 9 years ago

I am working with google contacts, and so far, getting contacts, updating them, and deleting them is all working, but for whatever reason when I use GoogleApi.post() its not sending the data to google, it goes through and successfully creates a contact, except that contact is completely blank. I know its not an xml issue when creating the contact because I put that through google playground and it worked smoothly. Not sure what might be causing this.

var url = '/m8/feeds/contacts/default/full';
var options = {
    'headers': {
        "Content-Type": "application/atom+xml" 
    },
    //also tried 'data': atomEntry, which errors out
    'body': atomEntry
}

GoogleApi.post(url, options, function (err, data) {
    if (err) {
        console.log("create error", err);
    }else {
        console.log("create success", data);
    }
});
petr24 commented 9 years ago

After playing around with this and trying everything I could find, finally fixed the issue. I'll close this out, but someone could see this if they have the same problem. The main issue came from me having to use xml, and when using 'body' or 'data' it apparently treats that as json which is obviously incorrect since google contacts requires one to use xml when creating contacts.

//non working
var options = {
    'headers': {
        "Content-Type": "application/atom+xml" 
    },
    //also tried 'data': atomEntry, which errors out
    'body': atomEntry
}

//working 
var options = {
    'headers': {
        "Content-Type": "application/atom+xml" 
    },
    //use content for xml instead of body or data
    content: atomEntry
}
anurag-itsolvs commented 8 years ago

Hi petr. Could you post full example. I am also having same issue response code is 201.

petr24 commented 8 years ago

Hey @anurag-itsolvs,

Here is what I have, but remember the data needs to be formatted to proper xml before sending the post to google. Their contacts api isn't the best in my opinion, and the xml is very specific or it errors out. Here is the link for the xml format for creating a contact. https://developers.google.com/google-apps/contacts/v3/#creating_contacts


//Helper function I made where I pass data to a function, and it takes care of formatting the data to xml.
var atomEntry = createAtomEntry(infoData);

var url = 'm8/feeds/contacts/default/full?v=3.0&alt=json';

var options = {
    'headers': {
        "Content-Type": "application/atom+xml" 
    },
    content: atomEntry      
}

GoogleApi.post(url, options, function (err, data) {
    if (err) {
        console.log("create rror", err);
    }else {
        console.log('result', data);
    }
});
anurag-itsolvs commented 8 years ago

Hi petr, Thanks for your quick reply. Still I am having issue, Here what I am sending. If I am doing anything wrong please help me.

var xmlData = '<atom:entry xmlns:atom="http://www.w3.org/2005/Atom"'+ 'xmlns:gd="http://schemas.google.com/g/2005">'+ '<atom:category scheme="http://schemas.google.com/g/2005#kind"'+ 'term="http://schemas.google.com/contact/2008#contact"/>'+ 'gd:name'+ 'gd:givenNameAnurag/gd:givenName'+ 'gd:familyNameChoudhary/gd:familyName'+ 'gd:fullNameAnurag Choudhary/gd:fullName'+ '/gd:name'+ 'Notes/atom:content'+ '<gd:email rel="http://schemas.google.com/g/2005#work"'+ 'primary="true"'+ 'address="liz@gmail.com" displayName="E. Bennet"/>'+ '<gd:email rel="http://schemas.google.com/g/2005#home"'+ 'address="liz@example.org"/>'+ '<gd:phoneNumber rel="http://schemas.google.com/g/2005#work"'+ 'primary="true">'+ '(206)555-1212'+ '/gd:phoneNumber'+ ''+ '(206)555-1213'+ '/gd:phoneNumber'+ '<gd:im address="liz@gmail.com"'+ 'protocol="http://schemas.google.com/g/2005#GOOGLE_TALK"'+ 'primary="true"'+ 'rel="http://schemas.google.com/g/2005#home"/>'+ '<gd:structuredPostalAddress'+ 'rel="http://schemas.google.com/g/2005#work"'+ ' primary="true">'+ 'gd:cityMountain View/gd:city'+ 'gd:street1600 Amphitheatre Pkwy/gd:street'+ 'gd:regionCA/gd:region'+ 'gd:postcode94043/gd:postcode'+ 'gd:countryUnited States/gd:country'+ 'gd:formattedAddress'+ '1600 Amphitheatre Pkwy Mountain View'+ '/gd:formattedAddress'+ '/gd:structuredPostalAddress'+ '/atom:entry'

      var postheaders = {
          'Content-Type': 'application/atom+xml'
      };

     var query = {

      'oauth_token': oauth2Client.credentials.access_token,
      'alt': 'json'
     };
     var stringifyQuery = qs.stringify(query);

    var options = {
      host: "www.google.com",
      port: 443,
      path: '/m8/feeds/contacts/default/full?'+ stringifyQuery,
      method: 'POST',
      headers: postheaders,
      content: xmlData
    }

    //then call google rest API  

    var reqPost = https.request(options, function (response) {

      // if err.code === 401 means that Access token has expired.
      if(response.statusCode === 401) {

          console.log("Access token exp!");
          console.log(user.refresh_token);

          // Try to fetch a new one with the help of refresh token.
          refresh.requestNewAccessToken('google', user.refresh_token, function(err, accessToken) {

            // if any error occors
            if(err || !accessToken) { 
              console.log("rfs err: "+  err); 
              return send401Response(); 
            }

            // if successfully get the access token, update in database
            User.findOne({_id: user._id}, function(err, user){

               User.findByIdAndUpdate(
                {_id: user._id}, 
                {access_token :accessToken},
                 function(err, result) {
                  if(err)
                    throw err;
                  console.log(result);
                  console.log("result doneeee!");
                  //done(null, User);
                  makeRequest();
              });
            });

          });
        }

        else {

          // There was another error, handle it appropriately.
          if(response.statusCode === 200)
            {
              var data = '';

              response.on('data', function (moreData) {
                data += moreData;
              });

              response.on('end', function () {
                res.send(data);
              })
            }

            else{
              console.log(response.statusCode)
               res.send("There is some other error!" + response.statusCode);
            }
        }
      });

      reqPost.on('error', function (e) {
        console.error(e);
        response.send({message:"err"});
      });

      reqPost.end();
anurag-itsolvs commented 8 years ago

It is creating new contact but its not sending the data to google.

in sort I am sending this:

{ host: 'www.google.com', port: 443, path: '/m8/feeds/contacts/default/full?oauth_token=ya29.IAL7TOXgFHXzDUaKsD42YP7Bmp_PJeoq3XeoHqeQU3oYwBqQVtenbtUvzGV1GQE94hRb97fK_qSAZMyUhLhO5QY&alt=json', method: 'POST', headers: { 'Content-Type': 'application/atom+xml' }, content: 'gd:namegd:givenNameElizabeth/gd:givenNamegd:familyNameBennet/gd:familyNamegd:fullNameElizabeth Bennet/gd:fullName/gd:nameNotes/atom:content(206)555-1212/gd:phoneNumber(206)555-1213/gd:phoneNumbergd:cityMountain View/gd:citygd:street1600 Amphitheatre Pkwy/gd:streetgd:regionCA/gd:regiongd:postcode94043/gd:postcodegd:countryUnited States/gd:countrygd:formattedAddress1600 Amphitheatre Pkwy Mountain View/gd:formattedAddress/gd:structuredPostalAddress/atom:entry' }

petr24 commented 8 years ago

@anurag-itsolvs

Hey I was under the assumption that you were using this package. Thats the first recommandation I would give, is to use this package, it works really well and automatically handles your tokens. Your code will be much nicer using this vs going manual HTTP route.

I am pasting some code below, I just testing it and it works. So just copy and paste the code (make sure you add this package), inside a function, then call that function and it will create a contact called "Testing Bennet"

And anything inside the else block here means its successful. So in your case the code for success response just put that in the else, no need to check for a certain code for a successful callback.

    //Making xml data for google sync.
    var atomEntry = '<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:gd="http://schemas.google.com/g/2005"><atom:category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"/><gd:name><gd:givenName>Testing</gd:givenName><gd:familyName>Bennet</gd:familyName><gd:fullName>Testing Bennet</gd:fullName></gd:name><atom:content type="text">Notes</atom:content><gd:email rel="http://schemas.google.com/g/2005#work" primary="true" address="liz@gmail.com" displayName="E. Bennet"/><gd:email rel="http://schemas.google.com/g/2005#home" address="liz@example.org"/><gd:phoneNumber rel="http://schemas.google.com/g/2005#work" primary="true">(206)555-1212</gd:phoneNumber><gd:phoneNumber rel="http://schemas.google.com/g/2005#home">(206)555-1213</gd:phoneNumber><gd:im address="liz@gmail.com" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" primary="true" rel="http://schemas.google.com/g/2005#home"/><gd:structuredPostalAddress rel="http://schemas.google.com/g/2005#work" primary="true"><gd:city>Mountain View</gd:city><gd:street>1600 Amphitheatre Pkwy</gd:street><gd:region>CA</gd:region><gd:postcode>94043</gd:postcode><gd:country>United States</gd:country><gd:formattedAddress>1600 Amphitheatre Pkwy Mountain View</gd:formattedAddress></gd:structuredPostalAddress></atom:entry>';

    console.log('atomEntry', atomEntry);

    var url = 'm8/feeds/contacts/default/full?v=3.0&alt=json';
    var options = {
        'headers': {
            "Content-Type": "application/atom+xml" 
        },
        content: atomEntry      
    }

    GoogleApi.post(url, options, function (err, data) {
        if (err) {
            console.log('error ', err);
        }else {
            console.log('data ', data);
        }
    });
anurag-itsolvs commented 8 years ago

Hi petr, Thank you so much. Its working!. Contacts are creating in "Other Contacts" Group. Is there any way to create in "My Contacts" Group.

I was testing this : "gContact:groupMembershipInfo deleted='false' href='http://www.google.com/m8/feeds/groups/default/base/6'/"

But its giving namespace err in xml data.

petr24 commented 8 years ago

@anurag-itsolvs

This is a case of poor documentation on googles end. Add this right before closing out the atom entry xml.

//I use Meteor.user().services.google.email to get users email, but if you have it on hand you can 
//pass the email of user directly, and it should work.
<gContact:groupMembershipInfo deleted='false' 
href='http://www.google.com/m8/feeds/groups/" + Meteor.user().services.google.email + "/base/6'/> 
</atom:entry> //Closing Entry Tag
anurag-itsolvs commented 8 years ago

Hello petr, It is working!. You helped me a lot. Thank you for your nice answer, for my all the issues..

petr24 commented 8 years ago

No problem, glad I could help :)

anurag-itsolvs commented 8 years ago

Hello Petr. I am having some issue related to update google contact API. could you assist me to resolve that. I am getting 400 response code. I am sending data like this:-

xml data is:-

<entry gd:etag=""Q3w4eDVSLyt7I2A9XRJVGE8OQgE."">http://www.google.com/m8/feeds/contacts/anurag%40itsolvs.com/base/449a237c0fc6f288gd:namegd:givenNameAnuragaaaaaaaaaaaa/gd:givenNamegd:familyNameChoudhary/gd:familyNamegd:fullNameAnuragaaaaaaaaaaaa Choudhary/gd:fullName/gd:nameNotes/atom:contentgd:cityindore/gd:citygd:streetbhawarkaun/gd:streetgd:regionmp/gd:regiongd:postcode452001/gd:postcodegd:countryindia/gd:countrygd:formattedAddressindore bhawarkaun mp 452001 india/gd:formattedAddress/gd:structuredPostalAddress88888888/gd:phoneNumber9999999999/gd:phoneNumber

https method:-
{ host: 'www.google.com', port: 443, path: '/m8/feeds/contacts/anurag%40itsolvs.com/base/449a237c0fc6f288?oauth_token=ya29.JAL7JaqJsXmKae5_MMOGUDLhA1hmb8jO2mEqoItAls0bgO_MD7fhEh6meO8rNzbjFMjn&alt=json', method: 'PUT', 'If-Match': '"Q3w4eDVSLyt7I2A9XRJVGE8OQgE."', headers: { 'Content-Type': 'application/atom+xml', 'Content-Length': 1382, 'GData-Version': '3.0' } }

The gd:etag is individual gd$etag Which is each contact , should i use top level of gd$etag?

petr24 commented 8 years ago

Looks like you are putting the if match in the wrong spot. Should be inside headers.

    'headers' : { 
        "Content-Type": "application/atom+xml",
        "If-Match": "etag value goes here"
    }
anurag-itsolvs commented 8 years ago

I did try with this also, but still getting 400 err

{ host: 'www.google.com', port: 443, path: '/m8/feeds/contacts/anurag%40itsolvs.com/base/449a237c0fc6f288?oauth_token=ya29.JAL7JaqJsXmKae5_MMOGUDLhA1hmb8jO2mEqoItAls0bgO_MD7fhEh6meO8rNzbjFMjn&alt=json', method: 'PUT', headers: { 'Content-Type': 'application/atom+xml', 'Content-Length': 1370, 'GData-Version': '3.0', 'If-Match': '"Q3w4eDVSLyt7I2A9XRJVGE8OQgE."' } }

Should I use the top level of etag value? Is there any way to check my contact xml data?

anurag-itsolvs commented 8 years ago

Hi petr, I found https://developers.google.com/oauthplayground/ link, which is quite handy to test google Api's.

I tested my update contact api, I found the issue now its working. Thanks for help.

anurag-itsolvs commented 8 years ago

Hi petr, I am working on google push notification.I am trying to get notification if I create new event.

I am watching event like this:

googleCalendar.events.watch({ auth: oauth2Client, //accessToken calendarId: calendarId, // calendar ID resource: { id: uuID, // unique ID address: 'https://crm.itsolvs.com/google/calendar', // Callback URL type: 'web_hook' // The type of delivery mechanism } }, function(err, results) { console.log(err); if(err) { } });

I am getting google watch event response successfully :

{ expiration: "1451925670000" id: "c208d8c4-4701-4b9f-907b-edfb487a635c" kind: "api#channel" resourceId: "mZYd9lVa31BF1G8XMPIsqsh98dQ" resourceUri: "https://www.googleapis.com/calendar/v3/calendars/64udo0ukou686hf5fioettb698@group.calendar.google.com/events?alt=json" }

After that what should I do? as far as I understand the concept of google calendar push notification, I should have get the sync message along with watch response but I am not getting. If you have any idea please help me how can I get push notification.
Thanks.