'How to allow AJAX endpoints in mkdocs

Visual is to get this working in mkdocs driven by a local AJAX server.

This one is hard to give an example to but I will. Before I do that, the problem is that I want to use various ajax endpoints to drive Vega visuals in MkDocs. But I run into the CORS permissions.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://machine1:8080/dataflare. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.

I have struggled to find documentation on how to solve this so, does anyone know how do we enable this in mkdocs?

The sample is long but this works until you use a local data source other than the vega data. When you change the AJAX endpoint from "url": "https://vega.github.io/vega/data/flare.json" to http://myhost:8080/shortflare (or fullflare endpoint) you get the CORS permission error above.

So should the client mkdocs enable the endpoint as safe cross site source or should bottle AJAX be sending a CORS header? I dont understand why the original AJAX works when the bottle endpoint does not work.

Now the hard part. Showing an example.

This simple bottle server will be the AJAX endpoint mimic of the original flaredata :

  • sample of data is on http://myhost:8080/shortflare

  • full dataset http://myhost:8080/fullflare is served via proxy from https://vega.github.io/vega/data/flare.json

     from bottle import route, run, template
     import requests
    
    
     DATAFLARE = '''
     [
       {
         "id": 1,
         "name": "flare"
       },
       {
         "id": 2,
         "name": "analytics",
         "parent": 1
       },
       {
         "id": 3,
         "name": "cluster",
         "parent": 2
       },
       {
         "id": 4,
         "name": "AgglomerativeCluster",
         "parent": 3,
         "size": 3938
       }
     ]
     '''
    
     @route('/shortflare')
     def getflare():
         return DATAFLARE
    
    
     @route('/fullflare')
     def proxyflare():
         return requests.get('https://vega.github.io/vega/data/flare.json').text
    
    
     if __name__ == "__main__":    
         run(host='0.0.0.0', port=8080, debug=True)
    

mkdocs setup (i.e mkdocs.yml)

site_name: VEGA

dev_addr: '0.0.0.0:2001'

theme:
    name: material
    nav_style: dark
    palette:
        accent: pink
        primary: lime




plugins:
    - search
    - charts
markdown_extensions:


  - pymdownx.superfences:
      custom_fences:
        - name: vegalite
          class: vegalite
          format: !!python/name:mkdocs_charts_plugin.fences.fence_vegalite

extra_javascript:
    - https://cdn.jsdelivr.net/npm/vega@5
    - https://cdn.jsdelivr.net/npm/vega-lite@5
    - https://cdn.jsdelivr.net/npm/vega-embed@6

ve requires

mkdocs==1.2.3
mkdocs-charts-plugin==0.0.6
mkdocs-material==7.3.6

and the markdown to make the vegalite graphic (add this in index.md or anywhere)

Relational maps.

```vegalite
{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "An example of Cartesian layouts for a node-link diagram of hierarchical data.",
  "width": 600,
  "height": 1600,
  "padding": 5,

  "signals": [
    {
      "name": "labels", "value": true,
      "bind": {"input": "checkbox"}
    },
    {
      "name": "layout", "value": "tidy",
      "bind": {"input": "radio", "options": ["tidy", "cluster"]}
    },
    {
      "name": "links", "value": "diagonal",
      "bind": {
        "input": "select",
        "options": ["line", "curve", "diagonal", "orthogonal"]
      }
    },
    {
      "name": "separation", "value": false,
      "bind": {"input": "checkbox"}
    }
  ],

  "data": [
    {
      "name": "tree",
      "url": "https://vega.github.io/vega/data/flare.json",
      "transform": [
        {
          "type": "stratify",
          "key": "id",
          "parentKey": "parent"
        },
        {
          "type": "tree",
          "method": {"signal": "layout"},
          "size": [{"signal": "height"}, {"signal": "width - 100"}],
          "separation": {"signal": "separation"},
          "as": ["y", "x", "depth", "children"]
        }
      ]
    },
    {
      "name": "links",
      "source": "tree",
      "transform": [
        { "type": "treelinks" },
        {
          "type": "linkpath",
          "orient": "horizontal",
          "shape": {"signal": "links"}
        }
      ]
    }
  ],

  "scales": [
    {
      "name": "color",
      "type": "linear",
      "range": {"scheme": "magma"},
      "domain": {"data": "tree", "field": "depth"},
      "zero": true
    }
  ],

  "marks": [
    {
      "type": "path",
      "from": {"data": "links"},
      "encode": {
        "update": {
          "path": {"field": "path"},
          "stroke": {"value": "#ccc"}
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "size": {"value": 100},
          "stroke": {"value": "#fff"}
        },
        "update": {
          "x": {"field": "x"},
          "y": {"field": "y"},
          "fill": {"scale": "color", "field": "depth"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "text": {"field": "name"},
          "fontSize": {"value": 9},
          "baseline": {"value": "middle"}
        },
        "update": {
          "x": {"field": "x"},
          "y": {"field": "y"},
          "dx": {"signal": "datum.children ? -7 : 7"},
          "align": {"signal": "datum.children ? 'right' : 'left'"},
          "opacity": {"signal": "labels ? 1 : 0"}
        }
      }
    }
  ]
}

```


Solution 1:[1]

I actually found out why. The bottle server would need to send the proper header. This is done by adding these few lines to the bottle server.

from bottle_cors_plugin import cors_plugin
from bottle import app
    
app = app()
app.install(cors_plugin('*'))

if __name__ == "__main__":
    run(app=app, host='0.0.0.0', port=8080, debug=True)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1