Skip to content

Commit 9ebcf83

Browse files
authored
Merge pull request #6 from etilite/v2
Builder v2: Build XLSX from stream
2 parents bd69732 + 287ee64 commit 9ebcf83

33 files changed

+1981
-546
lines changed

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
LOCAL_BIN=./bin
22

3+
.PHONY: up
4+
up:
5+
docker-compose --file ./build/docker-compose.yml up -d --remove-orphans
6+
7+
.PHONY: down
8+
down:
9+
docker-compose --file ./build/docker-compose.yml down
10+
311
.PHONY: dev-up
412
dev-up:
513
docker-compose --file ./build/docker-compose.yml up -d --build --remove-orphans
@@ -12,6 +20,10 @@ dev-down:
1220
run:
1321
CGO_ENABLED=0 go build -ldflags='-w -s' -o $(LOCAL_BIN)/app ./cmd/xlsx-builder/main.go && HTTP_ADDR=:8080 $(LOCAL_BIN)/app
1422

23+
.PHONY: lint
24+
lint:
25+
golangci-lint run ./...
26+
1527
.PHONY: test
1628
test:
1729
go test -v -shuffle=on -count=2 -short -cover ./...

README.md

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,76 @@
1-
# Xlsx builder
2-
[![codecov](https://codecov.io/gh/etilite/xlsx-builder/graph/badge.svg?token=PYVPKWSEP1)](https://codecov.io/gh/etilite/xlsx-builder)
1+
# xlsx-builder
2+
3+
`xlsx-builder` is a lightweight microservice built with Go, designed to generate XLSX files from HTTP JSON requests.
4+
5+
## Features
6+
7+
- Fast and efficient XLSX file generation from http stream
8+
- Easy-to-use API for creating Microsoft Excel™ spreadsheets from JSON data
9+
- Dockerized for easy deployment
310

411
## Usage
5-
Request `POST http://localhost:8080/table/`
6-
7-
Example JSON:
8-
```JSON
9-
{
10-
"header": [
11-
"date",
12-
"id",
13-
"price"
14-
],
15-
"data": [
16-
[
17-
"01.01.2023",
18-
1,
19-
10.5
20-
],
21-
[
22-
"02.01.2023",
23-
2,
24-
20.3
25-
],
26-
[
27-
"03.01.2023",
28-
3,
29-
"33"
30-
]
31-
]
32-
}
12+
### Quick Start with Docker
13+
14+
```sh
15+
docker run --rm -p 8080:8080 -e HTTP_ADDR=:8080 etilite/xlsx-builder:latest
16+
```
17+
18+
This will start the service and expose its API on port 8080.
19+
20+
### API
21+
22+
**Endpoint:**
23+
24+
- `POST /api/build`
25+
26+
**Request Body:**
27+
28+
The request body should be a JSON object with the following structure:
29+
30+
```json
31+
[
32+
{"data": [1, "a", 2.1]},
33+
{"data": [2, "some-cell-data", 2, "another-cell"]}
34+
]
3335
```
34-
Service responses with `.xlsx` file
36+
37+
**Request Example:**
38+
39+
Using `cURL`, you can make a request like this:
40+
41+
```sh
42+
curl --location 'localhost:8080/api/build' \
43+
--header 'Content-Type: application/json' \
44+
--data '[
45+
{"data": [1, "a", 2.1]},
46+
{"data": [2, "some-cell-data", 2, "another-cell"]}
47+
]' -o sheet.xlsx
48+
```
49+
50+
**Response:**
51+
52+
The response will be a binary XLSX file with the generated content.
53+
54+
### Build from source
55+
56+
```sh
57+
git clone https://github.com/your-repo/xlsx-builder.git
58+
cd xlsx-builder
59+
make run
60+
```
61+
This will build and run app at `http://localhost:8080`.
62+
63+
## License
64+
65+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
66+
67+
## Contributing
68+
69+
If you'd like to contribute to the project, please open an issue or submit a pull request on GitHub.
70+
71+
## Badges
72+
73+
[![docker pulls](https://img.shields.io/docker/pulls/etilite/xlsx-builder)](https://hub.docker.com/r/etilite/xlsx-builder)
74+
[![docker push](https://github.com/etilite/xlsx-builder/actions/workflows/docker.yml/badge.svg)](https://github.com/etilite/xlsx-builder/actions/workflows/docker.yml)
75+
[![go build](https://github.com/etilite/xlsx-builder/actions/workflows/go.yml/badge.svg)](https://github.com/etilite/xlsx-builder/actions/workflows/go.yml)
76+
[![codecov](https://codecov.io/gh/etilite/xlsx-builder/graph/badge.svg?token=PYVPKWSEP1)](https://codecov.io/gh/etilite/xlsx-builder)

build/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ARG GO_VERSION=1.21.1
1+
ARG GO_VERSION=1.22.5
22
ARG DISTROLESS_IMAGE=gcr.io/distroless/static:nonroot
33
############################
44
# STEP 1 build executable binary

build/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ version: '3.8'
22

33
services:
44
xlsx-builder:
5+
image: etilite/xlsx-builder:latest
56
build:
67
context: ./../
78
dockerfile: ./build/Dockerfile

cmd/xlsx-builder/main.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@ package main
22

33
import (
44
"context"
5+
"log/slog"
56
"os/signal"
67
"syscall"
78

8-
"github.com/etilite/xlsx-builder/internal/config"
9-
"github.com/etilite/xlsx-builder/internal/delivery/http"
9+
"github.com/etilite/xlsx-builder/internal/app"
10+
httpserver "github.com/etilite/xlsx-builder/internal/delivery/http"
1011
)
1112

1213
func main() {
1314
ctx, done := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
1415
defer done()
1516

16-
realMain(ctx)
17-
}
17+
cfg := app.NewConfigFromEnv()
1818

19-
func realMain(ctx context.Context) {
20-
cfg := config.Read()
21-
mux := http.NewRouter()
22-
srv := http.NewServer(cfg.HTTPAddr, mux)
19+
server := httpserver.NewServer(cfg.HTTPAddr)
2320

24-
srv.Run(ctx)
21+
if err := server.Run(ctx); err != nil {
22+
slog.Error("unable to start app", "error", err)
23+
panic(err)
24+
}
2525
}

go.mod

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
module github.com/etilite/xlsx-builder
22

3-
go 1.21
3+
go 1.22
44

5-
require github.com/xuri/excelize/v2 v2.8.0
5+
require (
6+
github.com/gojuno/minimock/v3 v3.3.14
7+
github.com/stretchr/testify v1.9.0
8+
github.com/xuri/excelize/v2 v2.8.1
9+
)
610

711
require (
12+
github.com/davecgh/go-spew v1.1.1 // indirect
13+
github.com/kr/pretty v0.3.1 // indirect
814
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
15+
github.com/pmezard/go-difflib v1.0.0 // indirect
916
github.com/richardlehane/mscfb v1.0.4 // indirect
1017
github.com/richardlehane/msoleps v1.0.3 // indirect
11-
github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca // indirect
12-
github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
13-
golang.org/x/crypto v0.13.0 // indirect
14-
golang.org/x/net v0.15.0 // indirect
15-
golang.org/x/text v0.13.0 // indirect
18+
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
19+
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
20+
golang.org/x/crypto v0.26.0 // indirect
21+
golang.org/x/net v0.28.0 // indirect
22+
golang.org/x/text v0.17.0 // indirect
23+
gopkg.in/yaml.v3 v3.0.1 // indirect
1624
)

go.sum

Lines changed: 30 additions & 61 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
package config
1+
package app
22

33
import "os"
44

55
type Config struct {
66
HTTPAddr string
77
}
88

9-
func Read() Config {
9+
func NewConfigFromEnv() Config {
1010
config := Config{
11-
HTTPAddr: ":8888",
11+
HTTPAddr: ":8080",
1212
}
13-
httpAddr, ok := os.LookupEnv("HTTP_ADDR")
14-
if ok {
13+
if httpAddr, ok := os.LookupEnv("HTTP_ADDR"); ok {
1514
config.HTTPAddr = httpAddr
1615
}
16+
1717
return config
1818
}

internal/app/config_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package app
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestNewConfigFromEnv(t *testing.T) {
10+
t.Run("default http address", func(t *testing.T) {
11+
cfg := NewConfigFromEnv()
12+
13+
assert.Equal(t, ":8080", cfg.HTTPAddr)
14+
})
15+
16+
t.Run("http address from env", func(t *testing.T) {
17+
t.Setenv("HTTP_ADDR", ":7777")
18+
cfg := NewConfigFromEnv()
19+
20+
assert.Equal(t, ":7777", cfg.HTTPAddr)
21+
})
22+
}

0 commit comments

Comments
 (0)