How to wrap up external api response in order to meet json datasource backend requirements - plugins

I’m trying to create a grafana datasource plugin which is to return a response with table type format.
There is a third party rest API which should provide data to the datasource, however, the request/response format of this external API do not conform with the example simple-json-datasource plugin urls and requests formats.
Is there a way I could wrap up the external api response in the datasource backend methods (/query) which is then called by the front-end part of datasource? (external api URL will be provided at the datasource settings UI while adding the datasource)
I'll appreciate the answers/suggestion.

You would have to write code that transforms the third party API response into the table type format.
You may want to start off of something like this: https://github.com/grafana/simple-json-datasource
Clone that project, and change the url values passed in options to the doRequest() function, those values should point to your third party API.
The doRequest() function seems to return a Promise, so you can do your data transformation when they are fulfilled, for example:
// simple-json-datasource/src/datasource.js
// in the query() function
return this.doRequest(options).then(function(result) {
// your transform code here;
return transformedResult;
})

Related

How to filter by dimension using Google Analytics Data API (GA4) Java client library?

I am trying to call Google Analytics Data API (GA4) using the Java client library and applying a dimension filter. This is the call which is working if I don't use the setDimensionFilter call:
RunReportRequest request =
RunReportRequest.newBuilder()
.setProperty(propertyId)
.addDimensions(com.google.analytics.data.v1beta.Dimension.newBuilder().setName("pageLocation"))
.addMetrics(com.google.analytics.data.v1beta.Metric.newBuilder().setName("screenPageViews"))
.addMetrics(com.google.analytics.data.v1beta.Metric.newBuilder().setName("activeUsers"))
// .setDimensionFilter(FilterExpression.newBuilder().setFilter(Filter.newBuilder().setStringFilter(
// Filter.StringFilter.newBuilder()
// .setMatchType(Filter.StringFilter.MatchType.FULL_REGEXP)
// .setField(Descriptors.FieldDescriptor, "pageLocation")
// .setValue("MY_REGEXP")
// .build())))
.addDateRanges(com.google.analytics.data.v1beta.DateRange.newBuilder()
.setStartDate(startDate.toStringYYYYMMDDWithDashes())
.setEndDate(endDate.toStringYYYYMMDDWithDashes()))
.setKeepEmptyRows(true)
.build();
I don't know how to use setDimensionFilter. If the usage which is commented in the previous code is correct, then the only thing missing is the call to setField. I don't know how to generate the Descriptors.FieldDescriptor instance (or even its meaning).
I have reviewed the client library javadoc, and also the code samples (which are really simple and unfortunately do not show any usage of setDimensionFilter).
The Descriptors.FieldDescriptor isn't part of the GA4 Data API and is an internal functionality of the protobuf framework
If you are trying to call this filter on a field with the name 'pageLocation' instead of using setField, I think you can do something like this
RunReportRequest request =
RunReportRequest.newBuilder()
.setProperty("properties/" + propertyId)
.addDimensions(com.google.analytics.data.v1beta.Dimension.newBuilder().setName("pageLocation"))
.addMetrics(com.google.analytics.data.v1beta.Metric.newBuilder().setName("screenPageViews"))
.addMetrics(com.google.analytics.data.v1beta.Metric.newBuilder().setName("activeUsers"))
.setDimensionFilter(FilterExpression.newBuilder()
.setFilter(Filter.newBuilder()
.setFieldName("pageLocation")
.setStringFilter(Filter.StringFilter.newBuilder()
.setMatchType(Filter.StringFilter.MatchType.FULL_REGEXP)
.setValue("MY_REGEXP"))))
.addDateRanges(com.google.analytics.data.v1beta.DateRange.newBuilder()
.setStartDate("2020-03-31")
.setEndDate("2021-03-31"))
.build();
Also, if you want an additional example of how to use setDimensionFilter, here is another code example that might help
RunReportRequest request =
RunReportRequest.newBuilder()
.setProperty("properties/" + propertyId)
.addDimensions(Dimension.newBuilder().setName("city"))
.addMetrics(Metric.newBuilder().setName("activeUsers"))
.addDateRanges(DateRange.newBuilder().setStartDate("2020-03-31").setEndDate("today"))
.setDimensionFilter(FilterExpression.newBuilder()
.setAndGroup(FilterExpressionList.newBuilder()
.addExpressions(FilterExpression.newBuilder()
.setFilter(Filter.newBuilder()
.setFieldName("platform")
.setStringFilter(Filter.StringFilter.newBuilder()
.setMatchType(Filter.StringFilter.MatchType.EXACT)
.setValue("Android"))))
.addExpressions(FilterExpression.newBuilder()
.setFilter(Filter.newBuilder()
.setFieldName("eventName")
.setStringFilter(Filter.StringFilter.newBuilder()
.setMatchType(Filter.StringFilter.MatchType.EXACT)
.setValue("in_app_purchase"))))))
.setMetricFilter(FilterExpression.newBuilder()
.setFilter(Filter.newBuilder()
.setFieldName("sessions")
.setNumericFilter(Filter.NumericFilter.newBuilder()
.setOperation(Filter.NumericFilter.Operation.GREATER_THAN)
.setValue(NumericValue.newBuilder()
.setInt64Value(1000)))))
.build();

Controller return type and httpStatus best practice and production/consumption on method in REST WS

I commence in REST and I have some questions:
What type must the controller return? Typically, I'm asking if my Rest #Controller must return Item object as it is or encapsulate it in ResponseEntity in order to specify http-status-code.
What http status code to use in a GET method on a particular item ("/items/2") if the given item does not exists: HttpMediaStatus.OK(200) and null return or HttpStatus.NO_CONTENT(204) and null return ?
Second part: I saw it was possible to specify #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
Context: using Spring Boot, hibernate, REST webservice.
Thank you.
Many questions in one, I'll provide short answers with a bunch of link to relevant articles and the reference documentation.
What type must the controller return?
Depends on your annotation and the RESTful-ness of your service. There are three annotations you can use for controllers: #Controller, #RestController and #RepositoryRestController.
Controller is the base annotation to mark your class as a controller. The return type of the controller endpoint methods can be many things, I invite you to read this dedicated post to get a grasp of it.
When developing a pure-REST service, you will focus on using RestController and RepositoryRestController.
RestControlleris Controller + ResponseBody. It binds the return value of the endpoint method to the web response body:
#RestController
public ItemController {
#RequestMapping("/items/{id}")
public Item getItem(#PathVariable("id") String id) {
Item item = ...
return item;
}
}
With this, when you hit http:/.../api/items/foo, Spring does its magic, automatically converting the item to a ResponseEntity with a relevant 40X status code and some default HTTP headers.
At some point, you will need more control over the status code and headers, while still benefiting from Spring Data REST's settings. That's when you will use RepositoryRestController with a ResponseEntity<Item> as return type, see the example the Spring Data REST reference.
What http status code to use in a GET method on a particular item if the given item does not exists?
Bluntly said: use HttpStatus.NOT_FOUND. You're looking for a resource that does not exist, there's something wrong.
That being said, it is completely up to you to decide how to handle missing resources in your project. If your workflow justifies it, a missing resource could be something completely acceptable that indeed returns a 20X response, though you may expect users of your API to get confused if you haven't warned them or provided some documentation (we are creatures of habits and conventions). But I'd still start with a 404 status code.
(...) #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
#Consumes and #Produces are respectively matched against content-type and accept headers from the request. It's a mean of restricting the input accepted and the output provided by your endpoint method.
Since we're talking about a REST service, communications between clients of the API and the service are expected to be JSON-formatted. Thanks to Spring HATEOAS, the answer are actually formatted with the application/hal+json content-type.
In that scenario, you can indeed not bother with those two annotations. You will need them if you develop a service that accepts different content-types (application/text, application/json, application/xml...) and provides, for instance, HTML views to users of your website and JSON or XML response to automated clients of your service.
For real life examples:
Facebook provides the Graph API for applications to read to/write from its graph, while users happily (?) surf on web pages
Google does the same with the Google Maps API

admin-on-rest Rest Client not mapping record with GET_ONE secondary API call

I have a GET_LIST that calls an API, displaying me an object of data. But when I create the Edit component which is under the same resource component, I am calling a second API call that shows additional data for that GET_ONE item.
My issue, is that although I can configure a GET_ONE call inside my rest client, I am unable to get the Edit component do use the GET_ONE API call response object. It is still using the GET_ALL API call response object.
Here's some sample code. Note, that my GET_ONE api call doesn't have {resource} because the URL link doesn't have that word.
const API_URL = 'https://api.link/biglist';
const 2API_URL2 = 'https://api.link/address=123456';
switch (type) {
case GET_LIST: {
url = `${API_URL}/${resource}`;
break;
}
case
GET_ONE:
url = `${2API_URL2}/${params.id}`;
break;
Inside the Edit component, I have an ImageField. By console logging the record inside the ImageField component, I can see that the record response object is using the GET_ALL API response, instead of GET_ONE API response.
I have made sure that when I access the Edit component of a specific item inside the GET_LIST, my Edit component does indeed make an API call to my secondary API request, and it does return me the response object of the secondary request. I just can't somehow map the secondary API response with record inside ImageField.
So my question is, how do I get the GET_ONE response inside my Edit component (which is under the same resource component) to use my second API response and map it with record?
I solved my problem by adding a fetch API call inside my resource components, and created custom field components to use my secondary API calls instead of using record. I feel there's a much better solution, but this'll do.

Integrate AngularJS App with SoftwareAG webMethods Integration Server

I have been trying to set up a sample AngularJS app with webMethods Integration Server on the backend. Using $resource, I can easily pull normal JSON files and manipulate the data within the file. However, the goal is that I want to create services in webMethods Designer and call them from AngularJS using $resource to display the data in my app. The problem is that from AngularJS I cannot extract the data I need from the service that I'm creating in Designer. In Designer I can use (in WMPublic) documentToJSONString, and output something like:
jsonString {"id":"1", "name":"Dan", "quantity":"3"}
But I cannot extract the data because this is not a pure JSON string. Does anyone know how to (1) extract the JSON string output data using AnularJS or (2) output a JSON document from Designer? I am calling a REST service; something to the effect of
http://localhost:2222/rest/Get/getOrderData
from my services.js file in AngularJS.
Here is my services.js file:
/* Services */
var orderServices = angular.module('orderServices', ['ngResource']);
orderServices.factory('Order', ['$resource',
function($resource){
return $resource('http://localhost:2222/rest/REST/getOrderData', {}, {
query: {method:'GET', isArray:true}
});
}]);
Then, in my app, I want to use an ng-repeat to call things like {{order.id}}, {{order.name}} etc. Is anyone good with webMethods and Angular or done this before?
To force the response that you want, I would have used the service
pub.flow:setResponse mapping the jsonString to it's string parameter and probably hardcoded (eww!) the contentType parameter to 'application/json'
You may also need to use the service pub.flow:setResponseCode to set the response code.
They would be the last services in getOrderData
I would have invoked it using the below (where namespace is the folder structure in designer)
http://localhost:2222/invoke/namespace:getOrderData
The above applies to Integration Server V8 and it looks like you're using V9 since some of the services that you mention didn't exist in V8. This would also apply to a normal flow service, not a specific REST one (assuming they exist in V9).

Guidance on a better way to retain filtering options when using ASP.NET MVC 2

I have an ASP.NET MVC 2 application which in part allows a user to filter data and view that data in a JQGrid.
Currently this consists of a controller which initialises my filter model and configures how I wish my grid to be displayed. This information is used by a view and a partial view to display the filter and the grid shell. I use an editor template to display my filter. The JQGrid makes use of a JsonResult controller action (GET) to retrieve the results of the filter (with the addition of the paging offered by the grid - only a single page of data is returned by the GET request. The Uri used by the grid to request data contains the filter model as a RouteValue - and currently contains a string representation of the current state of the filter. A custom IModelBinder is used to convert this representation back into an instance of the filter model class.
The user can change the filter and press a submit button to get different results - this is then picked up by an (HttpPost) ViewResult action which takes the filter model - reconstituted by a further model binder and causes the grid shell to be updated.
So I have:
FilterModel
Represents the user's desired filtering characteristics
FilterModelEditorTemplateSubmissionBinder : DefaultModelBinder - used to convert the request information supplied from a user changing their filtering characteristics into the appropriate FilterModel instance.
FilterModelStringRepresentationBinder : IModelBinder - used to convert the encoded filter from the JQGrid GET request for data so the correct request is made of the service which is ultimately performing the query and returning the relevant data.
ViewResult Index() - constructs a default filter, configures the grid specification and returns the view to render the filter's editor template, and the grid shell.
[HttpPost]ViewResult Filter(FilterModel filter) - takes the new filter characteristics and returns the same view as Index(). Uses FilterModelEditorTemplateSubmissionBinder to bind the filter model.
JsonResult GetData(FilterModel filter, string sidx, string sord, int page, int rows) - called from the JQGrid in order to retrieve the data. Uses FilterModelStringRepresentationBinder to bind the filter model.
As a complication, my filter model contains a option to select a single value from a collection of items. This collection is retrieved from a service request and I don't want to keep querying for this data everytime I show the filter, currently I get it if the property is null, and then include the options hidden in the editor template and encoding in the string representation. These options are then reconstituted by the relevant model binder.
Although this approach works I can't help but feel that I am having to basically reinvent viewstate in order to maintain my filter and the included options. As I am new to ASP.NET MVC but am very happy with classic ASP and ASP.NET Web Forms I thought I'd throw this out there for comment and guidance as to find a way which more closely fits with the MVC pattern.
It seems to me that the best way in to divide some actions which provide pure data for the jqGrid from other controller action. Such jqGrid-oriented actions can have prototype like:
JsonResult GetData(string filter, string sidx, string sord, int page, int rows)
I personally prefer to implement this part as WCF service and to have this WCF service as a part of the same ASP.NET site. In general it's much more the matter of taste and depends on your other project requirements.
This part of you ASP.NET site could implement users authentication which you need and can be tested with unit tests exactly like other actions of your controllers.
The views of the ASP.NET MVC site can have empty data for jqGrids, and have only correct URLs and probably generate the HTML code depends on the users permission in the site. Every page will fill the data of jqGrids with respect of the corresponds requests to the server (request to the corresponding GetData action).
You can use HTTP GET for the data for the best data caching. The caching of data is the subject of a separate discussion. If you do this, you should use prmNames: { nd:null } in the definition of jqGrid to remove unique nd parameter with the timestamp added per default to every GET request. To have full control of the data caching on the server side you can for example add in HTTP headers of the server responses both "Cache-Control" set to "max-age=0" and "ETag" header with the value calculated based of the data returned in the response. You should test whether the request from the client has "If-None-Match" HTTP header with the value of "ETag" coresponds the data cached on the client. Then you should verify whether the current data on the server (in the database) are changed and, if there are not changed, generate a response with an empty body (set SuppressEntityBody to true) and return "304 Not Modified" status code (HttpStatusCode.NotModified) instead of default "200 OK". A more detail explanation is much more longer.
If you don't want optimize you site for caching of HTTP GET data for jqGrids you can either use HTTP POST or don't use prmNames: { nd:null } parameter.
The code inside of JsonResult GetData(string filter, string sidx, string sord, int page, int rows) is not very short of cause. You should deserialise JSON data from the filter string and then construct the request to the data model depends on the method of the data access which you use (LINQ to SQL, Entity Model or SqlCommand with SqlDataReader). Because you have this part already implemented it has no sense to discuss this part.
Probably the main part of my suggestion is the usage of clear separation of controller actions which provide the data for all your jqGrids and the usage of MVC views with empty data (having only <table id="list"></table><div id="pager"></div>). You should also has no doubt with having a relative long code for analyzing of filters which come from the Advance Searching feature of the jqGrid and generating or the corresponding requests to your data model. Just implement it one time. In my implementation the code in also relatively complex, but it is already written one time, it works and it can be used for all new jqGrids.
I made this once, very simple.
pseudo code:
Controller
[HttpGet]
public ActionResult getList(int? id){
return PartialView("Index", new ListViewModel(id??0))
}
ViewModel
public class ListViewModel{
//ObjectAmountPerPage is the amount of object you want per page, you can modify this as //parameter so the user
//can choose the amount
public int ObjectAmountPerPage = 20 //you can make this into a variable of any sort, db/configfile/parameter
public List<YourObjectName> ObjectList;
public int CurrentPage;
public ListViewModel(id){
Currentpage = id;
using (MyDataContext db = new MyDataContext()){
ObjectList = db.YourObjectName.OrderBy(object=>object.somefield).getListFromStartIndexToEndIndex(id*ObjectAmountPerPage ,(id*ObjectAmountPerPage) +20).toList();
}
}
}
Now Create A RenderPartial:
PartialView
<#page inherit="IEnumerable<ListViewMode>">
<%foreach(YourObjectName object in Model.ObjectList){%>
Create a table with your fields
<%}%>
And create a view that implements your Jquery, other components+your partialView
View
<javascript>
$(function(){
$("#nextpage").click(function(){
(/controller/getlist/$("#nextpage").val(),function(data){$("#yourlist").html = data});
});
});
</javascript>
<div id="yourlist">
<%=Html.RenderPartial("YourPartialView", new ListViewModel())%>
</div>
<something id="nextpage" value"<%=Model.CurentPage+1%>">next page</something>
I hope this helps, this is according to the MVC- mv-mv-c principle ;)
Model-View -(modelview) - control