Authenticating, searching, and adding events/entires to the google calendar is working as expected, but deleting results in a 400 bad request error.
The code is mostly copied from google's documentation.
Below googleUri is the link to the calendar entry (created by this same application/user, titled "Event To Delete") that should be removed and ConfigurationManager.AppSettings contains authentication information.
The debug output shows the calendar entry is found, yet deleting is not successful.
This uses Google Calendar API v2 which should still be functioning until 10/2014. Moving to v3 would be nice but, as far as I can tell, offers no way to authenticate with a known user+password (instead using expiring tokens that have require manually entering google credentials (?) ).
Debug.Write ("want to remove: " + googleURI);
// autheticate and get service
CalendarService svc = new CalendarService(ConfigurationManager.AppSettings["GoogleCalendarName"]);
svc.setUserCredentials(ConfigurationManager.AppSettings["GoogleCalendarUsername"], ConfigurationManager.AppSettings["GoogleCalendarPassword"]);
svc.QueryClientLoginToken();
// find the event(s) -- should only be one that can match the googleuri
EventQuery evtquery = new EventQuery(ConfigurationManager.AppSettings["GoogleCalendarPostURI"]);
evtquery.Uri = new Uri(googleURI);
EventFeed evtfeed = svc.Query (evtquery);
//should only be one event in the query
if (evtfeed == null || evtfeed.Entries.Count != 1) {
Debug.Write ("No match or too many matches for " + googleURI); // if this is less than 1, we should panic
return;
}
// we found the right one
EventEntry entry = (EventEntry)(evtfeed.Entries[0]);
Debug.Write ("Title: " + entry.Title.Text);
//hangs here until "The remote server returned an error: (400) Bad Request.
entry.Delete();
The output is:
[0:] want to remove: https://www.google.com/calendar/feeds/default/private/full/77e0tr0e3b4ctlirug30igeop0
[0:] Title: Event To Delete
I've also tried using the batch method to no avial
// https://developers.google.com/google-apps/calendar/v2/developers_guide_dotnet?csw=1#batch
// Create an batch entry to delete an the appointment
EventEntry toDelete = (EventEntry)calfeed.Entries[0];
toDelete.Id = new AtomId(toDelete.EditUri.ToString());
toDelete.BatchData = new GDataBatchEntryData("Deleting Appointment", GDataBatchOperationType.delete);
// add the entry to batch
AtomFeed batchFeed = new AtomFeed(calfeed);
batchFeed.Entries.Add(toDelete);
// run the batch
EventFeed batchResultFeed = (EventFeed)svc.Batch(batchFeed, new Uri(ConfigurationManager.AppSettings["GoogleCalendarPostURI"] ));
// check for succses
Debug.Write (batchResultFeed.Entries [0].BatchData.Status.Code);
if (batchResultFeed.Entries [0].BatchData.Status.Code != 200) {
return;
}
Couldn't figure out what was going on with v2, but I Was able to move to v3 of APIs using a Service Account to autheticate.
using System;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
// for BaseClientService.Initializer
using Google.Apis.Services;
// provides ServiceAccountCredential
using Google.Apis.Auth.OAuth2;
// read in cert
using System.Security.Cryptography.X509Certificates;
namespace LunaIntraDBCalTest
{
public class Program
{
public static string calendarname = "xxxx#gmail.com"; //address is default calendar
public static string serviceAccountEmail = "yyyy#developer.gserviceaccount.com";
public static CalendarService getCalendarService(){
// certificate downloaded after creating service account access at could.google.com
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
// autheticate
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "LunaIntraDB",
});
return service;
}
// create event object that will later be inserted
public static Event createEvent(string Summary, string Location, string Description, DateTime ApptDateTime, double duration ){
// create an event
Event entry= new Event
{
Summary = Summary,
Location = Location,
Description = Description,
Start = new EventDateTime { DateTime = ApptDateTime },
End = new EventDateTime { DateTime = ApptDateTime.AddHours(duration) },
};
return entry;
}
// capture event ID after inserting
public static string insertEvent(Event entry){
var service = getCalendarService ();
EventsResource.InsertRequest insert = service.Events.Insert (entry, calendarname);
// TODO: try catch here; will be throw exception if bad datetime
return insert.Execute().Id;
}
public static void deleteEvent(string id){
// TODO CATCH IF NOT FOUND
// to have access to this calendar, serviceAccountEmail needed permission set by ownner in google calendar
//Calendar cal1 = service.Calendars.Get (calnedarname).Execute();
var service = getCalendarService ();
service.Events.Delete (calendarname, id).Execute ();
}
public static void Main(string[] args)
{
// create event
var entry = createEvent ("TEST", "here", "this is a test", DateTime.Now, 1.5);
string id = insertEvent (entry);
Console.WriteLine("Run to your calendar to see that this event was created... (any key after checking)");
Console.ReadKey();
deleteEvent (id);
Console.WriteLine("Should now be deleted!... (any key to close)");
Console.ReadKey();
}
}
}
Related
I have set up a webhook in my Braintree account https://example.com/Webhook/Accept
I have the site setup to create a Customer & Subscription via a purchase link.
When I make a purchase via the link these are both created ok in the Braintree vault, the webhook fires and I receive 2 notifications.
This all seems great, all is working, except the for 'Check URL' in the Braintree Control panel Webhook section, where im getting a HTTP error 500 internal server error.
I think its a bt_challenge issue on the GET but having tried a few variations I simply cant get it working.
Here is my controller:
public class WebhookController : Controller
{
public IBraintreeConfiguration config = new BraintreeConfiguration();
public ActionResult Accept()
{
CrmDB db = new CrmDB();
Log log;
var gateway = config.GetGateway();
if (Request.HttpMethod == "POST")
{
WebhookNotification webhookNotification = gateway.WebhookNotification.Parse(
Request.Params["bt_signature"],
Request.Params["bt_payload"]
);
string message = string.Format(
"[Webhook Received {0}] | Kind: {1} | Subscription: {2}",
webhookNotification.Timestamp.Value,
webhookNotification.Kind,
webhookNotification.Subscription.Id
);
System.Console.WriteLine(message);
// Save to Db
log = new Log
{
Stamp = DateTime.Now,
App = "api/Webhook/Accept",
IsInsight = true,
Insight = message
};
// Db
db.Log.Add(log);
db.SaveChanges();
return new HttpStatusCodeResult(200);
}
else
{
string msg = gateway.WebhookNotification.Verify(Request.QueryString["bt_challenge"]);
// Save to Db
log = new Log
{
Stamp = DateTime.Now,
App = "Webhook - bt_challenge",
IsInsight = true,
Insight = msg
};
// Db
db.Log.Add(log);
db.SaveChanges();
return Content(msg);
}
}
}
I believe the issue is in the else section (ie HTTP GET) but I cant work out what it is.
Why am I getting the 500 internal error from Webhook control Panel?
I have an AngularJS + C#.NET OnePage Application Website. The goal is to retrieve a JSON from a private Google Spreadsheet that I own with C# (not with AngularJS). I read various Google Sheets Documentation and API, OAuth 2.0 etc. and tried some examples, but none of them seem to work for me. I see there are different ways to access and retrieve data from a Google Spreadsheet, still, they don't work for my case.
Can anyone help me?
Thank you.
Edit: I managed to get a token by creating an Other Application type under Google Developers Console=>Credentials=>Create Client ID. Here's the C# Console Application:
using System;
using Google.GData.Client;
using Google.GData.Spreadsheets;
namespace MySpreadsheetIntegration
{
class Program
{
static void Main(string[] args)
{
////////////////////////////////////////////////////////////////////////////
// STEP 1: Configure how to perform OAuth 2.0
////////////////////////////////////////////////////////////////////////////
// TODO: Update the following information with that obtained from
// https://code.google.com/apis/console. After registering
// your application, these will be provided for you.
string CLIENT_ID = "12345678.apps.googleusercontent.com";
// This is the OAuth 2.0 Client Secret retrieved
// above. Be sure to store this value securely. Leaking this
// value would enable others to act on behalf of your application!
string CLIENT_SECRET = "Gc0230jdsah01jqpowpgff";
// Space separated list of scopes for which to request access.
string SCOPE = "https://spreadsheets.google.com/feeds https://docs.google.com/feeds";
// This is the Redirect URI for installed applications.
// If you are building a web application, you have to set your
// Redirect URI at https://code.google.com/apis/console.
string REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
////////////////////////////////////////////////////////////////////////////
// STEP 2: Set up the OAuth 2.0 object
////////////////////////////////////////////////////////////////////////////
// OAuth2Parameters holds all the parameters related to OAuth 2.0.
OAuth2Parameters parameters = new OAuth2Parameters();
// Set your OAuth 2.0 Client Id (which you can register at
// https://code.google.com/apis/console).
parameters.ClientId = CLIENT_ID;
// Set your OAuth 2.0 Client Secret, which can be obtained at
// https://code.google.com/apis/console.
parameters.ClientSecret = CLIENT_SECRET;
// Set your Redirect URI, which can be registered at
// https://code.google.com/apis/console.
parameters.RedirectUri = REDIRECT_URI;
////////////////////////////////////////////////////////////////////////////
// STEP 3: Get the Authorization URL
////////////////////////////////////////////////////////////////////////////
// Set the scope for this particular service.
parameters.Scope = SCOPE;
// Get the authorization url. The user of your application must visit
// this url in order to authorize with Google. If you are building a
// browser-based application, you can redirect the user to the authorization
// url.
string authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
Console.WriteLine(authorizationUrl);
Console.WriteLine("Please visit the URL above to authorize your OAuth "
+ "request token. Once that is complete, type in your access code to "
+ "continue...");
parameters.AccessCode = Console.ReadLine();
////////////////////////////////////////////////////////////////////////////
// STEP 4: Get the Access Token
////////////////////////////////////////////////////////////////////////////
// Once the user authorizes with Google, the request token can be exchanged
// for a long-lived access token. If you are building a browser-based
// application, you should parse the incoming request token from the url and
// set it in OAuthParameters before calling GetAccessToken().
OAuthUtil.GetAccessToken(parameters);
string accessToken = parameters.AccessToken;
Console.WriteLine("OAuth Access Token: " + accessToken);
////////////////////////////////////////////////////////////////////////////
// STEP 5: Make an OAuth authorized request to Google
////////////////////////////////////////////////////////////////////////////
// Initialize the variables needed to make the request
GOAuth2RequestFactory requestFactory =
new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v1", parameters);
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;
Console.ReadLine();
}
}
}
With this code I have to copy the link I get and paste it in a browser in order to get a token. Is there a way to get this token directly in my app without the need to copy the link manually?
I found another working solution where you don't have to open a browser window which is similar to the solution with the p12-key above.
First, create in the Google Developer Console under Create Credentials a Service account key.
Then, you have to choose a service account type, you can choose App Engine Default Service account and you can download a json file that contains the private key.
With this file, you can create programmatically Google credentials (where "sheets-test.json" is the downloaded json file):
var credential = GoogleCredential.FromStream(new FileStream("Sheets-test.json", FileMode.Open)).CreateScoped(Scopes);
Give the service user (you can find it in the json file in the client_email field) access to your spreadsheet.
Here is the full code that enables you to append a value to the first free row in a spreadsheet, I modified the google .NET quickstart project:
using System;
using System.Collections.Generic;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
namespace SheetApiTest
{
public class SheetApiWithGoogleCredentials
{
static string[] Scopes = { SheetsService.Scope.Spreadsheets };
static string ApplicationName = "Google Sheets API .NET Quickstart";
public void AppendData()
{
// the downloaded jsonn file with private key
var credential = GoogleCredential.FromStream(new FileStream("Sheets-test.json", FileMode.Open)).CreateScoped(Scopes);
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// spreadsheet id - your own spreadsheet id
var spreadsheetId = "11AwV7d1pEPq4x-rx9WeZHNwGJa0ehfRhh760";
var valueRange = new ValueRange { Values = new List<IList<object>> { new List<object>() } };
valueRange.Values[0].Add(DateTime.Now.ToLongTimeString());
// insert here the name of your spreadsheet table
var rangeToWrite = "Tabellenblatt1";
var appendRequest = service.Spreadsheets.Values.Append(valueRange, spreadsheetId, rangeToWrite);
appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
var appendReponse = appendRequest.Execute();
}
}
}
There is a way for server-to-server communication using certificate instead of copy/paste the provided by Google AccessCode for the communication to be established.
First you will need to get your certificate from Google Console.
If you haven't already created a project do so and give it a name. Then:
Go to "Credentials" section (found on the left menu) and
Click on the button "Create Credentials" and choose "Service account key"
Choose the service account to be "App Engine default service account" and
Choose the "key type" to be "p.12" which is the certificate type.
Image for reference:
After these steps are completed, the certificate will be automatically downloaded. Upload it in some folder in your project and use it as shown below.
Note: for the sake of simplicity the code is placed in the controller.
public async Task<ActionResult> ServerAuth()
{
ViewBag.Message = "Server to server authentication";
List<string> records = new List<string>();
const string ServiceAccountEmail = "your-account#appspot.gserviceaccount.com";
string fullKeyPath = HttpContext.Server.MapPath("~/Key/MyProjectKey.p12"); // The certificate generated by Google and uploaded in the project.
var certificate = new X509Certificate2(fullKeyPath, "notasecret", X509KeyStorageFlags.Exportable); // "notasecret" is the password for the certificate
var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[] { "https://spreadsheets.google.com/feeds", "http://spreadsheets.google.com/feeds/spreadsheets/private/full" }
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!await credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None))
{
throw new InvalidOperationException("Access token request failed.");
}
var requestFactory = new GDataRequestFactory(null);
requestFactory.CustomHeaders.Add("Authorization: Bearer " + credential.Token.AccessToken);
var service = new SpreadsheetsService(null) { RequestFactory = requestFactory };
SpreadsheetQuery query = new SpreadsheetQuery();
query.Title = "Test Sheet"; // The exact name of the sheet you want to read
query.Exact = true;
var feed = service.Query(query);
foreach (SpreadsheetEntry entry in feed.Entries)
{
foreach (WorksheetEntry worksheet in entry.Worksheets.Entries.Cast<WorksheetEntry>())
{
CellQuery cellQuery = new CellQuery(worksheet.CellFeedLink);
CellFeed cellFeed = service.Query(cellQuery);
foreach (CellEntry cell in cellFeed.Entries)
{
records.Add(cell.InputValue);
}
}
}
return View(records);
}
I'm working with google calendar API, to retrieve all the events for a specified calendar. After setting up my credentials and calling the following, to get a list of all my calenders:
var test = service.Calendars.Get(id).Execute();
The Execite() is making the call to the google server, but I'm not getting a response from that call. And it stays in this idle state.
public static ServiceAccountCredential Authenticate()
{
string serviceAccountEmail = "...#developer.gserviceaccount.com";
string keyFilePath = #"filepath";
// what googleApi features to access
_scopes = new string[]
{
CalendarService.Scope.Calendar, // Manage your calendars
CalendarService.Scope.CalendarReadonly // View your Calendars
};
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = _scopes
}.FromCertificate(certificate));
return credential;
}
public class GoogleCalenderRepository
{
ServiceAccountCredential _credentials;
public GoogleCalenderRepository()
{
_credentials = GoogleServiceAccountAuthentication.Authenticate();
}
// use async and await
public string RoomAvailability()
{
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = _credentials,
ApplicationName = "My Project",
});
var calender = service.Calendars.Get("#...group.calendar.google.com").Execute();
return null;
}
}
edit: when call the Execute() in my output screen I'm getting:
The thread 0x13f4 has exited with code 0 (0x0)
This example is in VB.NET extracted from my live application.
It shows you how to call the Google Calendar API to get the list of Calendars for your username. You can translate to C# manually or by using one of the online services.
Dim objCalendarListRequest As CalendarListResource.ListRequest
Dim objCalendars As Data.CalendarList
objCalendarListRequest = objCalendarService.CalendarList.List
objCalendars = objCalendarListRequest.Execute()
For Each objCal As Data.CalendarListEntry In objCalendars.Items
If objCal.Primary IsNot Nothing AndAlso objCal.Primary Then
Debug.WriteLine("ID=" & objCal.Id & ", Description=" & objCal.Description & ", Summary=" & objCal.Summary)
Exit For
End If
Next
I am new to Asp.net and does any one have a working solution to pulling data from google analytics into my webapplication, I have obtained the following code but it gives me an IO error.
Here is my method:
public void Main()
{
//This is the API url which we're storing to a string
string scope = AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue();
//For whatever reason, this is labelled wrong. It is the email address
//that you have added as a user to your Analytics account
string clientId = "xxxxxxxx#gmail.com";
//This is the physical path to the file we downloaded earlier
//To demonstrate this, I've kept the full path to my key file.
//Obviously, you will need to change this to match where you've
//stored yours.
string keyFile = #"C:\Users\Jacob\Documents\Adwords and analytics\katoona\793c6491a4ad1118dda49d6bf6824a975edd485a-privatekey.p12";
//The password Google gives you, probably the same as the one below
string keyPassword = "notasecret";
//Store the authentication description
AuthorizationServerDescription desc = GoogleAuthenticationServer.Description;
//Create a certificate object to use when authenticating
X509Certificate2 key = new X509Certificate2(keyFile, keyPassword, X509KeyStorageFlags.Exportable);
//Now, we will log in and authenticate, passing in the description
//and key from above, then setting the accountId and scope
AssertionFlowClient client = new AssertionFlowClient(desc, key)
{
ServiceAccountId = clientId,
Scope = scope
};
//Finally, complete the authentication process
//NOTE: This is the first change from the update above
OAuth2Authenticator<AssertionFlowClient> auth =
new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
//First, create a new service object
//NOTE: this is the second change from the update
//above. Thanks to James for pointing this out
AnalyticsService gas = new AnalyticsService(new BaseClientService.Initializer() { Authenticator = auth });
//Create our query
//The Data.Ga.Get needs the parameters:
//Analytics account id, starting with ga:
//Start date in format YYYY-MM-DD
//End date in format YYYY-MM-DD
//A string specifying the metrics
DataResource.GaResource.GetRequest r = gas.Data.Ga.Get("ga:9475327", "2013-09-09", "2013-09-23", "ga:visitors");
//Specify some addition query parameters
r.Dimensions = "ga:visitorType";
r.Sort = "-ga:visitors";
r.MaxResults = 5;
//Execute and fetch the results of our query
try
{
//Write the column headers
GaData d = r.Execute();
foreach (var h in d.ColumnHeaders)
{
ListBox1.Items.Add(h.Name);
}
//Write the data
foreach (var row in d.Rows)
{
ListBox1.Items.Add(row[0] + " ------ " + row[1]);
}
}
catch (Exception webEx)
{
throw;
}
Giving the exact error received would help - then we can see whether it's an OAuth problem or a problem with what you're sending to query the GA data API.
Also, start by using the Google Analytics Query Explorer to construct and test your query before converting it to C# code.
I've been trying to list users in my google apps domain for a while now.
No problem in Python, but in C# i get an error message:
An error has occured:
Google.Apis.Requests.RequestError
Bad Request [400]
Errors [
Message[Bad Request] Location[ - ] Reason[badRequest] Domain[global]
]
I'm no C# guru of any sorts but when I looked through the Google.Apis.Admin.directory_v1.cs - file it looked to me as if the UserResource ListRequest is wrong???
It's found on line 7349-7352 in the file. Anyone know's if it's not yet implemented in the API?
Edit:
I start with why I THINK the code in Google.Apis.Admin.directory_v1.cs, lines 7349-7352 is wrong(as I mentioned - I'm not a C#-guru):
The Code:
/// <summary>Retrieve either deleted users or all users in a domain (paginated)</summary>
public virtual ListRequest List() {
return new ListRequest(service);
}
Why I find it odd:
I can' see where to pass the customerid or domain as an argumant to this request, but in the APIs Explorer it's needed (otherwise I get the same error message as above, in my original post).
Edit : I looked a bit further down in the file and I guess that line 8904 and onwards is doing what I looked for earlier. My Bad!
But still I can't get my code to work?!?!?
And my code that won't work:
static void Main(string[] args)
{
// Display the header and initialize the sample.
CommandLine.EnableExceptionHandling();
Console.WriteLine("List users in a google apps domain!");
Console.WriteLine("by Jonas Bergstedt 2013");
// Get the domainname
Console.Write("Domain: ");
string domain = Console.ReadLine();
// Register the authenticator.
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = <myClientId>,
ClientSecret = <myClientSecret>",
};
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
// Create the service.
var service = new DirectoryService(new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = "List Users",
ApiKey = <myApiKey>
});
// Trying to add the domain
service.Users.List().Domain = domain;
Users results = service.Users.List().Execute();
foreach (User list in results.UsersValue)
{
Console.WriteLine("- " + list.Name);
}
}
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { DirectoryService.Scopes.AdminDirectoryUser.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.WriteLine();
Console.Write("Authorization Code: ");
string authCode = Console.ReadLine();
// Retrieve the access token by using the authorization code:
return arg.ProcessUserAuthorization(authCode, state);
}
}
ListRequest had those properties. It looks like those properties aren't mandatory, so they aren't part of the constructor.
You can do the following:
var listReq = service.Users.List();
listReq.Customer = "CUSTOMER_HERE";
listReq.Domain = "DOMAIN_HERE";
Users results = listReq.Execute();