REST API LimitのPostman Visualizerを解析します


自分でもVisualizerを使ってみて初めて気づきました。Limitにも可視化が設定してあるなぁ。
なかなか素晴らしい機能が追加されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/b806f3df-3847-1bbd-cead-2cb5d910cf66.png)


ここにあるchart.jsを使っていますね。バージョンを4.0.1に上げると機能しないです。3.9.0は機能するので3系の書き方なのかもしれません。

https://cdn.jsdelivr.net/npm/chart.js@3.5.1/


pm.getData((err, chartData)はテンプレート内のデータへのアクセスするメソッド


```JavaScript
// Visualizer that renders a Chart.js graph with the Salesforce API limits consumption
const template = 
`<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
<canvas id="myChart" width="400" height="800"></canvas>
<script>
pm.getData((err, chartData) => {
    const getTooltipFooter = (tooltipItems) => {
        const index = tooltipItems[0].dataIndex;
        return 'Consumed '+ chartData.consumed[index] +' out of '+ chartData.max[index];
    };

    const ctx = document.getElementById('myChart').getContext('2d');
    const myChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: chartData.labels,
            datasets: [
                {
                    label: '% Consumed',
                    data: chartData.consumedPercent,
                    backgroundColor: 'rgba(255, 99, 132, 1)',
                    borderColor: 'rgba(255, 99, 132, 1)',
                    borderWidth: 1
                }
            ]
        },
        options: {
            //Y軸に対してX軸が変動量となるグラフを描画する場合 'y' を指定。
            indexAxis: 'y',
            //コンテナがリサイズされた場合にチャートもリサイズするか否か。デフォルトは true。
            responsive: true,
            scales: {
                x: {
                    beginAtZero: true,
                    //目盛り設定。
                    ticks: {
                        callback: (value, index, values) => value + '%'
                    },
                    max: 100
                }
            },
            //インタラクション:ポイントにマウスを乗せたときに表示するツールチップに関する設定を行う。
            interaction: {
                //true はマウスがポイント上にある時のみ、false は常に表示。デフォルトは true。
                intersect: false,
                //インタラクションモード。ツールチップのどのデータを表示するか、
                //'point'(対象ポイントのみ), 'nearest'(一番近いポイント), 'index'(各データセットの該当インデックスの値), 
                //'dataset'(該当データセットの値), 'x'(同じX軸上の値), 'y'(同じY軸上の値) のいずれかで指定。
                mode: 'y'
            },
            plugins: {
                tooltip: {
                    callbacks: {
                        footer: getTooltipFooter,
                        label: (tooltipItems) => tooltipItems.raw + '%'
                    }
                },
                title: {
                    display: true,
                    text: 'API Limits Consumption'
                },
                //凡例
                legend: {
                    display: false
                }
            }
        }
    });
});
</script>
`;

// Format API response into chart data
const responseJson = pm.response.json();
const chartData = {
    labels: [],
    consumed: [],
    consumedPercent: [],
    max: []
};
Object.keys(responseJson).forEach((limitName) => {
    const { Max, Remaining } = responseJson[limitName];
    const consumed = Max - Remaining;
    let percent = 0;
    if (Max !== 0) {
        percent = (consumed / Max) * 100;
        percent = percent
            .toString()
            .match(/^-?\d+(?:\.\d{0,2})?/)[0];
    }
    //pushメソッドを使って配列の最後に要素を追加する
    chartData.labels.push(limitName);
    chartData.consumed.push(consumed);
    chartData.consumedPercent.push(percent);
    chartData.max.push(Max);
});
// Render vizualizer
pm.visualizer.set(template, chartData);
```


レスポンスとしては以下のようなJsonが返ってきます。

```Json
{
    "AnalyticsExternalDataSizeMB": {
        "Max": 40960,
        "Remaining": 40960
    },
    "ConcurrentAsyncGetReportInstances": {
        "Max": 200,
        "Remaining": 200
    },
    "ConcurrentEinsteinDataInsightsStoryCreation": {
        "Max": 5,
        "Remaining": 5
    },
    "ConcurrentEinsteinDiscoveryStoryCreation": {
        "Max": 2,
        "Remaining": 2
    },
    "ConcurrentSyncReportRuns": {
        "Max": 20,
        "Remaining": 20
    },
    "DailyAnalyticsDataflowJobExecutions": {
        "Max": 60,
        "Remaining": 60
    },
    "DailyAnalyticsUploadedFilesSizeMB": {
        "Max": 51200,
        "Remaining": 51200
    },
    "DailyApiRequests": {
        "Max": 15000,
        "Remaining": 14959
    },
    "DailyAsyncApexExecutions": {
        "Max": 250000,
        "Remaining": 250000
    },
    "DailyAsyncApexTests": {
        "Max": 500,
        "Remaining": 500
    },
    "DailyBulkApiBatches": {
        "Max": 15000,
        "Remaining": 15000
    },
    "DailyBulkV2QueryFileStorageMB": {
        "Max": 976562,
        "Remaining": 976562
    },
    "DailyBulkV2QueryJobs": {
        "Max": 10000,
        "Remaining": 10000
    },
    "DailyDeliveredPlatformEvents": {
        "Max": 10000,
        "Remaining": 10000
    },
    "DailyDurableGenericStreamingApiEvents": {
        "Max": 10000,
        "Remaining": 10000
    },
    "DailyDurableStreamingApiEvents": {
        "Max": 10000,
        "Remaining": 10000
    },
    "DailyEinsteinDataInsightsStoryCreation": {
        "Max": 1000,
        "Remaining": 1000
    },
    "DailyEinsteinDiscoveryOptimizationJobRuns": {
        "Max": 25,
        "Remaining": 25
    },
    "DailyEinsteinDiscoveryPredictAPICalls": {
        "Max": 50000,
        "Remaining": 50000
    },
    "DailyEinsteinDiscoveryPredictionsByCDC": {
        "Max": 500000,
        "Remaining": 500000
    },
    "DailyEinsteinDiscoveryStoryCreation": {
        "Max": 100,
        "Remaining": 100
    },
    "DailyFieldServiceAppointmentOptimization": {
        "Max": 50000,
        "Remaining": 50000
    },
    "DailyFunctionsApiCallLimit": {
        "Max": 50000,
        "Remaining": 50000
    },
    "DailyGenericStreamingApiEvents": {
        "Max": 10000,
        "Remaining": 10000
    },
    "DailyStandardVolumePlatformEvents": {
        "Max": 10000,
        "Remaining": 10000
    },
    "DailyStreamingApiEvents": {
        "Max": 10000,
        "Remaining": 10000
    },
    "DailyWorkflowEmails": {
        "Max": 435,
        "Remaining": 435
    },
    "DataStorageMB": {
        "Max": 5,
        "Remaining": 5
    },
    "DurableStreamingApiConcurrentClients": {
        "Max": 20,
        "Remaining": 20
    },
    "FileStorageMB": {
        "Max": 20,
        "Remaining": 20
    },
    "HourlyAsyncReportRuns": {
        "Max": 1200,
        "Remaining": 1200
    },
    "HourlyDashboardRefreshes": {
        "Max": 200,
        "Remaining": 200
    },
    "HourlyDashboardResults": {
        "Max": 5000,
        "Remaining": 5000
    },
    "HourlyDashboardStatuses": {
        "Max": 999999999,
        "Remaining": 999999999
    },
    "HourlyLongTermIdMapping": {
        "Max": 100000,
        "Remaining": 100000
    },
    "HourlyManagedContentPublicRequests": {
        "Max": 50000,
        "Remaining": 50000
    },
    "HourlyODataCallout": {
        "Max": 1000,
        "Remaining": 1000
    },
    "HourlyPublishedPlatformEvents": {
        "Max": 50000,
        "Remaining": 50000
    },
    "HourlyPublishedStandardVolumePlatformEvents": {
        "Max": 1000,
        "Remaining": 1000
    },
    "HourlyShortTermIdMapping": {
        "Max": 100000,
        "Remaining": 100000
    },
    "HourlySyncReportRuns": {
        "Max": 500,
        "Remaining": 500
    },
    "HourlyTimeBasedWorkflow": {
        "Max": 1000,
        "Remaining": 1000
    },
    "MassEmail": {
        "Max": 10,
        "Remaining": 10
    },
    "MonthlyEinsteinDiscoveryStoryCreation": {
        "Max": 500,
        "Remaining": 500
    },
    "Package2VersionCreates": {
        "Max": 10,
        "Remaining": 10
    },
    "Package2VersionCreatesWithoutValidation": {
        "Max": 500,
        "Remaining": 500
    },
    "PermissionSets": {
        "Max": 1500,
        "Remaining": 1493,
        "CreateCustom": {
            "Max": 1000,
            "Remaining": 993
        }
    },
    "PrivateConnectOutboundCalloutHourlyLimitMB": {
        "Max": 0,
        "Remaining": 0
    },
    "PublishCallbackUsageInApex": {
        "Max": 5242880,
        "Remaining": 5242880
    },
    "SingleEmail": {
        "Max": 15,
        "Remaining": 15
    },
    "StreamingApiConcurrentClients": {
        "Max": 20,
        "Remaining": 20
    }
}
```

# Chart.jsの部分

基本の形は以下のようです。

```JavaScript
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
　　 <title>グラフ</title> 
</head>
  <body>
　　　　  <h1>グラフ</h1>
    <canvas id="myChart"></canvas>
　　　　　　　　<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script>
    <script>
    var ctx = document.getElementById("myChart");
    var myChart = new Chart(ctx, {
      type: タイプ,
      data: データ,
      options: オプション
    });
    </script>
    
  </body>
</html>
```

 var ctx = document.getElementById("myChart").getContext('2d'); は２つのデータがあるということみたい。

```
 <canvas id="myChart"></canvas>も必須みたいです。
 ```

 ```JavaScript
 <canvas id="myChart"></canvas>
 <script>
  var ctx = document.getElementById("myChart").getContext('2d');
  var myBarChart_01 = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: ["A", "B", "C", "D", "E"],
      datasets: [{
        data: [60, 40, 10, 90, 50, 30],
      },{
        data: [60, 40, 10, 20, 30, 30],
      }]
    },
  });
</script>
```

+ [Chart.jsでグラフを描画してみた](https://qiita.com/Haruka-Ogawa/items/59facd24f2a8bdb6d369)

# chart.js@4.0.1だとうごきませんねぇ。

どこが問題なのかさっぱり理解できないので、とりあえず最小の設定で試したいと思います。
Option以下をコメントアウトしてみます。chart.js@3.5.1では単なる棒グラフになったようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/9f27e0e3-8ed5-0021-147e-a69a484cde32.png)


chart.js@4.0.1では動きません。ええええ、最初からアカンとは...

datasetsの中身をコメントアウトしてもchart.js@3.5.1では枠が表示できる。
chart.js@4.0.1ではこれすら機能しない。ああああ、なんだろう。


```JavaScript
const myChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: chartData.labels,
            datasets: [
                {
        //            label: '% Consumed'
        //            data: chartData.consumedPercent,
        //            backgroundColor: 'rgba(255, 99, 132, 1)',
        //            borderColor: 'rgba(255, 99, 132, 1)',
        //            borderWidth: 1
               }
            ]
        }
```

Chart.js 4.0 では、多数の重大な変更が導入されていらしい。そりゃぁ分からんわ。

https://www.chartjs.org/docs/latest/migration/v4-migration.html

4系のサンプルを見つけました。

+ [chart.js 4.2で時系列のグラフを書く（日付を扱うアダプタを指定必要）](https://blog.cybercube.co.jp/chart-js-4-2-time-%E6%99%82%E7%B3%BB%E5%88%97%E3%81%AE%E3%82%B0%E3%83%A9%E3%83%95%E3%82%92%E6%9B%B8%E3%81%8F%EF%BC%88%E6%97%A5%E4%BB%98%E3%82%92%E6%89%B1%E3%81%86%E3%82%A2%E3%83%80%E3%83%97#google_vignette)

ローカルでは機能しますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/837a921e-9d01-f4f4-34ba-d7eadd0e8c66.png)


組み込んでも動かない。分からん

```JavaScript
// Visualizer that renders a Chart.js graph with the Salesforce API limits consumption
const template = 
`
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.2.0/dist/chart.udm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script><!-- アダプタ -->

<canvas id="myChart" width="400" height="800"></canvas>
<script>
pm.getData((err, chartData) => {
    const ctx = document.getElementById('myChart');
    const myChart = new Chart(ctx, {
        type: "line",
        data: {
          datasets: [{
            label: "値1",
            fill: false,
            borderColor: 'rgba(0, 0, 255, 0.5)',
            lineTension: 0.1,   //曲線にする度合
            data: values,
          }],
        },
        options: {
          spanGaps: true,   //点をつなげる場合
          scales: {
            x: {
              type: 'time',
              time:{
                unit: 'week',    //週に線
                displayFormats: {
                    week: 'yyyy-MM-dd'
                }
              },
 
            }
          }
        }
    });
});
</script>
`;

 var chartData =[
    {x:"2020-02-01",y:100},
    {x:"2020-02-02",y:200},
    {x:"2020-02-03",y:300},
    {x:"2020-02-04",y:400},
    {x:"2020-02-07",y:200},
    {x:"2020-02-14",y:600}
 ];
// Render vizualizer
pm.visualizer.set(template, chartData);
```