Prevent Simple.OData.Client from fetching the whole structure - c#

I am using simple.odata.client in my application. The problem is the client is retrieving the whole structure at the first call which is too large (more than 30MB) and so I am getting a timeout? Is there any parameter/setting to prevent the client to retrieve the whole structure.
Is there any other package which can help me with my application instead of simple.odata.client

The Simple.OData client will retrieve the metadata from the service once for the lifecycle of the object.
You can also initialize the client with a metadata xml string which will prevent the client from making the call.
Below is an except of my code where MetaDataDocumentAsString is the XML metadata as a string. This code also sets the OAuth2 bearer token in the httpclient instance used to create the client.
HttpClient.BaseAddress = new Uri(AppSettings.Dynamics365.WebAPI_ServiceRootURL);
//Use the httpClient we setup with the Bearer token header
ODataClientSettings odataSettings = new ODataClientSettings(HttpClient, new Uri(WebAPI_VersionRelativeURL, UriKind.Relative))
{
//Setting the MetadataDocument property prevent Simple.OData from making the expensive call to get the metadata
MetadataDocument = MetaDataDocumentAsString
};
_ODataClient = new ODataClient(odataSettings);
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", GetToken().Access_token);}
See the github issue for more details
https://github.com/simple-odata-client/Simple.OData.Client/issues/314

I use OData Top and Skip in my client request call. For example;
var accessToken = await _psUtils.GetUspsReferenceApiAccessToken(token);
var client = new ODataClient(SetODataToken(_psUtils.GetBaseUspsReferenceApiUrl(), accessToken));
var annotations = new ODataFeedAnnotations();
addressComplianceCodes = await client.For<AddressComplianceCode>()
.Filter(x => x.Description.Contains(searchValue) || x.Code.Contains(searchValue))
.Top(pageSize).Skip(skip)
.OrderByDescending(sortColumn)
.FindEntriesAsync(annotations, token);
and in my client code, I have a pager that tracks the values I pass to top and skip so I can step through the pages. The Top is the total number of records per page. The annotations object returns a Count property you can use to show the total number of records. I.e.
annotations.Count
Here is a link to the OData.org tutorial that talks about top and skip.
https://github.com/simple-odata-client/Simple.OData.Client/wiki/Results-projection,-paging-and-ordering that talks about paging.

Related

AutoRest is requiring ServiceClientCredentials in the constructor, but I can't obtain that data without instantiating the client object

I generated an API client with AutoRest and am using the --add-credentials parameter so that I can pass in a bearer token. In order to get the token, I need to be able to instantiate the object and call my login method like this:
var client = new IOIWebAPI(new Uri("https://localhost:44325", UriKind.Absolute));
var loginResult = client.Login(authModel);
The problem is that every constructor requires ServiceClientCredentials. From what I understand, I need to create an instance of TokenCredentials, which includes the token string. But I can't do that because I can't get the token string without calling Login. And I can't call Login without having the token string.
I'm sure I'm just misunderstanding how to consume the API client. But any ideas on what I'm doing wrong here?
My best guess is that --add-credentials does not support unauthenticated endpoints. When you add creds, AutoRest assumes everything needs auth. I submitted an issue for this, but I suspect it won't be addressed any time soon.
My workaround was to create a TokenHelper class. I copy and pasted the code that AutoRest generated from my login endpoint into that class. So the code stays consistent, but it's not ideal because I may forget to update the endpoint if it ever changes.
var tokenHelper = new TokenHelper(baseUri);
var tokenResult = tokenHelper.GetTokenAsync(authModel).GetAwaiter().GetResult();
var token = new TokenCredentials(tokenResult.AccessToken, "Bearer");
var client = new IOIWebAPI(baseUri, token);

Clarification on how to update (patch) objects using the Microsoft.Graph Client

The following code is the only way I found so far to update an object using the Microsoft Graph Client Library
Scenario:
Load an exisiting object (an organization)
Modify a value (add entry in securityComplianceNotificationPhones)
Send the update
Code
var client = new GraphServiceClient(...);
var org = client.Organization["orgid"].Request().GetAsync().Result;
var secPhones = new List<string>(org.SecurityComplianceNotificationPhones);
secPhones.Add("12345");
var patchOrg = new Organization();
patchOrg.SecurityComplianceNotificationPhones = secPhones;
var orgReq = new OrganizationRequest(
client.Organization[org.Id].Request().RequestUrl,
client, new Option[] {});
orgReq.UpdateAsync(patchOrg).Wait();
I needed to use the patchOrg instance because of two things:
The Graph API documentation states
"In the request body, supply the values for relevant fields that
should be updated. Existing properties that are not included in the
request body will maintain their previous values or be recalculated
based on changes to other property values. For best performance you
shouldn't include existing values that haven't changed."
If you actually do include existing values that haven't changed
(i.e. assginedLicenses) the request fails, if those existing values
are readonly.
My question is: Is/will there be a more straightforward way of updating existing objects like for example in the Azure ActiveDirectory GraphClient? Just for comparison, the same scenario in Azure Active Directory Graph
var client = new ActiveDirectoryClient(...);
var org = client.TenantDetails.GetByObjectId("orgid").ExecuteAsync().Result;
org.SecurityComplianceNotificationPhones.Add("12345");
org.UpdateAsync().Wait();
The Graph client library model is slightly different from the older SDK model the AAD client library you linked. The older model passed around objects that tried to be a bit smarter and reason about which properties were changed, only sending those. One of the main drawbacks of this model was that the library made many more service calls in the background and had a much heavier payload in each call since ExecuteAsync() would often need to retrieve every object in the request builder chain. The newer library does require the developer to do more explicit reasoning about what data is being passed but also gives greater control over network calls and payload. Each model has its tradeoffs.
To accomplish what you want, here's the approach I would recommend instead of creating a second org object altogether:
var client = new GraphServiceClient(...);
var orgRequest = client.Organization["orgid"].Request();
var org = orgRequest.Select("securityComplianceNotificationPhones").GetAsync().Result;
var secPhones = new List<string>(org.SecurityComplianceNotificationPhones);
secPhones.Add("12345");
org.SecurityComplianceNotificationPhones = secPhones;
orgRequest.UpdateAsync(org).Wait();

How to remove a users manager in AzureAD using Microsoft.Azure.ActiveDirectory.GraphClient

I'm using the Microsoft.Azure.ActiveDirectory.GraphClient (Version 2.1.0) to write an app for Azure AD user management. I'm able to set the Manager of a user but have no idea how to clear the field.
Unfortunately the sample project provided on GitHub do not contain this function either.
I managed to clear the "manager" field using the code below. It is not using the Microsoft.Azure.ActiveDirectory.GraphClient library but gets the job done.
var token = <get your adal token here>
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var url = "https://graph.windows.net/<tenant domain>/users/<userid>/$links/manager?api-version=1.6"
var resp = httpClient.DeleteAsync(url).Result;
if (!resp.IsSuccessStatusCode)
{
// log / throw exception etc.
}
You need to perform a DELETE HTTP request to https://graph.microsoft.com/v1.0/users/<user_email>/manager/$ref (make sure to replace the <user_email> in the URL.
A successful call will receive 204 response code and empty string as the response body.
This method is currently missing from the Microsoft Graph API docs but should be added in the future. (see here)
Also you should start using Microsoft Graph (graph.microsoft.com) instead of Azure AD Graph (graph.windows.net) as the latter is becoming obsolete. (See here)
//Assign and remove user's manager
// User.Manager = newUser as DirectoryObject;
User.Manager = null;

Read Emails from Office 365 via c# using refresh token

So I'm able to login to Outlook via OAuth2 and get it to provide my app with access and refresh tokens.
However, I cannot seem to figure out how to get Outlook OAuth2 to give me another token using the provided refresh token. I've messed with this code quite a few times trying to get something to work using C# HttpClient(). Additionally, I've tried to follow this article and use the "native" "Experimental.IdentityModel.Clients.ActiveDirectory" library (what is this anyway?) to accomplish my task.
I could log in with this library and get an access code, but it wouldn't give me a refresh token. This particular library doesn't seem to provide access to refresh tokens, even if they are provided in the response.
Anyway, so here's the HttpClient code that I'm using to get an Access Token (this is from my callback Controller Method):
string authCode = Request.Params["code"];
var client = new HttpClient();
var clientId = ConfigurationManager.AppSettings["ida:ClientID"];
var clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
var parameters = new Dictionary<string, string>
{
{"client_id", clientId},
{"client_secret", clientSecret},
{"code",authCode },
{"redirect_uri", Url.Action("Authorize", "Manage", null, Request.Url.Scheme)},
{"grant_type","authorization_code" }
};
var content = new FormUrlEncodedContent(parameters);
var response = await client.PostAsync("https://login.microsoftonline.com/common/oauth2/v2.0/token",content);
var tokens = await response.Content.ReadAsAsync<MicrosoftOAuthAuthenticationModel>();
var originalRefreshToken = tokens.refresh_token;
var originalAccessToken = tokens.access_token;
originalAccessToken gets generated as expected. Now here's the part I can't figure out:
var parameters2 = new Dictionary<string, string>
{
{"grant_type", "refresh_token"},
{"refresh_token", originalRefreshToken},
{"client_id", clientId},
{"client_secret", clientSecret},
{"resource","https://outlook.office365.com" }
};
var content2 = new FormUrlEncodedContent(parameters2);
var response2 = await client.PostAsync("https://login.microsoftonline.com/common/oauth2/token", content2);
var tokens2 = await response2.Content.ReadAsAsync<MicrosoftOAuthAuthenticationModel>();
var newRefreshtoken = tokens2.refresh_token;
var newAccessToken = tokens2.access_token;
I get a 400 error from the server that says "Authentication failed: Refresh Token is malformed or invalid". This seems weird because I'm literally grabbing the refresh token from the response and using it.
Does anyone have any information that might help? Alternatively, does anyone know who to contact for help? Last, the goal here is to simply be able to persistently read emails from an inbox on office 365 via the API so I can get the email id, conversation id, subject, content, from email address, etc. and process it. Is there an easier way to be doing this? This can't be a difficult or uncommon thing to do.
If you are using the ADAL library (Experimental.IdentityModel.Clients.ActiveDirectory), you don't need to save the refresh token. The library saves the token and manages refreshing as needed for you. You just always retrieve the token using the AcquireSilent... (don't remember the exact method name), and it will pull from cache as it can and refresh when needed.
In your code your likely seeing this problem because in the refresh you're not posting to the v2 endpoint. Change your endpoint URL to https://login.microsoftonline.com/common/oauth2/v2.0/token and see if that doesn't fix it. If it still doesn't work, you might compare with http://oauthplay.azurewebsites.net/.

How to read data from Azure Marketplace Fixed query with Windows Store app?

I have this code to call the Service and I get the following error.
Uri serviceUri = new Uri("https://api.datamarket.azure.com/miosoft/coordinate-distance-calculator/v1/");
CoordinateDistance context = new CoordinateDistance(serviceUri);
context.IgnoreMissingProperties = true;
context.Credentials = new NetworkCredential("xxx#outlook.com", "xxx");
IEnumerable<Distance> nquery;
nquery = context.CoordinateDistanceServiceMethod(-122.347938, 47.637933, -122, 47);
This target framework does not enable you to directly enumerate over a data service query. This is because enumeration automatically sends a synchronous request to the data service. Because this framework only supports asynchronous operations, you must instead call the BeginExecute and EndExecute methods to
obtain a query result that supports enumeration.
So I found code that explained how to call this the right way so I added this code here
DataServiceQuery<Distance> query = (DataServiceQuery<Distance>)nquery;
TaskFactory<IEnumerable<Distance>> taskFactory = new TaskFactory<IEnumerable<Distance>>();
IEnumerable<Distance> result = await taskFactory.FromAsync(query.BeginExecute(null, null), iar => query.EndExecute(iar));
But now on the final Line I get Invalid Parameters.
Any idea ? Or working samples to call a Azure Marketplace fixed query with a Windows Store App ?

Categories