diff --git a/README.md b/README.md
index dce5863..b939d07 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,8 @@
- [Bars](#bars)
- [`GET /donut`](#get-donut)
- [Donuts](#donuts)
+ - [`GET /pie`](#get-pie)
+ - [Donuts](#donuts-1)
## [`GET /line`](https://instachart.coveritup.app/line?title=Single+Line+series&x_label=dates&y_label=amount&data={%20%22x%22:%20[[%222022-12-23%22,%222022-12-24%22,%222023-12-25%22]],%20%22y%22:%20[[1,2,3]]%20})
@@ -187,3 +189,40 @@ https://instachart.coveritup.app/donut?title=Donut+Chart&data={
![Donut chart](https://instachart.coveritup.app/donut?title=Donut+Chart&data={%20"names":%20["Monday",%20"Friday",%20"Saturday",%20"Sunday"],%20"values":%20[4,%206%20,7,%209]%20})
+
+
+## [`GET /pie`](https://instachart.coveritup.app/pie?title=Pie+Chart&data={%20"names":%20["Monday",%20"Friday",%20"Saturday",%20"Sunday"],%20"values":%20[4,%206%20,7,%209]%20})
+
+
+| Query | Required | Description | Example |
+| :------- | :------- | :---------- | :-------------------------------------------------------- |
+| `data` | ◯ | JSON | `?data={ "names": ["Mon","Tue","Wed"],"values": [1,2,3]}` |
+| `title` | | string | |
+| `height` | | int | |
+| `width` | | int | |
+
+
+| `data` | Required | Description | Example |
+| :------- | :------- | :------------- | :-------------------------- |
+| `names` | ◯ | Array (string) | `"x": ["Mon","Tue", "Wed"]` |
+| `values` | ◯ | Array (int) | `"y": [1,2,3]` |
+
+
+### Donuts
+
+
+ REQUEST URL
+
+```sh
+https://instachart.coveritup.app/pie?title=Pie+Chart&data={
+ "names": ["Monday", "Friday", "Saturday", "Sunday"],
+ "values": [4, 6 ,7, 9]
+}
+```
+
+
+
+
+
+![Donut chart](https://instachart.coveritup.app/pie?title=Pie+Chart&data={%20"names":%20["Monday",%20"Friday",%20"Saturday",%20"Sunday"],%20"values":%20[4,%206%20,7,%209]%20})
+
diff --git a/pkg/pie_chart.go b/pkg/pie_chart.go
new file mode 100644
index 0000000..743e56f
--- /dev/null
+++ b/pkg/pie_chart.go
@@ -0,0 +1,23 @@
+package pkg
+
+import "github.com/wcharczuk/go-chart/v2"
+
+type PieChart struct {
+ chart *Chart
+}
+
+func NewPieChart() *PieChart {
+ return &PieChart{
+ chart: NewChart(),
+ }
+}
+func (c *PieChart) GetValues(names []string, values []float64) []chart.Value {
+ var chartValues []chart.Value
+ for i := 0; i < len(names); i++ {
+ chartValues = append(chartValues, chart.Value{
+ Value: values[i],
+ Label: names[i],
+ })
+ }
+ return chartValues
+}
diff --git a/pkg/pie_chart_handler.go b/pkg/pie_chart_handler.go
new file mode 100644
index 0000000..7a85eda
--- /dev/null
+++ b/pkg/pie_chart_handler.go
@@ -0,0 +1,62 @@
+package pkg
+
+import (
+ "bytes"
+ "encoding/json"
+ "net/http"
+
+ "github.com/labstack/echo/v4"
+ "github.com/wcharczuk/go-chart/v2"
+)
+
+type PieChartHandler struct {
+ chart *PieChart
+}
+
+func NewPieChartHandler() *PieChartHandler {
+ return &PieChartHandler{
+ chart: NewPieChart(),
+ }
+}
+
+type PieChartRequest struct {
+ ChartData string `json:"data" query:"data" form:"data" validate:"required" message:"data is required"`
+ ChartTitle string `json:"title" query:"title" form:"title"`
+ Height int `json:"height" query:"height" form:"height"`
+ Width int `json:"width" query:"width" form:"width"`
+}
+
+type PieChartData struct {
+ Names []string `json:"names"`
+ Values []float64 `json:"values"`
+}
+
+func (h *PieChartHandler) Get(c echo.Context) ([]byte, error) {
+ req := new(PieChartRequest)
+ if err := BindRequest(c, req); err != nil {
+ return nil, echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
+ }
+
+ var data PieChartData
+ if err := json.Unmarshal([]byte(req.ChartData), &data); err != nil {
+ return nil, echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
+ }
+
+ if len(data.Values) == 0 || len(data.Values) != len(data.Names) {
+ return nil, echo.NewHTTPError(http.StatusUnprocessableEntity, "data provided is invalid")
+ }
+
+ graph := chart.PieChart{
+ Title: req.ChartTitle,
+ Height: req.Height,
+ Width: req.Width,
+ Values: h.chart.GetValues(data.Names, data.Values),
+ }
+
+ buffer := bytes.NewBuffer([]byte{})
+ err := graph.Render(chart.PNG, buffer)
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
+ }
+ return buffer.Bytes(), err
+}
diff --git a/pkg/pie_chart_handler_test.go b/pkg/pie_chart_handler_test.go
new file mode 100644
index 0000000..f2c8369
--- /dev/null
+++ b/pkg/pie_chart_handler_test.go
@@ -0,0 +1,53 @@
+package pkg
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "testing"
+
+ "github.com/labstack/echo/v4"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetPieChart(t *testing.T) {
+ e := echo.New()
+
+ e.GET("/pie", func(c echo.Context) error {
+ img, err := NewPieChartHandler().Get(c)
+ if err != nil {
+ return err
+ }
+ return c.Blob(http.StatusOK, "image/png", img)
+ })
+
+ // Start a test HTTP server
+ server := httptest.NewServer(e)
+ defer server.Close()
+
+ type TestCase struct {
+ QueryParams string
+ ExpectedStatus int
+ }
+ testCases := []TestCase{
+ {
+ QueryParams: `{"names": ["Monday","Tuesday","Wednesday"], "values": [1,2,3]}`,
+ ExpectedStatus: http.StatusOK,
+ },
+ {
+ QueryParams: `{"names": ["Monday,"Tuesday","Wednesday"], "values": [1,2,3]}`,
+ ExpectedStatus: http.StatusUnprocessableEntity,
+ },
+ {
+ QueryParams: `{"names": [["Monday","Tuesday","Wednesday"]], "values": [1,2,3]}`,
+ ExpectedStatus: http.StatusUnprocessableEntity,
+ },
+ }
+
+ for _, tc := range testCases {
+ url := server.URL + "/pie?data=" + url.QueryEscape(tc.QueryParams)
+ resp, err := http.Get(url)
+ assert.NoError(t, err)
+ assert.Equal(t, tc.ExpectedStatus, resp.StatusCode)
+ }
+}
diff --git a/pkg/routes.go b/pkg/routes.go
index bb02688..d50e827 100644
--- a/pkg/routes.go
+++ b/pkg/routes.go
@@ -24,6 +24,14 @@ func SetupRoutes(e *echo.Echo) {
setHeaders(c)
return c.Blob(http.StatusOK, "image/png", img)
})
+ e.GET("/bar", func(c echo.Context) error {
+ img, err := NewBarChartHandler().Get(c)
+ if err != nil {
+ return err
+ }
+ setHeaders(c)
+ return c.Blob(http.StatusOK, "image/png", img)
+ })
e.GET("/donut", func(c echo.Context) error {
img, err := NewDonutChartHandler().Get(c)
if err != nil {
@@ -32,8 +40,8 @@ func SetupRoutes(e *echo.Echo) {
setHeaders(c)
return c.Blob(http.StatusOK, "image/png", img)
})
- e.GET("/bar", func(c echo.Context) error {
- img, err := NewBarChartHandler().Get(c)
+ e.GET("/pie", func(c echo.Context) error {
+ img, err := NewPieChartHandler().Get(c)
if err != nil {
return err
}