querying across application insights resource with REST API - azure-application-insights

I have data which are stored in 3 different application insights resources, thanks to query across resource feature added last year (https://azure.microsoft.com/en-us/blog/query-across-resources/) was possible to query those 3 application insights at once with app identifier.
I'm trying to execute this query through app insights REST API : https://dev.applicationinsights.io (app insights REST API) for a very basic need from a static HTML page (no backend)
but without luck
I do suspect that app identifier isn't supported, it is actually the case ? any workaround for my use case (no backend).

Here is an example with the query in the body. My queries are quite complex and have a lot of let statements and therefore passing the query in the body is easier. There are some PowerShell quirks in the example below, but I'll update with a C# example tomorrow.
The let statement in the example below is pretty pointless, it's mostly there to show that you can do complex queries with let expressions etc.
AppId is the Application Insights resource ID - and NOT the instrumentation key. The API key is just a long string and you can create up to 10 of them AFAIK.
You will find both the id and keys under API Access (I've added a screenshot as it's easy to get them confused). When you use the app() function use the app id.
$app1Id = "GUID"
$app2Id = "GUID"
$app1Key = "string"
$app2Key = "string"
# EXAMPLE: "X-Api-Key" = "key1:GUID1,key2:GUID2"
$headers = #{ "X-Api-Key" = "${app1Key}:$app1Id,${app2Key}:$app2Id"; "Content-Type" = "application/json" }
# EXAMPLE: "query" = "union app('GUID1').something, app('GUID2').something | limit 5"
$query = #{"query" = "let days=1d;union app('$app1Id').exceptions,app('$app2Id').exceptions | where timestamp > ago(days)"}
$body = ConvertTo-Json $query | % { [regex]::Unescape($_) }
$result = Invoke-RestMethod "https://api.applicationinsights.io/v1/apps/$app1Id/query" -H $headers -Body $body -Method POST
The query above will return all the exceptions for the two Application insights resources for the last day. You can do a query across 10 resources at the time of writing, 200 requests per 30 seconds or a max of 86,400 requests per day (UTC). Other limits apply if you use ADD.
NOTE: the extra {} in the header is a PowerShell quirk in regards to variables and the use of the colon char, and as you can see in the example you should not bracket the keys in the header :)

Checked with the dev team that owns that service:
You should be able to put the api key in as apiKey1:appId1,apiKey2:appId2 in the api key box and this should work.
the [object ProgressEvent] response is a bug in the explorer that should have really showed you an error.
And as a workaround, you could always do the queries inside the azure portal itself in workbooks for any of the AI resources, or hypothetically also from the analytics portal for any of the AI resources, and those wouldn't require the API key at all, if all you needed was to see the data.

Related

Adding a button for google signin using f#/fable/asp.net/react

I'm working with the SAFE stack (https://safe-stack.github.io/) and through the example dojo. It's great so far.
I'd like to extend the example to include a button to login/auth via Google. So I looked at an example on the Google website (https://developers.google.com/identity/sign-in/web/build-button). And then I had a look how to do authentication using ASP.NET (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-2.1&tabs=aspnetcore2x) As a result I ended up confused as to how to integrate this into a SAFE project. Can someone tell me what they would do? SHould I be trying to use ASP.NET Identity or should I be using the JWT approach? I don't even know if they are the same since I'm very new to web frameworks.....
The other question I have is how would one inject raw Javascript into the client side of a SAFE project. The google example above shows raw JS/CSS/HTML code? Should I be injecting that as is or should I look in React for some button that does this and map that idea back through Fable?
Setting up OAuth
The easiest way to use Google OAuth is to wait until the next release of Saturn, at which point Saturn will include the use_google_oauth feature that I just added. :-) See the source code if you're interested in how it works, though I'm afraid you can't implement this yourself with use_custom_oauth because you'll run into a type error (the underlying ASP.NET code has a GoogleOptions class, and use_custom_oauth wants an OAuthOptions class, and they aren't compatible).
To use it, add the following to your application CE:
use_google_oauth googleClientId googleClientSecret "/oauth_callback_google" []
The last parameter should be a sequence of string * string pairs that represent keys and values: you could use a list of tuples, or a Map passed through Map.toSeq, or whatever. The keys of that sequence are keys in the JSON structure that Google returns for the "get more details about this person" API call, and the values are the claim types that those keys should be mapped to in ASP.NET's claims system. The default mapping that use_google_oauth already does is:
id → ClaimTypes.NameIdentifier
displayName → ClaimTypes.Name
emails[] (see note) → ClaimTypes.Email
Those three are automatically mapped by ASP.NET. I added a fourth mapping:
avatar.url → `"urn:google:avatar:url"
There's no standard ClaimTypes name for this one, so I picked an arbitrary URN. Caution: this feature hasn't been released yet, and it's possible (though unlikely) that this string might change between now and when the feature is released in the next version of Saturn.
With those four claim types mapped automatically, I found that I didn't need to specify any additional claims, so I left the final parameter to use_google_oauth as an empty list in my demo app. But if you want more (say you want to get the user's preferred language to use in your localization) then just add them to that list, e.g.:
use_google_oauth googleClientId googleClientSecret "/oauth_callback_google" ["language", "urn:google:language"]
And then once someone has logged in, look in the User.Claims seq for a claim of type "urn:google:language".
Note re: the emails[] list in the JSON: I haven't tested this with a Google account that has multiple emails, so I don't know how ASP.NET picks an email to put in the ClaimTypes.Email claim. It might just pick the first email in the list, or it might pick the one with a type of account; I just don't know. Some experimentation might be needed.
Also note that third-party OAuth, including GitHub and Google, has been split into a new Saturn.Extensions.Authorization package. It will be released on NuGet at the same time that Saturn's next version (probably 0.7.0) is released.
Making the button
Once you have the use_google_oauth call in your application, create something like the following:
let googleUserIdForRmunn = "106310971773596475579"
let matchUpUsers : HttpHandler = fun next ctx ->
// A real implementation would match up user identities with something stored in a database, not hardcoded in Users.fs like this example
let isRmunn =
ctx.User.Claims |> Seq.exists (fun claim ->
claim.Issuer = "Google" && claim.Type = ClaimTypes.NameIdentifier && claim.Value = googleUserIdForRmunn)
if isRmunn then
printfn "User rmunn is an admin of this demo app, adding admin role to user claims"
ctx.User.AddIdentity(new ClaimsIdentity([Claim(ClaimTypes.Role, "Admin", ClaimValueTypes.String, "MyApplication")]))
next ctx
let loggedIn = pipeline {
requires_authentication (Giraffe.Auth.challenge "Google")
plug matchUpUsers
}
let isAdmin = pipeline {
plug loggedIn
requires_role "Admin" (RequestErrors.forbidden (text "Must be admin"))
}
And now in your scope (NOTE: "scope" will probably be renamed to "router" in Saturn 0.7.0), do something like this:
let loggedInView = scope {
pipe_through loggedIn
get "/" (htmlView Index.layout)
get "/index.html" (redirectTo false "/")
get "/default.html" (redirectTo false "/")
get "/admin" (isAdmin >=> htmlView AdminPage.layout)
}
And finally, let your main router have a URL that passes things to the loggedInView router:
let browserRouter = scope {
not_found_handler (htmlView NotFound.layout) //Use the default 404 webpage
pipe_through browser //Use the default browser pipeline
forward "" defaultView //Use the default view
forward "/members-only" loggedInView
}
Then your login button can just go to the /members-only route and you'll be fine.
Note that if you want multiple OAuth buttons (Google, GitHub, Facebook, etc) you'll probably need to tweak that a bit, but this answer is long enough already. When you get to the point of wanting multiple OAuth buttons, go ahead and ask another question.

Joining PageViews and Request in Application Insights Log Analytics

I want to join pageViews that are coming from the AppInsights browser SDK, to the request on the backend. I don't see a foreign key that makes sense, is there one OOTB? or do I need to code something to join them together?
To add context, I am interested in pageView duration by cloudRoleInstance (server), but cloudRoleInstance is only available on requests.
I tried the following, and did not work, I supose the operation IDs are not the same.
pageViews
| join (requests) on operation_Id
You can join by Operation ID (operation_Id).
Here is the query which returns all documents for a particular operation_Id:
union *
| where timestamp > ago(1d)
| where operation_Id == "<operation_id>"
I was interested in exactly the same thing and this is how I ended up solving it:
Set a "cloud_RoleInstance" cookie for each response from the server so that the client javascript would know which role instance sent the last response.
Add a TelemetryInitializer to the client-side Application Insights instance which pulls the RoleInstance cookie and adds it as data to the telemetry collected client-side.
*The reason I did it this way instead of joining on operationId as the other answer says is because operationId seemed to span many requests on the server, sometimes over the course of a half an hour. Maybe that has is because of the way our Single Page Application is set up, but operationId just wasn't working for me.
Code
BaseController.cs::BeginExecute (We have our own BaseController which all other controllers inherit from)
var roleInstanceCookie = requestContext.HttpContext.Response.Cookies.Get("cloud_RoleInstance");
roleInstanceCookie.Value = Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.CurrentRoleInstance.Id;
requestContext.HttpContext.Response.Cookies.Set(roleInstanceCookie);
ApplicationInsights.js (This contains our AI snippet that loads AI, currently using version 2.3.1 of the JS SDK)
// ... initialization snippet ...
appInsights.addTelemetryInitializer((envelope) => {
envelope.data.cloud_RoleInstance = getCookie("cloud_RoleInstance");
});
The cloud_RoleInstance will then end up in the customDimensions column of your PageViews in Application Insights

Meteor Publish/Subscribe passing object with string parameter issue

I am trying to pass a object { key:value} and send it to meteor publish so i can query to database.
My Mongo db database has (relevant datas only) for products:
products : {
categs:['Ladies Top','Gents'],
name : Apple
}
In meteor Publish i have the following:
Meteor.publish('product', (query) =>{
return Clothings.find(query);
})
In client i use the following to subscribe:
let query = {categs:'/ladies top/i'}; // please notice the case is lower
let subscribe = Meteor.subscribe('product',query);
if (subscribe.ready()){
clothings = Products.find(query).fetch().reverse();
let count = Products.find(query).fetch().reverse().length; // just for test
}
The issue is, when i send the query from client to server, it is automatically encoded eg:
{categs:'/ladies%top/i'}
This query doesnot seem to work at all. There are like total of more than 20,000 products and fetching all is not an option. So i am trying to fetch based on the category (roughly around 100 products each).
I am new to ,meteor and mongo db and was trying to follow existing code, however this doesnot seem to be correct. Is there a better way to improve the code and achieve the same ?
Any suggestion or idea is highly appreciated.
I did go through meteor docs but they dont seem to have examples for my scenario so i hope someone out there can help me :) Cheers !
Firstly, you are trying to send a regex as a parameter. That's why it's being encoded. Meteor doesn't know how to pass functions or regexes as parameters afaict.
For this specific publication, I recommend sending over the string you want to search for and building the regex on the server:
client:
let categorySearch = 'ladies top';
let obj = { categorySearch }; // and any other things you want to query on.
Meteor.subscribe('productCategory',obj);
server:
Meteor.publish('productCategory',function(obj){
check(obj,Object);
let query = {};
if (obj.categorySearch) query.category = { $regex: `/${obj.categorySearch}/i` };
// add any other search parameters to the query object here
return Products.find(query);
});
Secondly, sending an entire query objet to a publication (or Method) is not at all secure since an attacker can then send any query. Perhaps it doesn't matter with your Products collection.

Linq2Dynamodb run expression string on Table

I have an ASP.NET Web API site which records all objects in AWS DynamoDb. I took a quick look at linq2Dynamodb. It seems that the common way to use it is like:
var moviesTable = ctx.GetTable<Movie>();
var inceptionMovie = moviesTable
.Where(m => m.Genre == "Thriller" && m.Title == "Inception")
.Single();
But I want some API like:
moviesTable.Execute(string querystring);
The reason is that from the Web API, I usually get some query like:
http://host/service.svc/Orders?$filter=ShipCountry eq 'France'
I'd like to pass the filter string "ShipCountry eq 'France'" here. Do anyone know if there is a way for me to do this? Thanks.
With Linq2DynamoDb you can expose your DynamoDb tables as OData resources. Please, see the doc here.
As soon as you have an OData resource, you can query it with OData queries of any valid kind.
And you can use any OData client library for that. E.g. with System.Data.Services.Client you would say something like:
var entities = myContext.Execute<MyEntityType>(new Uri("<here goes URI with valid OData query string>"), "GET", true);
or just construct a LINQ query on the client side.
The drawback is that you will need to host your OData service somewhere (DynamoDb itself doesn't support OData, so that's the reason).
The advantages are:
you will be able to cache your data in ElastiCache.
you will be able to implement custom authentication inside your service.
Sorry for a bit late answer.

Returning ItemStats from Tridion UGC

I was wondering if anyone can offer any pointers on this one. I'm trying to return ItemStats from the Tridion UGC web service but I'm getting the following error when trying to bind the results:-
The closed type TridionWebUGC.CDS.ItemStat does not have a corresponding LastRatedDate settable property.
An example of code is:-
WebServiceClient ugcCall2 = new WebServiceClient();
Uri uri = new Uri("http://new.ugc.service/odata.svc");
CDS.ContentDeliveryService cds = new CDS.ContentDeliveryService(uri);
var myItemStats = cds.ItemStats.Where(p => p.PublicationId == 68 && p.Id == 17792 && p.Type==16);
I can get comments and ratings with no problem. E.g.
var myComments = cds.Comments.Where(p => p.ItemId == 17805).OrderBy(p => p.CreationDate);
It's just ItemStats that are giving me an issue. Anybody any ideas?
Thanks
John
Unfortunately, the metadata of the UGC WebService is not correct in regards to the ItemsStats. For you it means that the webservice metadata does not expose the fact that the ItemStat entity contains the LastRatedDate property. This makes your .NET proxies not to be aware of this property and makes your query fail.
To work-around this defect you have two option:
Add to your service the following property: cds.IgnoreMissingProperties = true;. Advantage of this approach is that you're done with it in 2 sec. Disadvantage is that you will not be able to access that property (in case you actually use it);
Modify the proxies generated by Visual Studio and manually add that property to the ItemStat class. Advantage of this approach is that you will be able to access the property from your project. Disadvantage is that it's totally not manageable from the coding point of view, you need to be careful when you upgrade or regenerate the proxies and it's easy to make a mistake while manually adding the property.
Note 1: to access the metadata of your webServer from the browser your can go to /odata.svc/$metadata.
Note 2: on a closer look there are 2 properties missing from the webService metadata: LastRatedDate and LastCommentedDate.
Hope this helps.

Resources