Microsoft graph SingleValueLegacyExtendedProperty request returns empty GUID - c#

I made a request to get a specific single value property from all events in a calendar with the Graph-SDK. To achieve this i used a filter according to the Graph APIs documentation (https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/singlevaluelegacyextendedproperty_get). The filter i used was " id eq 'Boolean {00062002-0000-0000-C000-000000000046} Id 0x8223' ". Below is the code i used for this request.
public static async Task<Tuple<List<Event>, List<ICalendarEventsCollectionRequest>>> GetEventsSingleValuePropertyAsync(GraphServiceClient graphClient, String userId, String calendarId, String filterQuery, int top, String select)
{
List<ICalendarEventsCollectionRequest> requestList = new List<ICalendarEventsCollectionRequest>();
// filterQuery = "id eq 'Boolean {00062002-0000-0000-C000-000000000046} Id 0x8223'"
String filterSingleVP = "singleValueExtendedProperties($filter=" + filterQuery + ")";
List<Event> eventList = new List<Event>();
ICalendarEventsCollectionPage result = null;
ICalendarEventsCollectionRequest request = null;
if (calendarId == "")
request = graphClient.Users[userId].Calendar.Events.Request().Expand(filterSingleVP).Top(top).Select(select);
else
request = graphClient.Users[userId].Calendars[calendarId].Events.Request().Expand(filterSingleVP).Top(top).Select(select);
try
{
if (request != null)
{
result = await request.GetAsync();
requestList.Add(request);
eventList.AddRange(result);
}
if (result != null)
{
var nextPage = result;
while (nextPage.NextPageRequest != null)
{
var nextPageRequest = nextPage.NextPageRequest;
nextPage = await nextPageRequest.GetAsync();
if (nextPage != null)
{
requestList.Add(nextPageRequest);
eventList.AddRange(nextPage);
}
}
}
}
catch
{
throw;
}
return new Tuple<List<Event>, List<ICalendarEventsCollectionRequest>>(eventList, requestList);
}
I get all events and every event that matched the query gets expanded with the SingleValueLegacyExtendedProperty. The only thing that bothers me is that it looks like this:
"singleValueExtendedProperties":
[
{
"value":"true",
"id":"Boolean {00000000-0000-0000-0000-000000000000} Id 0x8223"
}
],
As you can see the value is present but the id now has an empty GUID.
I tested some other properties but i always had the same result.
I thought my filter compares the given "filterQuery" with the "id" in the answer.
Did i misunderstand something or is my request implementation just wrong?

That just looks like a bug on our side. Seems like the Guid prop might not be getting set or serialized correctly. I will bring it up to the team - thanks for the report.
-edit-
Yep, in fact we already have a fix for this that should be checked in in a few days.
-edit, edit-
Just for education's sake, the reason that it behaves this way is that the GUID that you are using is one of the "well known guids". In that case, our code is setting the well-known GUID field internally instead of the normal propertySetId guid field and REST always uses the propertySetId when rendering responses. The fix of course on our side is to use the well known guid field if the propertySetId is Guid.Empty.
-edit,edit,edit-
A fix was checked in for this and will begin normal rollout. It should reach WW saturation in a few weeks.

Related

Firestore.Query.GetSnapshotAsync() never returns and doesn't give any errors - the program just doesn't stop

I'm building a game in Unity version 2019.4.1f1 (LTS) and i'm using Firebase Firestore database.
Basically when I try to fetch the user from the database, specifically when i'm calling GetSnapshotAsync(), it gets stuck.
The hierarchy is as follows:
StartState calls FetchUserState which calls DBGetObjectState which communicates with DatabaseManager (DatabaseManager contains the line where GetSnapshotAsync() gets called).
Would be really nice if someone could help me figure out this problem.
Thank you for helping!
class FetchUserState
//fetch user by userId
await stateManager.PushTempState(new DBGetObjectState<T>(StringConstants.DB_USERS_COLLECTION, //("Users")
field: "UserId", fieldEqualTo: _userId, withLoading: false));
class DBGetObjectState
Log("Getting Documents");
var documents = await DatabaseManager.GetDocuments<T>(_path, _limit, _orderBy, _orderByDescending, _field, _fieldEqualTo);
Log("Got something, checking..."); // doesn't get here
class DatabaseManager
public static async Task<Collection<T>> GetDocuments<T>(string path, int limit = -1,
string orderBy = null, string orderByDescending = null, string field = null, object fieldEqualTo = null)
{
var collectionRef = DBUtills.GetCollectionRef(path);
Query query = collectionRef;
if (field != null && fieldEqualTo != null)
query = query.WhereEqualTo(field, fieldEqualTo);
if (orderBy != null)
query = query.OrderBy(orderBy);
if (orderByDescending != null)
query = query.OrderByDescending(orderByDescending);
if (limit >= 0)
query = query.Limit(limit);
Debug.Log("Snapshotting!");
QuerySnapshot collectionSnapshot = await query.GetSnapshotAsync();
Debug.Log("got a snapshot, checking..."); // doesn't get here
//....
}
Thanks to anyone who tried to help, apparently it was a plugin that I used for handling tasks, I should've checked it first before even asking the question.
Anyways, if someone still experiences an issue, #derHugo's answer was definitely a solution, instead of awaiting the task, simply use ContinueWithOnMainThread() to create a callback that will be called after the task is finished, like that:
query.GetSnapshotAsync().ContinueWithOnMainThread(task =>
{
if(task.IsFaulted)
{
//....
}
QuerySnapshot collectionSnapshot = task.Result;
//....
}

Use an "If this already exists in the API" type statement

I have an API that I've created. I've (finally) managed to both GET and POST with it. Now I want to check the POST before it gets submitted.
I have a class with properties (is that the right word? I'm still learning the lingo) of id, name, city, state, and country.
I have a form with a button, and the button has a click event method that looks like this:
protected void submitButton_Click(object sender, EventArgs e)
{
void Add_Site(string n, string ci, string s, string co)
{
using (var client = new HttpClient())
{
site a = new site
{
name = n,
city = ci,
state = s,
country = co
};
var response = client.PostAsJsonAsync("api/site", a).Result;
if (response.IsSuccessStatusCode)
{
Console.Write("Success");
}
else
Console.Write("Error");
}
}
Add_Site(nameText.Text, cityText.Text, stateText.Text, countryText.Text);
}
Now, at this point, it's working as expected. However, I'd like to limit it.
What I want to do is to have it look at the nameText.Text value. Before it runs the POST statement, I want it to look at the other values in the GET of the API and make sure that name doesn't already exist.
While I know that I could probably update the database to make the name field unique, I'd rather do it programatically in C#.
Is this something that's possible within my C# code? If so, what function would I use and how would I get it to return the Site.Name attribute to compare my nameText.Text value?
EDIT:
Here is the code for the POST as requested in one of the comments. Note: This was auto-generated by Visual Studio when I added the controller.
// POST: api/site
[ResponseType(typeof(site))]
public IHttpActionResult Postsite(site site)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.site.Add(site);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = site.id }, site);
}
I wouldn't have any idea where to even start with adding an "If the name already exists, throw an error," so some kind of walkthrough is plenty.
Here's code for looking in the database if any sites have the provided name (site.Name):
if (db.site.Any(o => o.Name == site.Name))
return BadRequest("Name already exists.");
I eventually determined that I should modify the POST as said in the comments. The solution I used ended up in this question: How to limit POST in web API

Updating Entity in EF6 gives primary key exception

When I try to update this object in EF6 I get an error stating more than 1 entity has this primary key. Looking at this DB I know this to be untrue(from what I can see).
I need to be able to update a second object based on one of the properties on the posted object. The code below produces the error. I have left in commented out pieces that I have tried to get this to work.
public async Task<ActionResult> Edit(PricingRule pricingRule)
{
if(ModelState.IsValid)
{
var currentUser = await serv.UserManager.FindByIdAsync(User.Identity.GetUserId());
var company = currentUser.Company;
//var entityRule = serv.PricingService.PricingRules.Get(pricingRule.PricingRuleId);
//If this is the first rule, set it to the company default
var rulesCount = company.PricingRules.Count;
if (rulesCount <= 1 || company.DefaultPricingRule == null)
pricingRule.DefaultPricingRule = true;
//Make sure no other rules are marked as default, and update the company with this rule as default
if (pricingRule.DefaultPricingRule)
{
if (company.DefaultPricingRule != null)
{
var oldRule = serv.PricingService.PricingRules.Get(company.DefaultPricingRule.PricingRuleId);
oldRule.DefaultPricingRule = false;
//serv.PricingService.PricingRules.Update(oldRule);
}
company.DefaultPricingRule = pricingRule;
serv.CoreService.Companies.Update(company);
}
serv.PricingService.PricingRules.Update(pricingRule);
await serv.SaveAllChangesAsync();
return RedirectToAction("Index");
}
return View(pricingRule);
}
Whether or not it is the best practice or how it should technically be done, this is how I solved my problem.
The edited object I was passing in, needed to be marked as modified first, before doing any other operations. I am assuming this is because the context could then grab it and all other operations regarding it would be done "within context". Other wise I think it was trying to add a new object if I tried to attach it to company.DefaultPricingRule.
public async Task<ActionResult> Edit(PricingRule pricingRule)
{
if(ModelState.IsValid)
{
serv.PricingService.PricingRules.Update(pricingRule);
var currentUser = await serv.UserManager.FindByIdAsync(User.Identity.GetUserId());
var company = currentUser.Company;
//If this is the first rule, set it to the company default
var rulesCount = company.PricingRules.Count;
if (rulesCount <= 1 || company.DefaultPricingRule == null)
pricingRule.DefaultPricingRule = true;
//Make sure no other rules are marked as default, and update the company with this rule as default
if (pricingRule.DefaultPricingRule)
{
if (company.DefaultPricingRule != null)
{
var oldRule = serv.PricingService.PricingRules.Get(company.DefaultPricingRule.PricingRuleId);
oldRule.DefaultPricingRule = false;
serv.PricingService.PricingRules.Update(oldRule);
}
company.DefaultPricingRule = pricingRule;
serv.CoreService.Companies.Update(company);
}
await serv.SaveAllChangesAsync();
return RedirectToAction("Index");
}
return View(pricingRule);
}
If Anyone has a comment on if this is best practice or if there is a better way to do it, I gladly take criticism.

Modify GetQueryNameValuePairs() for UrlHelper.Link in ASP.NET WebApi

With ASP.NET WebApi, when I send GET api/question?page=0&name=qwerty&other=params and API should give result within pagination envelop.
For that, I'd like to put result and change given page querystring to other values.
I tried as below code but I got a bad feeling about this.
protected HttpResponseMessage CreateResponse(HttpStatusCode httpStatusCode, IEnumerable<Question> entityToEmbed)
// get QueryString and modify page property
var dic = new HttpRouteValueDictionary(Request.GetQueryNameValuePairs());
if (dic.ContainsKey("page"))
dic["page"] = (page + 1).ToString();
else
dic.Add("page", (page + 1).ToString());
var urlHelper = new UrlHelper(Request);
var nextLink= page > 0 ? urlHelper.Link("DefaultApi", dic) : null;
// put it in the envelope
var pageEnvelope = new PageEnvelope<Question>
{
NextPageLink = nextLink,
Results = entityToEmbed
};
HttpResponseMessage response = Request.CreateResponse<PageEnvelope<Question>>(httpStatusCode, pageEnvelope, this.Configuration.Formatters.JsonFormatter);
return response;
}
The NextPageLink gives a lot complex result.:
http://localhost/api/Question?Length=1&LongLength=1&Rank=1&SyncRoot=System.Collections.Generic.KeyValuePair%602%5BSystem.String%2CSystem.String%5D%5B%5D&IsReadOnly=False&IsFixedSize=True&IsSynchronized=False&page=1
My question is,
My page handling with Dictionary approach seems dirty and wrong. Is there better way to address my problem?
I don't know why urlHelper.Link(routeName, dic) gives such a verbose ToString result. How to get rid of unusable Dictionary-related properties?
The key issue probably in your code is the conversion to the HttpRouteValueDictionary. New it up instead and add in a loop all key value pairs.
The approach can be simplified quite a lot, and you should also probably want to consider using an HttpActionResult (so that you can more easily test your actions.
You should also avoid using the httproutevaluedictionary and instead write your UrlHelper like
urlHelper.Link("DefaultApi", new { page = pageNo }) // assuming you just want the page no, or go with the copying approach otherwise.
Where just pre calculate your page no (and avoid ToString);
Write it all in an IHttpActionResult that exposes an int property with the page No. so you can easily test the action result independently of how you figure out the pagination.
So something like:
public class QuestionsResult : IHttpActionResult
{
public QuestionResult(IEnumerable<Question> questions>, int? nextPage)
{
/// set the properties here
}
public IEnumerable<Question> Questions { get; private set; }
public int? NextPage { get; private set; }
/// ... execution goes here
}
To just get the page no, do something like:
Source - http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21
string page = request.Uri.ParseQueryString()["page"];
or
you can use this extension method below (from Rick Strahl)
public static string GetQueryString(this HttpRequestMessage request, string key)
{
// IEnumerable<KeyValuePair<string,string>> - right!
var queryStrings = request.GetQueryNameValuePairs();
if (queryStrings == null)
return null;
var match = queryStrings.FirstOrDefault(kv => string.Compare(kv.Key, key, true) == 0);
if (string.IsNullOrEmpty(match.Value))
return null;
return match.Value;
}

Unexpected results when querying on FormattedID

I have built a custom integration that queries the API by formatted ID. In cases where there are duplicate IDs of different types (US181 & DE181), I'm often receiving only a single response back from the system, and that seems to be the wrong artifact. I'd like to search for Tasks, Stories, and Defects using formatted ID (either US181 or 181) and receive the appropriate result.
C# code below:
public static string FindArtifactByFormattedId(string formattedId)
{
string artifactRef = null;
Request req = new Request("Artifact");
req.Query = new Query("FormattedId", Query.Operator.Equals, formattedId.Remove(0,2));
req.Workspace = rallyWorkspace;
QueryResult queryResult = restApi.Query(req);
if (queryResult.TotalResultCount > 0)
{
foreach(DynamicJsonObject djo in queryResult.Results)
{
if (djo["FormattedID"] == formattedId)
{
artifactRef = djo["_ref"];
break;
}
}
}
return artifactRef;
}
This appears to be a defect in our WSAPI. I have filed this internally so that it can be prioritized, until then you can you can always query on each individual artifact to look for a specific artifact by FormattedID.

Categories