AjaxDataSource

Hi all,

I’m am working on a dashboard to show live data from a detector from our Tango REST server (running in a container) using a static page and a AjaxDataSource.
I can get the JSON Data from the REST server when querying directly via
curl -u "<user>:<password>" http://localhost:10001/tango/rest/rc4/hosts/localhost/10000/devices/experiment/detector/1/attributes/spectrum/value
which returns the detector spectrum in the form of {“name”:“spectrum”, “values”:[0,1,5,…,1,7],“quality”:“ATTR_VALID”,“timestamp”:1578400816350}

I want to show the array from “values” as a spectrum, therefore the adapter in the bokeh code.

When I open the generated HTML, I get

Failed to fetch JSON from http://localhost:10001/tango/rest/rc4/hosts/localhost/10000/devices/experiment/detector/1/attributes/spectrum/value with code 0

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:10001/tango/rest/rc4/hosts/localhost/10000/devices/experiment/detector/1/attributes/spectrum/value.

Since I have absolutely no knowledge about Web Development, I am not sure, if this is a problem with the tango server or the bokeh code.
Some searching for the CORS error message seems to indicate, that it might be related to the server and not the bokeh code, but I can get the data in a Waltz server, which is running in a different Docker container and different Port than the Tango server, so there must be external origin access.

So I was wondering, if this might be related to missing authentication in bokeh (which is definitely missing at the moment) or if this error occurs already before authentication? And how can I pass the user/password combination via an AjaxDataSource?

I am not really sure, if this is the correct forum, but as I said, I have no experience with this stuff, so any help is appreciated

My bokeh code is

from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource,  AjaxDataSource, CustomJS


output_file("tango_ajax.html")


adapter = CustomJS(code="""
    const result = {x: [], y: []}
    const pts = cb_data.response.value
    for (i=0; i<pts.length; i++) {
        result.x.push(i)
        result.y.push(pts[i])
    }
    return result
""")


source =  AjaxDataSource(data_url='http://localhost:10001/tango/rest/rc4/hosts/localhost/10000/devices/experiment/detector/1/attributes/spectrum/value',
                        polling_interval=100, adapter = adapter, mode='replace')
source.data = {"x": np.arange(1024), "y":np.ones(1024)}
p = figure()
p.circle(x='x', y='y', source=source)
show(p)

After some trial and error, I found, that my problem was/is indeed the missing authentication in Bokeh.
I was able to get the data via a AjaxDataSource when adding
http_headers = {"Authorization": "Basic <key>"}
to the arguments. I got the key by monitoring the network traffic when directly calling the rest endpoint in the browser but this is no permanent solution.
Is there any way, to pass the username/password to the datasource?

The only facility AjaxDataSource has is the ability to add user-specified headers to the request it makes. How that interacts with any auth scheme at the data URL is really up to the the service that provides the data. If the service accepts headers with user/pass for authentication, then I guess you could do that, though AFAIK sending clear-text user/pass in HTTP headers is generally regarded as Not Good.

Better would be to send some kind of bearer auth token, similar to what you are doing now, but ideally one that has an expiry, and is sent over HTTPS. So then you need the Bokeh app code to be able to generate/obtain an access token (which you have already noted, implicitly). I’m afraid I don’t really have any concrete suggestions here. Bokeh app code could potentially make requests to “log in” or possibly an HTML page template that embed the app could, and could communicate the token. It’s also possible the recent “auth hooks” added in Bokeh 1.4 could be used for this somehow.