Google.Apis.Requests.RequestError Not Found when deleting event - google-calendar-api

I have below method to delete event in calendar:
public async Task<string> DeleteEventInCalendarAsync(TokenResponse token, string googleUserId, string calendarId, string eventId)
{
string result = null;
try
{
if (_calService == null)
{
_calService = GetCalService(token, googleUserId);
}
// Check if event exist
var eventResource = new EventsResource(_calService);
var erListRequest = eventResource.List(calendarId);
var eventsResponse = await erListRequest.ExecuteAsync().ConfigureAwait(false);
var existingEvent = eventsResponse.Items.FirstOrDefault(e => e.Id == eventId);
if (existingEvent != null)
{
var deleteRequest = new EventsResource.DeleteRequest(_calService, calendarId, eventId);
result = await deleteRequest.ExecuteAsync().ConfigureAwait(false);
}
}
catch (Exception exc)
{
result = null;
_logService.LogException(exc);
}
return result;
}
And I am getting error as follow -
Google.GoogleApiException Google.Apis.Requests.RequestError Not Found [404] Errors [ Message[Not Found] Location[ - ] Reason[notFound] Domain[global] ]
Can you help me understand why this error? Or where I can find the details about these error?

The error you are getting is due to the event's id you are passing doesn't exist or you are passing it in the wrong way. Following the .Net Quickstart I made a simple code example on how to pass the event's id to the Delete(string calendarId, string eventId) method from the Class Events
namespace CalendarQuickstart
{
class Program
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/calendar-dotnet-quickstart.json
static string[] Scopes = { CalendarService.Scope.Calendar };
static string ApplicationName = "Google Calendar API .NET Quickstart";
static void Main(string[] args)
{
UserCredential credential;
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request.
EventsResource.ListRequest request = service.Events.List("primary");
// List events.
Events events = request.Execute();
Event existingEvent = events.Items.FirstOrDefault(e => e.Id == "your event id you want to get");
Console.WriteLine("Upcoming events:");
if (existingEvent != null)
{
Console.WriteLine("{0} {1}", existingEvent.Summary, existingEvent.Id);
string deleteEvent = service.Events.Delete("primary", existingEvent.Id).Execute();
}
else
{
Console.WriteLine("No upcoming events found.");
}
Console.Read();
}
}
}
Notice
I made this example in a synchronous syntax way for testing purposes in the console. After you test it and see how it works, you could adapt it to your code. Remember, make your you are passing the correct Id.
Docs
For more info check this doc:
Namespace Google.Apis.Calendar.v3

Related

using Cloud Firestore Database API to create cloud firestore database with`database_id` of '(default)' not working

I used Google.Apis.Firestore.v1 library to create a cloud firestore for my firebase project as follows, but when running my code I am getting an error related to the database naming I guess, and it has a regular expression format as shown in the error below, and how can I fix this issue?
using Google.Apis.Firestore.v1;
private static FirestoreService _firestoreService;
public static void IntializeFirestoreAdmin() {
GoogleCredential credential = GoogleCredential.GetApplicationDefault();
if (CloudManager.Credential.IsCreateScopedRequired)
{
credential = CloudManager.Credential.CreateScoped(FirestoreService.Scope.CloudPlatform);
}
_firestoreService = new FirestoreService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = CloudManager.ApplicationName
});
}
public static void AddCloudFirestore() {
IntializeFirestoreAdmin();
var mydata = new GoogleFirestoreAdminV1Database {
LocationId = "nam5",
Type = "FIRESTORE_NATIVE",
Name = "projects/" + CloudManager.ProjectId + "/databases/(default)",
};
_firestoreService.Projects.Databases.Create(mydata, "projects/" + CloudManager.ProjectId).Execute();
}
Error :
Unhandled exception. The service firestore has thrown an exception.
HttpStatusCode is BadRequest.
Google.Apis.Requests.RequestError
database_id should be 4-63 characters, and valid characters are /[a-z][0-9]-/ [400]
Errors [
Message[database_id should be 4-63 characters, and valid characters are /[a-z][0-9]-/] Location[ - ] Reason[badRequest] Domain[global]
]
Google.GoogleApiException: The service firestore has thrown an exception. HttpStatusCode is BadRequest. database_id should be 4-63 characters, and valid characters are /[a-z][0-9]-/
at Google.Apis.Requests.ClientServiceRequest`1.ParseResponse(HttpResponseMessage response)
at Google.Apis.Requests.ClientServiceRequest`1.Execute()
If you go through the given method :
FirestoreService().Projects.Databases.Create it has parent as Parameter and databaseId as a Query parameter there is also a try this method section where you can verify your parameters are correct like for example :
using Google.Apis.Firestore.v1;
private static FirestoreService _firestoreService;
public static void IntializeFirestoreAdmin() {
GoogleCredential credential = GoogleCredential.GetApplicationDefault();
if (CloudManager.Credential.IsCreateScopedRequired)
{
credential = CloudManager.Credential.CreateScoped(FirestoreService.Scope.CloudPlatform);
}
_firestoreService = new FirestoreService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = CloudManager.ApplicationName
});
}
public static void AddCloudFirestore() {
IntializeFirestoreAdmin();
var mydata = new GoogleFirestoreAdminV1Database {
LocationId = "nam5",
Type = "FIRESTORE_NATIVE",
Name = "projects/" + CloudManager.ProjectId + "/databases/my-database",
};
_firestoreService.Projects.Databases.Create(mydata, "projects/" + CloudManager.ProjectId).Execute();
}
var body = new GoogleFirestoreAdminV1Database
{
Type = "FIRESTORE_NATIVE",
LocationId = "nam5"
};
string parent = $"projects/{CloudManager.ProjectId}";
var request = _firestoreService.Projects.Databases.Create(body, parent);
request.DatabaseId = "(default)";
var response = request.Execute();
The issue was that I didn't add the databaseId as a query parameter. For more details go to this link https://groups.google.com/g/google-cloud-firestore-discuss/c/EQ-04MnPyLk

how we can return a status code for the serialized JSON object using Newtonsoft.net

I have this Action method which act as an API end point inside our ASP.NET MVC-5, where it search for a username and return the username Phone number and Department from Active Directory (we are serializing the object using Newtonsoft.net):-
public ActionResult UsersInfo2()
{
DomainContext result = new DomainContext();
try
{
// create LDAP connection object
DirectoryEntry myLdapConnection = createDirectoryEntry();
string ADServerName = System.Web.Configuration.WebConfigurationManager.AppSettings["ADServerName"];
string ADusername = System.Web.Configuration.WebConfigurationManager.AppSettings["ADUserName"];
string ADpassword = System.Web.Configuration.WebConfigurationManager.AppSettings["ADPassword"];
using (var context = new DirectoryEntry("LDAP://mydomain.com:389/DC=mydomain,DC=com", ADusername, ADpassword))
using (var search = new DirectorySearcher(context))
{
// create search object which operates on LDAP connection object
// and set search object to only find the user specified
// DirectorySearcher search = new DirectorySearcher(myLdapConnection);
// search.PropertiesToLoad.Add("telephoneNumber");
search.Filter = "(&(objectClass=user)(sAMAccountName=test.test))";
SearchResult r = search.FindOne();
ResultPropertyCollection fields = r.Properties;
foreach (String ldapField in fields.PropertyNames)
{
// cycle through objects in each field e.g. group membership
// (for many fields there will only be one object such as name)
string temp;
// foreach (Object myCollection in fields[ldapField])
// {
// temp = String.Format("{0,-20} : {1}",
// ldapField, myCollection.ToString());
if (ldapField.ToLower() == "telephonenumber")
{
foreach (Object myCollection in fields[ldapField])
{
result.Telephone = myCollection.ToString();
}
}
else if (ldapField.ToLower() == "department")
{
foreach (Object myCollection in fields[ldapField])
{
result.Department = myCollection.ToString();
}
}
// }
}
string output = JsonConvert.SerializeObject(result);
return Json(output,JsonRequestBehavior.AllowGet);
}
}
catch (Exception e)
{
Console.WriteLine("Exception caught:\n\n" + e.ToString());
}
return View(result);
}
now the return JSON will be as follow:-
"\"DisplayName\":null,\"Telephone\":\"123123\",\"Department\":\"IT\",\"Name\":null,\"SamAccountName\":null,\"DistinguishedName\":null,\"UserPrincipalName\":null}"
but in our case we need to return a status code beside the return json data. for example inccase there is an exception we need to return an error code,also if we are able to get the user's info we need to pass succes code 200, and so on.. so how we can achieve this?
you can try something like this
var statusCode=200;
string output = JsonConvert.SerializeObject( new { result = result, StatusCode = statusCode);
but nobody usually do this. When users call API they can check status code that HTTP Client returns, using code like this
var response = await client.GetAsync(api);
//or
var response = await client.PutAsJsonAsync(api, data);
var statusCode = response.StatusCode.ToString();
//or usually
if (response.IsSuccessStatusCode) {...}
else {...}

Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'

further to this question, i have the same problem. PubFolder on Prem , users in O365
I have fetched and added the routing headers from Glen's post but still get the error
GetToken works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
GetX headers works...
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/public-folder-access-with-ews-in-exchange
--->> ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10))
Microsoft.Exchange.WebServices.Data.ServiceResponseException: 'There are no public folder servers available.'
static async System.Threading.Tasks.Task Test3()
{
string ClientId = ConfigurationManager.AppSettings["appId"];
string TenantId = ConfigurationManager.AppSettings["tenantId"];
string secret = ConfigurationManager.AppSettings["clientSecret"];
string uMbox = ConfigurationManager.AppSettings["userId"];
string uPwd = ConfigurationManager.AppSettings["userPWD"];
// Using Microsoft.Identity.Client 4.22.0
//https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth//
var cca = ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithClientSecret(secret)
.WithTenantId(TenantId)
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
var authResult = await cca.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService();
ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ewsClient.Credentials = new OAuthCredentials(authResult.AccessToken);
ewsClient.ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, uMbox);
AutodiscoverService autodiscoverService = GetAutodiscoverService(uMbox, uPwd);
GetUserSettingsResponse userResponse = GetUserSettings(autodiscoverService, uMbox, 3, UserSettingName.PublicFolderInformation, UserSettingName.InternalRpcClientServer);
string pfAnchorHeader= userResponse.Settings[UserSettingName.PublicFolderInformation].ToString();
string pfMailboxHeader = userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString(); ;
// Make an EWS call
var folders = ewsClient.FindFolders(WellKnownFolderName.MsgFolderRoot, new FolderView(10));
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
//get Public folder root
//Include x-anchormailbox header
Console.WriteLine("X-AnchorMailbox value for public folder hierarchy requests: {0}", pfAnchorHeader);
Console.WriteLine("X-PublicFolderMailbox value for public folder hierarchy requests: {0}", pfMailboxHeader);
//var test3 = GetMailboxGuidAddress(ewsClient, pfAnchorHeader, pfMailboxHeader, uMbox);
///https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-route-public-folder-content-requests <summary>
ewsClient.HttpHeaders.Add("X-AnchorMailbox", userResponse.Settings[UserSettingName.PublicFolderInformation].ToString());
//ewsClient.HttpHeaders.Add("X-AnchorMailbox", "SharedPublicFolder#contoso.com");
ewsClient.HttpHeaders.Add("X-PublicFolderMailbox", userResponse.Settings[UserSettingName.InternalRpcClientServer].ToString());
try
{
var pubfolders = ewsClient.FindFolders(WellKnownFolderName.PublicFoldersRoot, new FolderView(10));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
foreach (var folder in folders)
{
Console.WriteLine($"Folder: {folder.DisplayName}");
}
}
catch (MsalException ex)
{
Console.WriteLine($"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Hit any key to exit...");
Console.ReadKey();
}
}
public static AutodiscoverService GetAutodiscoverService(string username, string pwd)
{
AutodiscoverService adAutoDiscoverService = new AutodiscoverService();
adAutoDiscoverService.Credentials = new WebCredentials(username, pwd);
adAutoDiscoverService.EnableScpLookup = true;
adAutoDiscoverService.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
adAutoDiscoverService.PreAuthenticate = true;
adAutoDiscoverService.TraceEnabled = true;
adAutoDiscoverService.KeepAlive = false;
return adAutoDiscoverService;
}
public static GetUserSettingsResponse GetUserSettings(
AutodiscoverService service,
string emailAddress,
int maxHops,
params UserSettingName[] settings)
{
Uri url = null;
GetUserSettingsResponse response = null;
for (int attempt = 0; attempt < maxHops; attempt++)
{
service.Url = url;
service.EnableScpLookup = (attempt < 2);
response = service.GetUserSettings(emailAddress, settings);
if (response.ErrorCode == AutodiscoverErrorCode.RedirectAddress)
{
url = new Uri(response.RedirectTarget);
}
else if (response.ErrorCode == AutodiscoverErrorCode.RedirectUrl)
{
url = new Uri(response.RedirectTarget);
}
else
{
return response;
}
}
throw new Exception("No suitable Autodiscover endpoint was found.");
}
Your code won't work against an OnPrem Public folder tree as EWS in Office365 won't proxy to an OnPrem Exchange Org (even if hybrid is setup). (Outlook MAPI is a little different and allows this via versa setup but in that case it never proxies either it just makes a different connection to that store and its all the Outlook client doing this).
Because your trying to use the client credentials oauth flow for that to work onPrem you must have setup hybrid modern authentication https://learn.microsoft.com/en-us/microsoft-365/enterprise/hybrid-modern-auth-overview?view=o365-worldwide. Then you need to acquire a token with an audience set to the local OnPrem endpoint. (this is usually just your onPrem ews endpoint's host name but it should be one of the service principal names configured in your hybrid auth setup Get-MsolServicePrincipal). So in your code you would change
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
to
var ewsScopes = new string[] { "https://OnPrem.whatever.com/.default" };
which will then give you a token with an audience set for the onprem server then you need to send the EWS request to that endpoint so change that eg
ewsClient.Url = new Uri("https://OnPrem.whatever.com/EWS/Exchange.asmx");
if Hybird Modern Auth is setup then you need to default back to use Integrated or Basic Authenticaiton.

Cosmos DB partitioned access to a database

I am implementing a multi-tenant application using cosmosDB. I am using partition keys to separate multiple users data. Following best practices i am trying to allow each tenant to have its own db access token.
I create a user and permission and use the created token to access the partition. But I get the following error:
Partition key provided either doesn't correspond to definition in the collection or doesn't match partition key field values specified
in the document.
ActivityId: 1659037a-118a-4a2d-8615-bb807b717fa7, Microsoft.Azure.Documents.Common/1.22.0.0, Windows/10.0.17134
documentdb-netcore-sdk/1.9.1
My code goes as follows:
Constructor Initiates the client
public Projects (CosmosDbConfig cosmosConfig)
{
config = cosmosConfig;
client = new DocumentClient(new Uri(config.Endpoint), config.AuthKey);
collectionUri = UriFactory.CreateDocumentCollectionUri(config.Database, config.Collection);
config.AuthKey = GetUserToken().Result;;
client = new DocumentClient(new Uri(config.Endpoint), config.AuthKey);
}
The get user function creates the user and retrieves the token. User Ids are partition keys.
private async Task<string> GetUserToken()
{
User user = null;
try
{
try
{
user = await client.ReadUserAsync(UriFactory.CreateUserUri(config.Database, config.PartitionKey));
var permission = await GetorCreatePermission(user, config.Collection, config.PartitionKey);
return permission.Token;
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
if (user == null)
{
user = new User
{
Id = config.PartitionKey
};
user = await client.CreateUserAsync(UriFactory.CreateDatabaseUri(config.Database), user);
var permission = await GetorCreatePermission(user, config.Collection, config.PartitionKey);
return permission.Token;
}
else
{
throw new Exception("");
}
}
catch (Exception ex)
{
throw ex;
}
}
Permission are done per collections and holds the collection name as ID since Ids are unique per user.
private async Task<Permission> GetorCreatePermission(User user,
string collection,
string paritionKey)
{
var permDefinition = new Permission
{
Id = collection,
PermissionMode = PermissionMode.All,
ResourceLink = collectionUri.OriginalString,
ResourcePartitionKey = new PartitionKey(paritionKey),
};
var perms = client.CreatePermissionQuery(user.PermissionsLink).AsEnumerable().ToList();
var perm = perms.FirstOrDefault(x => x.Id == collection);
if (perm != null)
{
return perm;
}
else
{
var result = await client.CreatePermissionAsync(user.SelfLink, permDefinition);
perm = result.Resource;
return perm;
}
}
The create function utilizes the new client and this where the error occurs.
public async Task<string> Create(Project p)
{
var result = await client.CreateDocumentAsync(collectionUri, p, new RequestOptions()
{ PartitionKey = new PartitionKey(config.PartitionKey),
});
var document = result.Resource;
return document.Id;
}
Since error says that partition key is incorrect i can suggest you try define partition key pathes while creating collection:
var docCollection = new DocumentCollection();
docCollection.Id = config.CollectionName;
docCollection.PartitionKey.Paths.Add(string.Format("/{0}", config.PartitionKey );
collectionUri = UriFactory.CreateDocumentCollectionUri(config.Database, docCollection);

Google api 3 returning nothing when WEB APi is published

I have made a simple ASP.NET Web API that gets events from a shared google calendar, and serves them to me in json. When i fire it up from localhost i get 104 events returned: but when I've published it to my IIS server, the array contains no elements. I can't debug on it, but i get an empty array presented:
I have made an app on https://console.developers.google.com/ and added both localhost and http://domainname.dk/oauthcallback to it.
My code:
public void GetFixedEvents()
{
const string calendarId = "xxxxxxxxxxxxxxxx#group.calendar.google.com";
try
{
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = clientId,
ClientSecret = clientSecret,
},
new[] { CalendarService.Scope.Calendar },
"user",
CancellationToken.None).Result;
var service = new CalendarService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "CalendarTest",
});
var queryStart = DateTime.Now;
var queryEnd = queryStart.AddYears(1);
var query = service.Events.List(calendarId);
query.TimeMin = queryStart;
query.TimeMax = queryEnd;
query.SingleEvents = true;
var events = query.Execute().Items;
foreach (var item in events)
{
var tempEvent = new PubEvent
{
Title = item.Summary,
Description = item.Description ?? "None",
Start = item.Start.DateTime,
End = item.End.DateTime,
EventType = item.Summary.Contains("Ladies") ? EventType.LadiesNight : EventType.StudentNight
};
_pubEvents.Add(tempEvent);
}
}
catch (Exception e)
{
Console.WriteLine("Exception encountered: {0}", e.Message);
}
}
Can anyone help? Is it a problem with redirect uri? I have tried adding all the ones I can possibly think of. Is there any way to log the call to the google api?

Resources