Google Contact API version 3 [Prefix and Suffix] - google-contacts-api

I'm currently having trouble with setting the PREFIX and SUFFIX on Google Contacts. I'm using .NET and BATCH contacts from local DB and merging them Google contacts.
Code:
c.Name.GivenName = a.Firstname;
c.Name.FamilyName = a.Surname;
c.Name.NamePrefix = a.Prefix;
c.Name.NameSuffix = a.Suffix;
c.BatchData = new GDataBatchEntryData();
c.BatchData.Id = Guid.NewGuid().ToString();
c.BatchData.Type = ec != null ? GDataBatchOperationType.update : GDataBatchOperationType.insert;
cf.Entries.Add(c.AtomEntry);
var result = cservice.Batch(cf, new Uri(String.Format("https://www.google.com/m8/feeds/contacts/{0}/full/batch", username)));
In debugger i can see the AtomEntry has the correct attributes, but when the batch is executed and BatchResult returns OK (200), then my contacts still don't have the Suffix or Prefix.
So am I doing something wrong or is this a limitation in contacts?
best regards
Thomas

Related

Can you import Stock Options data into Google Sheets

I'm trying to create Stock Options watchlist in Google sheet but its not working. Let's say I need to pull AMZN Jun 4000 Option (AMZN220617C04000000) Bid price in to google sheet but I get an error "resource at url not found"
=TRANSPOSE(IMPORTXML(CONCATENATE("https://finance.yahoo.com/quote",AMZN220617C04000000,"?p=",AMZN220617C04000000,""""),"//tr[bid]"))
Is there an option to make this work ? Or can this be possible in MS Excel running on Mac?
Thank you for your time and Support
You cannot fetch data with importxml in this case because the webpage is built by javascript on your side and not on the server side. Nevertheless, all the data is available in a json which you can get this way
var source = UrlFetchApp.fetch(url).getContentText()
var jsonString = source.match(/(?<=root.App.main = ).*(?=}}}})/g) + '}}}}'
var data = JSON.parse(jsonString)
you can then get informations you need from 'data'
https://docs.google.com/spreadsheets/d/1sTA71PhpxI_QdGKXVAtb0Rc3cmvPLgzvXKXXTmiec7k/copy

EWS Create Contacts converts email to Exchange distinguished name instead of SMTP

I have a C# Console application that uses EWS (Exchange Web Services) to impersonate a user and I create or update his current Contacts list.
In order to determine if I have to create or update his list, I first need to search his existing Contacts for a particular domain name like so:
private static IEnumerable<Contact> GetExistingContacts(ExchangeService service)
{
var domainToFilterOn = "#contoso.com";
SearchFilter sfSearch = new SearchFilter.ContainsSubstring(ContactSchema.EmailAddress1, domainToFilterOn);
FindItemsResults<Item> contacts = service.FindItems(WellKnownFolderName.Contacts, sfSearch, new ItemView(int.MaxValue));
var results = contacts.Cast<Contact>().ToList();
return results;
}
The problem I’m facing is that the method GetExistingContacts() yields 0 results for the given domain name which is wrong since I know I have a bunch of Contacts holding the #contoso.com domain name inside the EmailAddre1 field.
After a little bit of digging and testing, I finally figure out why the method wasn’t returning any results and the reason was because the email addresses are stored in the Exchange distinguished name instead of the SMTP format.
To further my investigation, I created a few new Contacts with fake/non-existing #contoso.com domain name like: test#contoso.com, gazou#contoso.com, etc.
To my surprise, the GetExistingContacts() method started to return these fake Contacts.
The conclusion is that whenever I create new Contacts that have resolvable email addresses, then these Contacts are stored using the Exchange distinguished name but when I create new Contacts that have non-resolvable email addresses, then these Contacts are stored as SMTP (which are returned by my GetExistingContacts() method).
How do I start fixing this?
Is my search method wrong? Is there another way to search inside the EmailAddress1 field?
Meanwhile, I managed to find a workaround using the .Load() method of the Contact object but this workaround seems ugly and costly in terms of execution time.
I basically get all Contacts, loop and call the Load() method, then add them to a List() and make a Linq query to filter the results. If my user has 800 Contacts, that takes a long time to Load() everyone of them.
Here’s the example:
private static IEnumerable<Contact> GetCurrentContacts(ExchangeService service)
{
var contacts = new List<Contact>();
var data = service.FindItems(WellKnownFolderName.Contacts, new ItemView(int.MaxValue));
foreach (var item in data.Items)
{
if (item is Contact)
{
item.Load();
contacts.Add(item as Contact);
}
}
var result = contacts.Where(x => x.EmailAddresses[EmailAddressKey.EmailAddress1].Address.Contains("#contoso.com")).ToList();
return result;
}
Needless to say, I don't think that is the correct approach although it works.
Another alternative I’ve tried was to force the RoutingType to SMTP thinking it would create the new Contact in the SMTP format as opposed to the Exchange distinguished name but unfortunately, the email address still gets stored in the Exchange distinguished name disregarding the fact that I forced the RoutingType like so:
var email = new EmailAddress();
email.Address = "goodemail#contoso.com";
email.RoutingType = "SMTP";
Contact contact = new Contact(service);
contact.EmailAddresses[EmailAddressKey.EmailAddress1] = email;
...
contact.Save();
If anyone can help me shed some light on this, that would be great!
Thanks in advance
If the SMTP address your trying to use for a Contact is visible (or resolvable) in the Global address list then the X500 address of Directory entry will be used to track the Contact to the Directory entry (this is by design). If you want to return the SMTP address instead of the X500 address when you retrieve the contacts all you need to do is make use you do a GetItem or Load in the Managed API on the contact or contacts in question if you have multiple contact use LoadPropertiesFromItems https://blogs.msdn.microsoft.com/exchangedev/2010/03/16/loading-properties-for-multiple-items-with-one-call-to-exchange-web-services/
You can override this behavior by setting the extended properties on the contacts directly see https://social.technet.microsoft.com/Forums/exchange/en-US/2b375c56-bee1-4d88-b638-f95649ef964a/use-ews-create-a-contact-which-has-a-same-email-address-in-gal-it-will-show-up-with-x500-formatting?forum=exchangesvrdevelopment but I would recommended you stay with the default behavior you can make you code more efficient using batch Loads.
Thank you #Glen Scales, the LoadPropertiesForItems() helped.
This is the final result if anyone cares:
private static IEnumerable<Contact> GetExistingContacts(ExchangeService service)
{
var contacts = new List<Contact>();
var filterContactsOnDomain = "#contoso.com";
var data = service.FindItems(WellKnownFolderName.Contacts, new ItemView(int.MaxValue));
if (data.TotalCount > 0)
{
service.LoadPropertiesForItems(data, new PropertySet(ContactSchema.EmailAddress1));
foreach (var item in data.Items)
{
if (item is Contact)
{
contacts.Add(item as Contact);
}
}
}
var result = contacts.Where(x => x.EmailAddresses[EmailAddressKey.EmailAddress1].Address.ToLower().Contains(filterContactsOnDomain.ToLower())).ToList();
return result;
}
The only thing I dislike is having to get all Contacts (800 of them), load the EmailAddress1 field for all 800 of them, loop and add those 800 Contacts to a list and then, filter on that list...
I guess it would've been nice to have the ability to search (or filter) directly on an X500 email address format thus not having to fetch all 800 Contacts.
Oh well...
Thanks again #Glen Scales

Update Phone number on Google Apps user

Hi im using Advanced Services in Google Apps Script.
Im trying to add a number to the users profile.
var userValue = 'test#company.com';
var phoneValue = 017236233;
var users = AdminDirectory.Users.get(userValue);
for (var i = 0; i < users.length; i++) {
AdminDirectory.Users.update(users[i].phones[].primary, phoneValue);
}
The last part is the one i am not certain on. It fails with "Syntax error. (line 22, file "Code")"
Looking at the autocomplete on the update method you have to give it a User resource and a userKey (the user primary e-mail).
So this line of code should be:
AdminDirectory.Users.update(userResource, userPrimaryEmail);
Since you just want to add a phone your User resource can only contain this:
var userResource = {
phones:[{
value: phoneValue
}]
}
However be aware that this would update the whole list of phones and overwrite older values.
Also, note that the get method your are using doesn't return a list of User resources but a single User resource. You could use that same resource, update it and send it back.
So what you're looking would be:
var userPrimaryEmail = 'test#company.com';
var phoneValue = 017236233;
var user = AdminDirectory.Users.get(userPrimaryEmail);
// If user has no phones add a 'phones' empty list to the user resource
if (! user.phones){
user.phones = [];
}
user.phones.push(
{
value: phoneValue,
type: "mobile" // Could be 'home' or 'work' of whatever is allowed
}
)
AdminDirectory.Users.update(user, userPrimaryEmail);
A syntax error means that the code you wrote isn't valid. More information on syntax errors is available on the Apps Script troubleshooting guide.

Is it possible to impersonate domain's users with Google Drive API using Google Apps Script?

I am a head of studies and school administrator of our Google Apps for Education.
I used Google Apps Script for a lot of applications (control of absences, sending emails, automatic reporting, ScriptDb databases and more) using gas services. It's fantastic.
Basically I need to create a folder structure (years, courses, teachers, ...) within the Google Drive of students.
With Google Apps Script services I can do it easily but then the folders belong to the creator (administrator) and I think then users spend the administrator storage quota. This does not interest me.
(Yes, I can make an application to be executed by the users and create the structure in its Google Drive, but I'd rather do it in an automated manner and without intervention)
To create this documents (and folders) in Google Drive users (teachers, students, ...) have adapted the code provided by Waqar Ahmad in this response [ Add an writer to a spreadsheet ... ]
That allows me to take possession of documents of other users to make updates using the Google Document List API (Google Apps administrative access to impersonate a user of the domain) and also have adapted to create folders and files on other Google Drive users. It works perfectly. I mention here:
How to add a Google Drive folder ...
But now, the version 3 of the Google Documents List AP, has been officially deprecated and encourage us to work with the Google API Drive.
I tried to do the same with this new Google API. Has anyone managed to do this? Is it possible? I have no idea where to start!
Thank you.
Sergi
Updated:
This is the code i'm working but I get an "invalid request" error:
(...)
var user = e.parameter.TB_email // I get user from a TextBox
//https://developers.google.com/accounts/docs/OAuth2ServiceAccount
//{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}
//{Base64url encoded header}
var header = '{"alg":"RS256","typ":"JWT"}'
var header_b64e = Utilities.base64Encode(header)
//{Base64url encoded claim set}
var t_ara = Math.round((new Date().getTime())/1000) // now
var t_fins = t_ara + 3600 // t + 3600 sec
var claim_set = '{"iss":"1111111111-xxxxxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com",'+
'"prn":"' + user + '",' +
'"scope":"https://www.googleapis.com/auth/prediction",'+
'"aud":"https://accounts.google.com/o/oauth2/token",'+
'"exp":'+t_fins+','+
'"iat":'+t_ara+'}'
// where '1111111111-xxxxxxxxxxx... is my CLIENT-ID (API Access -> Service Account)
var claim_set_b64e = Utilities.base64Encode(claim_set)
claim_set_b64e = claim_set_b64e.replace(/=/g,'')
var to_sign = header_b64e + '.' + claim_set_b64e
// [signature bytes] ??? // password 'isnotasecret???'
var key_secret = DocsList.getFileById('0Biiiiiiiiii-XXXXXXXXXXX').getBlob().getBytes()
// where '0Biiiiiiiiii-XXXXXXXXXXX'... is my p12 file (key_secret) uploaded to GDRive
// I don't know if this is correct !!!
var sign = Utilities.base64Encode(Utilities.computeHmacSha256Signature(to_sign, key_secret))
var JWT_signed = to_sign + '.' + sign
JWT_signed = JWT_signed.replace(/=/g,'')
// Token Request /////////////////////////////////////////////////////////////
var url = 'https://accounts.google.com/o/oauth2/token'
//var url = 'https%3A%2F%2Faccounts.google.com%2Fo%2Foauth2%2Ftoken' ???
//var url = 'https:' + '%2F%2Faccounts.google.com%2Fo%2Foauth2%2Ftoken' ???
var parameters = {
"method" : "POST",
"payload" : '"' + 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=' + JWT_signed + '"',
"contentType" : "application/x-www-form-urlencoded"
}
var content = UrlFetchApp.fetch(url,parameters) //.getContentText()
// Token Request end ////////////////////////////////////////////////////////
And I get an "Invalid Request" and not a JSON with the token
The 2 first parts ( header & claim set ) are OK. The result are equal to the result of Google OAuth page.
I don't know if the signature part are correct or if the error is in the token request.
The issue with your example above is that it it's computing the signature with hmacsha256. You need to use rsasha256. There are two service account libraries for apps script right now. One that I put together is:
https://gist.github.com/Spencer-Easton/f8dad65932bff4c9efc1
The issue with both libraries is they are derived from jsrsa which runs very slow on the server side.

How to search within Google for Business domain?

Specifically users.
Currently I have script like this:
function findUsers(s) {
var users = UserManager.getAllUsers();
var r = new Array;
Logger.log("User Count" + users.length );
for( var i = 0 ; i < users.length ; i++ )
{
var u = users[i];
var n = formatUserName( u );
n = n.toUpperCase();
if( n.indexOf( s.toUpperCase() ) > -1 )
{
r.push(u);
}
}
return r;
}
This works, but is a little slow and I know it will get slower, as more users are migrated because of how UserManager.getAllUsers() currently works
So before I go off and look at ways of 'caching' the domain data myself...I'm wondering if there is a efficient way to search for objects within our domain via APIs or GAScript objects?
When you use the control panel for the domain you can search, fast and efficiently...
For my current task, I may abandon searching the domain myself and require the correct user name.
Here are your choices
1) Make use of the Google Apps Profiles API ( https://developers.google.com/google-apps/profiles/#Retrieving ) from within Google Apps Script. This requires use of oAuth. If you haven't used oAuth, try the oAuth Service Library ( https://developers.google.com/apps-script/notable-script-libraries ) to ease your pain of getting it to work.
2) The second option is what you've already noted. Cache the results in, say, a spreadsheet.
3) Irrespective of 1 or 2, you should open an issue in the Issue tracker if there isn't one already.