Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow formatting/callback functions to be submitted as strings #25

Closed
TaylorDale opened this issue Sep 18, 2019 · 4 comments
Closed

Allow formatting/callback functions to be submitted as strings #25

TaylorDale opened this issue Sep 18, 2019 · 4 comments

Comments

@TaylorDale
Copy link

Hi, thank you for this service.

I'd like to be able to submit functions for formatting as strings because currently this is what I have to do when generating my urls in Javascript:

  1. JSON Stringify an object that looks like this (Take note of the $FORMATFUNC$ delimiter):
    image
  2. Use a replace function to insert the function as it is not JSON compliant:
    image

Thanks for your consideration.

@typpo
Copy link
Owner

typpo commented Sep 18, 2019

Hi Taylor,

I realize it's a bit tricky to construct strings containing functions. I'll think about this problem and other potential solutions, but here are a few options to construct config strings. See options 1 and 3 in particular:

  1. Build the config as a string, not an object. This is the most straightforward way:
const chartStr = `{
  type: 'bar',
  data: {
    labels: ['January', 'February', 'March', 'April', 'May'],
    datasets: [{
      label: 'Dogs',
      data: [ 50, 60, 70, 180, 190 ]
    }]
  },
  options: {
    scales: {
      yAxes: [{
        ticks: {
          callback: function(value) {
            return '$' + value;
          }
        }
      }],
    },
  },
}`;

console.log(encodeURIComponent(chartStr));
  1. The string approach makes it more difficult to build and modify the object. Another option is to construct it in object format excluding the functions, and then substitute the functions later. This is basically what you are doing:
const chartObj = {
  type: 'bar',
  data: {
    labels: ['January', 'February', 'March', 'April', 'May'],
    datasets: [{
      label: 'Dogs',
      data: [ 50, 60, 70, 180, 190 ]
    }]
  },
  options: {
    scales: {
      yAxes: [{
        ticks: {
          callback: '{{CALLBACK_PLACEHOLDER}}',
        }
      }],
    },
  },
};

// ... modify chartObj as a normal javascript object ...

const chartStr = JSON.stringify(chartObj).replace('"{{CALLBACK_PLACEHOLDER}}"', 'function(value) { return "$" + value }');

console.log(encodeURIComponent(chartStr));
  1. Unfortunately the above is a bit ugly. You can make it all happen neatly by using a library like javascript-stringify:
const { stringify } = require('javascript-stringify');

const chartObj = {
  type: 'bar',
  data: {
    labels: ['January', 'February', 'March', 'April', 'May'],
    datasets: [{
      label: 'Dogs',
      data: [ 50, 60, 70, 180, 190 ]
    }]
  },
  options: {
    scales: {
      yAxes: [{
        ticks: {
          callback: function(value) {
            return '$' + value;
          }
        }
      }],
    },
  },
};

console.log(encodeURIComponent(stringify(chartObj)));

@TaylorDale
Copy link
Author

TaylorDale commented Sep 18, 2019

Thanks for your reply. Would it be easy-ish to parse the input as a regular JSON object and then go back, looking explicitly for where there is a callback which is currently a string and then EVAL'ing it there?

To be honest I'd call the string method the more technically correct solution in this situation but I think I'll continue using the 'hack'-ey one with the replacer because loosing the Object is not compatible with what I'm doing nor would it make it very pleasant to edit.

JS-stringify actually looks good too but it's another package too add. Not that bad in the scheme of things I guess.

@TheSnoozer
Copy link

Thank you for this awesome service...not sure if it is obvious, but when creating short URLs (via POST to https://quickchart.io/chart/create) the service seems to require a valid JSON and thus short URLs currently would lack the ability to define call-back functions.

E.g. posting a RFC 4627 valid Json results in an Image telling that function(value, index, values){return "foo bar";} is not a valid function (yes it's a string).

curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{"chart": {"type": "bar", "data": {"labels": ["Hello", "World"], "datasets": [{"label": "Foo", "data": [1, 2]}]}, "options": {"scales": {"yAxes": [{"type": "linear", "ticks": {"beginAtZero": true, "callback": "function(value, index, values){return \"foo bar\";}" } }] } }   }}' \
     https://quickchart.io/chart/create

When attempting to post it as actual function (invalid Json as per RFC 4627) one gets a 500 Internal Server Error

curl -X POST \
     -H 'Content-Type: application/json' \
     -d '{"chart": {"type": "bar", "data": {"labels": ["Hello", "World"], "datasets": [{"label": "Foo", "data": [1, 2]}]}, "options": {"scales": {"yAxes": [{"type": "linear", "ticks": {"beginAtZero": true, "callback": function(value, index, values){return "foo bar";} } }] } }   }}' \
     https://quickchart.io/chart/create

Not sure what would the best option for this.

@typpo
Copy link
Owner

typpo commented Nov 25, 2019

Hi @TheSnoozer,

You can POST chart definitions (including shorturl creation) that include Javascript functions by sending a JSON object while serializing the "chart" param as a string.

{
  "chart": "{type: \"bar\", ..., function(x) { return y }",
  "myOtherOption": "xyz"
}

The key is that the POSTed object is valid JSON, and the type of the "chart" value is string. Note that you'll have to escape the chart config accordingly.

I'm closing this issue because the original question is really a matter of developer preference as to how one might serialize their chart config as a string. I've included a few recommendations above, but ultimately string templating etc is beyond the scope of the service and could make things more confusing. In the end, if the chart is not JSON, the definition has to come through as a string and then Quickchart will attempt to safely eval it.

@typpo typpo closed this as completed Nov 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants