I have an event in Outlook with an attendee that uses Google calendar. When I change the event recurrence range type using the graph API from "noEnd" to "numbered", the google attendee's event does not get updated.
Here is the code I am using to set the recurrence as "noEnd":
public async System.Threading.Tasks.Task SetDailyRecurrenceForever(string ExternalID, string CalendarRefreshToken)
{
var tmpEvent = new Microsoft.Graph.Event
{
Recurrence = new PatternedRecurrence
{
Pattern = new RecurrencePattern
{
Type = RecurrencePatternType.Daily,
Interval = 1,
FirstDayOfWeek = Microsoft.Graph.DayOfWeek.Sunday,
Index = WeekIndex.First
},
Range = new RecurrenceRange
{
Type = RecurrenceRangeType.NoEnd,
StartDate = new Microsoft.Graph.Date(2020,10,6),
RecurrenceTimeZone = "Central Standard Time",
NumberOfOccurrences = 0
}
}
};
var graphClient = await MicrosoftAuthenticationProvider.GetGraphClient(CALENDAR_CLIENT_ID, CALENDAR_CLIENT_SECRET, CALENDAR_REDIRECT_URI, CALENDAR_ACCESS_SCOPES, CalendarRefreshToken)
.ConfigureAwait(false);
await graphClient.Me.Events[ExternalID]
.Request()
.UpdateAsync(tmpEvent)
.ConfigureAwait(false);
}
Here is the code I am using to set the recurrence as "numbered":
public async System.Threading.Tasks.Task SetDailyRecurrenceForFiveDays(string ExternalID, string CalendarRefreshToken)
{
var tmpEvent = new Microsoft.Graph.Event
{
Recurrence = new PatternedRecurrence
{
Pattern = new RecurrencePattern
{
Type = RecurrencePatternType.Daily,
Interval = 1,
FirstDayOfWeek = Microsoft.Graph.DayOfWeek.Sunday,
Index = WeekIndex.First
},
Range = new RecurrenceRange
{
Type = RecurrenceRangeType.Numbered,
StartDate = new Microsoft.Graph.Date(2020, 10, 6),
RecurrenceTimeZone = "Central Standard Time",
NumberOfOccurrences = 5
}
}
};
var graphClient = await MicrosoftAuthenticationProvider.GetGraphClient(CALENDAR_CLIENT_ID, CALENDAR_CLIENT_SECRET, CALENDAR_REDIRECT_URI, CALENDAR_ACCESS_SCOPES, CalendarRefreshToken)
.ConfigureAwait(false);
await graphClient.Me.Events[ExternalID]
.Request()
.UpdateAsync(tmpEvent)
.ConfigureAwait(false);
}
The bug is also repeatable by sending a PATCH using the graph explorer:
https://graph.microsoft.com/v1.0/me/events/{ID}
Update the event as "noEnd":
{"recurrence":{"pattern":{"type":"daily","interval":1,"month":0,"dayOfMonth":0,"firstDayOfWeek":"sunday","index":"first"},"range":{"type":"noEnd","startDate":"2020-10-06","endDate":"0001-01-01","recurrenceTimeZone":"Central Standard Time","numberOfOccurrences":0}}}
Update the event as "numbered":
{"recurrence":{"pattern":{"type":"daily","interval":1,"month":0,"dayOfMonth":0,"firstDayOfWeek":"sunday","index":"first"},"range":{"type":"numbered","startDate":"2020-10-06","endDate":"0001-01-01","recurrenceTimeZone":"Central Standard Time","numberOfOccurrences":5}}}
The following video displays the behavior the google attendee is experiencing:
https://www.screencast.com/t/uhwovvZf
Note: At the end of the video I show that switching the interval to 2 will cause the google event to be updated properly, so this is specifically a problem when the interval is 1.
Is this a known bug? Does anyone have a work-around for it?
Related
I have code that get's the default calendar and stores the id in a variable called calendar id. I then I then get a list of availabilities (which are events in our system) the end time and the start time are recorded in utc and I want to put those events into an office 365 calendar.
This is my code which is generating and 400 Bad request. Does anyone have any idea what I am doing wrong?
using (HttpClient httpClient = new HttpClient())
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", msBearerToken);
var callJson = new
{
Subject = "Booking",
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = "Available"
},
Start = new DateTimeTimeZone
{
DateTime = availabilitySummery.StartDateUTC.ToString(),
TimeZone = "Europe/London"
},
End = new DateTimeTimeZone
{
DateTime = availabilitySummery.EndDateUTC.ToString(),
TimeZone = "Europe/London"
}
};
string path = $"/me/calendars/{calendarId}/events";
// string path = "/me/calendars/events";
path = "https://graph.microsoft.com/v1.0" + path;
HttpResponseMessage responseMessage = await httpClient.PostAsJsonAsync(path, callJson);}
Date should be ISO8601 formatted.
Replace .ToString() by .ToString("o") when you want to convert dates to string.
Ref: The round-trip ("O", "o") format specifier
And createdDateTime property of event object
Update: This worked:
using (HttpClient httpClient = new HttpClient())
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", msBearerToken);
var callJson = new
{
Subject = "Booking",
Body = new
{
ContentType = BodyType.Html.ToString(),
Content = "Available"
},
Start = new
{
DateTime = availabilitySummery.StartDateUTC.ToString("yyyy-MM-ddTHH:mm:ss"),
TimeZone = "GMT Standard Time"
},
End = new
{
DateTime = availabilitySummery.EndDateUTC.ToString("yyyy-MM-ddTHH:mm:ss"),
TimeZone = "GMT Standard Time"
}
};
string path = $"/me/calendars/{calendarId}/events";
string jsoncall = JsonConvert.SerializeObject(callJson);
path = "https://graph.microsoft.com/v1.0" + path;
HttpResponseMessage responseMessage = await httpClient.PostAsJsonAsync(path, callJson);
string content = await responseMessage.Content.ReadAsStringAsync();
if (!responseMessage.IsSuccessStatusCode)
{
return false;
}
}
I am using GraphAPI SDK to create a new Team in Microsoft Teams:
var newTeam = new Team()
{
DisplayName = teamName,
Description = teamName,
AdditionalData = new Dictionary<string, object>()
{
{"template#odata.bind", "https://graph.microsoft.com/v1.0/teamsTemplates('standard')"}
},
Members = new TeamMembersCollectionPage()
{
new AadUserConversationMember
{
Roles = new List<String>()
{
"owner"
},
AdditionalData = new Dictionary<string, object>()
{
{"user#odata.bind", $"https://graph.microsoft.com/v1.0/users/{userId}"}
}
}
}
};
var team = await this.graphStableClient.Teams
.Request()
.AddAsync(newTeam);
The problem is that I get always null. According documentation this method returns a 202 response (teamsAsyncOperation), but the AddAsync method from SDK returns a Team object. Is there any way to get the tracking url to check if the team creation has been finished with the SDK?
Documentation and working SDK works different... As they wrote in microsoft-graph-docs/issues/10840, we can only get the teamsAsyncOperation header values if we use HttpRequestMessage as in contoso-airlines-teams-sample. They wrote to the people who asks this problem, look to the joined teams :)) :)
var newTeam = new Team()
{
DisplayName = model.DisplayName,
Description = model.Description,
AdditionalData = new Dictionary<string, object>
{
["template#odata.bind"] = $"{graph.BaseUrl}/teamsTemplates('standard')",
["members"] = owners.ToArray()
}
};
// we cannot use 'await client.Teams.Request().AddAsync(newTeam)'
// as we do NOT get the team ID back (object is always null) :(
BaseRequest request = (BaseRequest)graph.Teams.Request();
request.ContentType = "application/json";
request.Method = "POST";
string location;
using (HttpResponseMessage response = await request.SendRequestAsync(newTeam, CancellationToken.None))
location = response.Headers.Location.ToString();
// looks like: /teams('7070b1fd-1f14-4a06-8617-254724d63cde')/operations('c7c34e52-7ebf-4038-b306-f5af2d9891ac')
// but is documented as: /teams/7070b1fd-1f14-4a06-8617-254724d63cde/operations/c7c34e52-7ebf-4038-b306-f5af2d9891ac
// -> this split supports both of them
string[] locationParts = location.Split(new[] { '\'', '/', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);
string teamId = locationParts[1];
string operationId = locationParts[3];
// before querying the first time we must wait some secs, else we get a 404
int delayInMilliseconds = 5_000;
while (true)
{
await Task.Delay(delayInMilliseconds);
// lets see how far the teams creation process is
TeamsAsyncOperation operation = await graph.Teams[teamId].Operations[operationId].Request().GetAsync();
if (operation.Status == TeamsAsyncOperationStatus.Succeeded)
break;
if (operation.Status == TeamsAsyncOperationStatus.Failed)
throw new Exception($"Failed to create team '{newTeam.DisplayName}': {operation.Error.Message} ({operation.Error.Code})");
// according to the docs, we should wait > 30 secs between calls
// https://learn.microsoft.com/en-us/graph/api/resources/teamsasyncoperation?view=graph-rest-1.0
delayInMilliseconds = 30_000;
}
// finally, do something with your team...
I found a solution from another question... Tried and saw that it's working...
I'm facing a strange Issue when I'm inserting or updating an item in SharePoint2019 list which contains a user column the save completes with no errors but the user column always have empty value,
below is my code
context.Load(context.Web, web => web.Lists);
await context.ExecuteQueryAsync();
List RiskList = context.Web.Lists.GetByTitle("Risks");
context.Load(RiskList);
context.Load(RiskList, r => r.Fields);
await context.ExecuteQueryAsync();
ListItem listItem = RiskList.GetItemById(projectRisks.Id);
List<ListItemFormUpdateValue> listItemFormUpdateValue = new List<ListItemFormUpdateValue>();
if (projectRisks.AssignedTo.HasValue)
{
listItem["AssignedTo"] = new FieldUserValue() { LookupId = projectRisks.AssignedTo.Value };
}
if (projectRisks.Owner.HasValue)
{
User OwnerUser = context.Web.SiteUsers.GetById(projectRisks.Owner.Value);
context.Load(OwnerUser);
await context.ExecuteQueryAsync();
listItem["Owner"] = new FieldUserValue() { LookupId = OwnerUser.Id };
}
listItemFormUpdateValue.Add(new ListItemFormUpdateValue() { FieldName = "Category", FieldValue = GetSPSelectedChoiceValue(context, RiskList, "Category", projectRisks.CategoryName).Result });
listItemFormUpdateValue.Add(new ListItemFormUpdateValue() { FieldName = "Status", FieldValue = GetSPSelectedChoiceValue(context, RiskList, "Status", projectRisks.StatusName).Result });
listItem["Contingency_x0020_plan"] = projectRisks.ContingencyPlan;
listItem["Cost"] = projectRisks.Cost;
listItem["Cost_x0020_Exposure"] = string.Empty;
listItem["Description"] = projectRisks.Description;
listItem["DueDate"] = projectRisks.DueDate;
listItem["Exposure"] = projectRisks.Exposure;
listItem["Impact"] = projectRisks.Impact;
listItem["Mitigation_x0020_plan"] = projectRisks.MitigationPlan;
listItem["Probability"] = projectRisks.Probability;
listItem["Title"] = projectRisks.Name;
listItem.ValidateUpdateListItem(listItemFormUpdateValue, false, string.Empty);
listItem.Update();
await context.ExecuteQueryAsync();
as you can see in the AssignTo and in Owner Columns no mater how I try to save the users values the list will take the default value which is null.
I have made sure that there is values in the assigned to and Owner properties and I have tried using the ListItemFormUpdateValue but with no luck.
Thanks in advance
CommentThreads amount changes by order
Hi I'm trying to fetch all comments of a video. For testing purpose I'm using this video Id U55NGD9Jm7M.
When I order by time I get 1538 comments the last wrote on the 02.05.2015.
If I’m using the relevance I only receive 1353 comment and the last was wrote on the 29.04.2015
This doesn’t seem right to me. I expected to receive the same comments but in a different order and not different comments.
I also tried this on a different video and the results were the same.
My code cut down to minimum
Thank you for your help
public class foo
{
public void bar(string videoId)
{
var allTopLevelComments = new List<CommentThread>();
var searchListResponse = getThread(videoId);
allTopLevelComments.AddRange(searchListResponse.Items);
string nextPage = searchListResponse.NextPageToken;
while (!String.IsNullOrEmpty(nextPage))
{
searchListResponse = getThread(videoId, searchListResponse.NextPageToken);
nextPage = searchListResponse.NextPageToken;
allTopLevelComments.AddRange(searchListResponse.Items);
}
var first = allTopLevelComments.OrderBy(c => c.Snippet.TopLevelComment.Snippet.PublishedAt).First();
}
private CommentThreadListResponse getThread(string videoId, string nextPageToken = "")
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer
{
ApiKey = "my key",
ApplicationName = "my app"
});
var searchListRequest = youtubeService.CommentThreads.List("id, replies, snippet");
searchListRequest.VideoId = videoId;
searchListRequest.MaxResults = 100;
searchListRequest.Order = CommentThreadsResource.ListRequest.OrderEnum.Time;
searchListRequest.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
if (!String.IsNullOrEmpty(nextPageToken))
{
searchListRequest.PageToken = nextPageToken;
}
return searchListRequest.Execute();
}
}
The code seems to run. I don't get any error messages, but an invoice does not appear in QB after I sync. The code is basically this (http://pastebin.com/y7QENxeX) with a few (presumably) minor changes as noted. I'm able to create Accounts and Customers so I believe the basic infrastructure of my app is good. I don't understand why I'm stuck on invoices. I think my customerID is 2. I only have 5 in my company right now. And I think my itemID is 1 as I only have one in QB right now.
Any and all help is greatly appreciated.
Intuit.Ipp.Data.Qbd.PhysicalAddress physicalAddress = new Intuit.Ipp.Data.Qbd.PhysicalAddress();
physicalAddress.Line1 = "123 Main St.";
physicalAddress.Line2 = "Apt. 12";
physicalAddress.City = "Mountain View";
physicalAddress.CountrySubDivisionCode = "CA";
physicalAddress.Country = "USA";
physicalAddress.PostalCode = "94043";
physicalAddress.Tag = new string[] { "Billing" };
Intuit.Ipp.Data.Qbd.InvoiceHeader invoiceHeader = new Intuit.Ipp.Data.Qbd.InvoiceHeader();
invoiceHeader.ARAccountId = new Intuit.Ipp.Data.Qbd.IdType() { idDomain = Intuit.Ipp.Data.Qbd.idDomainEnum.QB, Value = "37" };
invoiceHeader.ARAccountName = "Accounts Receivable";
// original code : invoiceHeader.CustomerId = new IdType() { idDomain = idDomainEnum.NG, Value = "3291253" };
invoiceHeader.CustomerId = new Intuit.Ipp.Data.Qbd.IdType() { idDomain = Intuit.Ipp.Data.Qbd.idDomainEnum.QB, Value = "2" };
invoiceHeader.Balance = (decimal)100.00;
invoiceHeader.BillAddr = physicalAddress;
invoiceHeader.BillEmail = "detroit#tigers.com";
invoiceHeader.CustomerName = "Detroit Tigers";
invoiceHeader.DocNumber = "1234567";
invoiceHeader.DueDate = DateTime.Now;
invoiceHeader.ShipAddr = physicalAddress;
invoiceHeader.ShipDate = DateTime.Now;
invoiceHeader.TaxAmt = (decimal)5;
invoiceHeader.TaxRate = (decimal).05;
invoiceHeader.ToBeEmailed = false;
invoiceHeader.TotalAmt = (decimal)105.00;
List<Intuit.Ipp.Data.Qbd.InvoiceLine> listLine = new List<Intuit.Ipp.Data.Qbd.InvoiceLine>();
//Loop for multiple invoice lines could be added here
Intuit.Ipp.Data.Qbd.ItemsChoiceType2[] invoiceItemAttributes = { Intuit.Ipp.Data.Qbd.ItemsChoiceType2.ItemId, Intuit.Ipp.Data.Qbd.ItemsChoiceType2.UnitPrice, Intuit.Ipp.Data.Qbd.ItemsChoiceType2.Qty };
// original code : object[] invoiceItemValues = { new IdType() { idDomain = idDomainEnum.QB, Value = "5" }, new decimal(33), new decimal(2) };
object[] invoiceItemValues = { new Intuit.Ipp.Data.Qbd.IdType() { idDomain = Intuit.Ipp.Data.Qbd.idDomainEnum.QB, Value = "1" }, new decimal(33), new decimal(2) };
var invoiceLine = new Intuit.Ipp.Data.Qbd.InvoiceLine();
invoiceLine.Amount = 66;
invoiceLine.AmountSpecified = true;
invoiceLine.Desc = "test " + DateTime.Now.ToShortDateString();
invoiceLine.ItemsElementName = invoiceItemAttributes;
invoiceLine.Items = invoiceItemValues;
invoiceLine.ServiceDate = DateTime.Now;
invoiceLine.ServiceDateSpecified = true;
listLine.Add(invoiceLine);
Intuit.Ipp.Data.Qbd.Invoice invoice = new Intuit.Ipp.Data.Qbd.Invoice();
invoice.Header = invoiceHeader;
invoice.Line = listLine.ToArray();
Intuit.Ipp.Data.Qbd.Invoice addedInvoice = commonService.Add(invoice);
Chris
You need to read the following information about how QuickBooks for Windows Sync Manager works, how to see if Sync ran correctly, if objects are in an errored state and how to resolve. It could be any number of things. Once a record is inserted into the cloud, it asynchronously downloads to QuickBooks on the desktop, at which time business logic is applied and records are matched from the cloud to the desktop. If there is an issue, sync manager will show a record of the object that failed, why it failed and the object will now be in an error state.
At this point you can review the error and take steps to fix, like revert or update and resubmit. Links to the documentation below.
QuickBooks Sync Manager
Data Sync
Objects in Errored State
Sync Activity
Sync Status
regards
Jarred