Skip to content

Commit a8c411a

Browse files
authored
feat: Collect statistics and serve over API (#13)
* Add back linter on push * Add bbolt to project * Make it work with the new structure * Add new helper for ip * Add middleware for collecting stats * Add function to collect download asked * doc: update documentation * Add skeleton for API * Review structure a little bit * Add a proto for CORS custom middleware * Add visit statistic * Add unique switch stat * Add details per switch * Add ip for unkown switch * Trigger stats for download game only on existing game * Add stats about downloads * Begin to add test for StatsMiddleware * Add tests for StatsMiddleware * doc: Add more info * Add new github Action for testing * Add tests for API * Move bytes functions to utils * Add more tests * Fix ginkgo workflow * Another fix for ginkgo * Install dependencies in CI * Fix ginkgo workflow * Fix randomize tests * New badges * Another fix for GithubAction * doc: Cleaner readme * doc: More spaces
1 parent 6a7ffe0 commit a8c411a

26 files changed

+957
-26
lines changed

.github/workflows/ginkgo.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: test
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- name: Checkout
10+
uses: actions/checkout@v2
11+
with:
12+
fetch-depth: 0
13+
-
14+
id: vars
15+
run: |
16+
echo ::set-output name=go_version::$(cat go.mod | head -3 | tail -1 | cut -d ' ' -f 2)
17+
echo "Using Go version ${{ steps.vars.outputs.go_version }}"
18+
- name: Set up Go
19+
uses: actions/setup-go@v2
20+
with:
21+
go-version: ${{ steps.vars.outputs.go_version }}
22+
- run: go mod tidy && git diff --exit-code go.mod go.sum
23+
- name: Install ginkgo
24+
run: |
25+
go get github.com/onsi/ginkgo/v2/ginkgo
26+
go get github.com/onsi/gomega/...
27+
- name: Print out Ginkgo version
28+
run: ginkgo version
29+
- run: ginkgo -r --randomize-all --randomize-suites --race --trace

.github/workflows/golangci-lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: golangci-lint
22
on:
3-
# push:
3+
push:
44
pull_request:
55
permissions:
66
contents: read

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@
2020
# TinShop specific
2121
config.yaml
2222
titles.US.en.json
23-
/dist
23+
/dist
24+
stats.db
25+
coverprofile.out

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"cSpell.words": [
33
"asciicheck",
4+
"bbolt",
45
"bodyclose",
56
"dblk",
67
"deadcode",
@@ -34,6 +35,7 @@
3435
"ineffassign",
3536
"Infof",
3637
"interfacer",
38+
"itob",
3739
"logrus",
3840
"logutils",
3941
"mitchellh",

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
<p align="center">
2-
<p align="center">
3-
<img alt="TinShop" src="./logo.png" width="50%">
4-
</p>
5-
<p align="center">
6-
Your own personal shop right into tinfoil!
7-
</p>
8-
9-
[![golangci-lint](https://github.com/DblK/tinshop/actions/workflows/golangci-lint.yml/badge.svg?event=release)](https://github.com/DblK/tinshop/actions/workflows/golangci-lint.yml)
1+
<div align="center">
2+
<img alt="TinShop" src="./logo.png" width="50%"><br><br>
3+
Your own personal shop right into tinfoil!<br><br>
4+
5+
[![golangci-lint](https://github.com/DblK/tinshop/actions/workflows/golangci-lint.yml/badge.svg?branch=master&event=release)](https://github.com/DblK/tinshop/actions/workflows/golangci-lint.yml)
6+
[![test](https://github.com/DblK/tinshop/actions/workflows/ginkgo.yml/badge.svg?branch=master&event=release)](https://github.com/DblK/tinshop/actions/workflows/ginkgo.yml)
107
[![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/DblK/tinshop.svg)](https://github.com/DblK/tinshop)
118
[![GoDoc reference example](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/DblK/tinshop)
129
[![GoReportCard](https://goreportcard.com/badge/github.com/DblK/tinshop)](https://goreportcard.com/report/github.com/DblK/tinshop)
1310
[![GitHub release](https://img.shields.io/github/release/DblK/tinshop.svg)](https://GitHub.com/DblK/tinshop/releases/)
1411
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
12+
</div>
1513

1614
# Disclaimer
1715

@@ -48,7 +46,9 @@ Here is the list of all main features so far:
4846
- [X] You can specify custom titledb to be merged with official one
4947
- [X] Auto-watch for mounted directories
5048
- [X] Add filters path for shop
51-
- [X] Simple ticket check in NSP/NSZ
49+
- [X] Simple ticket check in NSP/NSZ (based on titledb file)
50+
- [X] Collect basic statistics
51+
- [X] An API to query information about your shop
5252

5353
## Filtering
5454

@@ -79,7 +79,8 @@ If you change an interface (or add a new one), do not forget to execute `./updat
7979

8080
## What to launch tests?
8181

82-
You can run `ginkgo -r` for one shot or `ginkgo watch -r` during development.
82+
You can run `ginkgo -r` for one shot or `ginkgo watch -r` during development.
83+
Note: you can add `-cover` to have an idea of the code coverage.
8384
# Roadmap
8485

8586
You can see the [roadmap here](https://github.com/DblK/tinshop/projects/1).

api/api.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
"log"
6+
"net/http"
7+
8+
"github.com/DblK/tinshop/repository"
9+
)
10+
11+
type endpoint struct {
12+
}
13+
14+
// New returns a new api
15+
func New() repository.API {
16+
return &endpoint{}
17+
}
18+
19+
func (e *endpoint) Stats(w http.ResponseWriter, stats repository.StatsSummary) {
20+
jsonResponse, jsonError := json.Marshal(stats)
21+
22+
if jsonError != nil {
23+
log.Println("[API] Unable to encode JSON")
24+
w.WriteHeader(http.StatusInternalServerError)
25+
return
26+
}
27+
28+
w.WriteHeader(http.StatusOK)
29+
_, _ = w.Write(jsonResponse)
30+
}

api/api_suite_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package api_test
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestApi(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "Api Suite")
13+
}

api/api_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package api_test
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
10+
"github.com/DblK/tinshop/api"
11+
"github.com/DblK/tinshop/repository"
12+
)
13+
14+
var _ = Describe("Api", func() {
15+
var (
16+
myAPI repository.API
17+
writer *httptest.ResponseRecorder
18+
)
19+
BeforeEach(func() {
20+
myAPI = api.New()
21+
})
22+
Describe("Stats", func() {
23+
It("Test with empty stats", func() {
24+
emptyStats := &repository.StatsSummary{}
25+
writer = httptest.NewRecorder()
26+
27+
myAPI.Stats(writer, *emptyStats)
28+
Expect(writer.Code).To(Equal(http.StatusOK))
29+
Expect(writer.Body.String()).To(Equal("{}"))
30+
})
31+
It("Test with some stats", func() {
32+
emptyStats := &repository.StatsSummary{
33+
Visit: 42,
34+
}
35+
writer = httptest.NewRecorder()
36+
37+
myAPI.Stats(writer, *emptyStats)
38+
Expect(writer.Code).To(Equal(http.StatusOK))
39+
Expect(writer.Body.String()).To(Equal("{\"visit\":42}"))
40+
})
41+
})
42+
})

config/config_test.go

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ var _ = Describe("Config", func() {
134134
})
135135
})
136136
Context("Security for Blacklist/Whitelist tests", func() {
137-
var myConfig = config.File{}
137+
var myConfig config.File
138+
139+
BeforeEach(func() {
140+
myConfig = config.File{}
141+
})
138142

139143
Describe("Blacklist tests", func() { //nolint:dupl
140144
It("With empty blacklist", func() {
@@ -206,7 +210,12 @@ var _ = Describe("Config", func() {
206210
})
207211
})
208212
Context("Security for theme", func() {
209-
var myConfig = config.File{}
213+
var myConfig config.File
214+
215+
BeforeEach(func() {
216+
myConfig = config.File{}
217+
})
218+
210219
Describe("IsBannedTheme", func() {
211220
It("should not be banned if empty config", func() {
212221
Expect(myConfig.IsBannedTheme("myTheme")).To(BeFalse())
@@ -226,7 +235,12 @@ var _ = Describe("Config", func() {
226235
})
227236
})
228237
Describe("Protocol", func() {
229-
var myConfig = config.File{}
238+
var myConfig config.File
239+
240+
BeforeEach(func() {
241+
myConfig = config.File{}
242+
})
243+
230244
It("Test with empty object", func() {
231245
Expect(myConfig.Protocol()).To(BeEmpty())
232246
})
@@ -236,7 +250,12 @@ var _ = Describe("Config", func() {
236250
})
237251
})
238252
Describe("Host", func() {
239-
var myConfig = config.File{}
253+
var myConfig config.File
254+
255+
BeforeEach(func() {
256+
myConfig = config.File{}
257+
})
258+
240259
It("Test with empty object", func() {
241260
Expect(myConfig.Host()).To(BeEmpty())
242261
})
@@ -246,7 +265,12 @@ var _ = Describe("Config", func() {
246265
})
247266
})
248267
Describe("Port", func() {
249-
var myConfig = config.File{}
268+
var myConfig config.File
269+
270+
BeforeEach(func() {
271+
myConfig = config.File{}
272+
})
273+
250274
It("Test with empty object", func() {
251275
Expect(myConfig.Port()).To(Equal(0))
252276
})
@@ -256,7 +280,12 @@ var _ = Describe("Config", func() {
256280
})
257281
})
258282
Describe("ShopTitle", func() {
259-
var myConfig = config.File{}
283+
var myConfig config.File
284+
285+
BeforeEach(func() {
286+
myConfig = config.File{}
287+
})
288+
260289
It("Test with empty object", func() {
261290
Expect(myConfig.ShopTitle()).To(BeEmpty())
262291
})
@@ -266,7 +295,12 @@ var _ = Describe("Config", func() {
266295
})
267296
})
268297
Describe("DebugNfs", func() {
269-
var myConfig = config.File{}
298+
var myConfig config.File
299+
300+
BeforeEach(func() {
301+
myConfig = config.File{}
302+
})
303+
270304
It("Test with empty object", func() {
271305
Expect(myConfig.DebugNfs()).To(BeFalse())
272306
})
@@ -276,7 +310,12 @@ var _ = Describe("Config", func() {
276310
})
277311
})
278312
Describe("VerifyNSP", func() {
279-
var myConfig = config.File{}
313+
var myConfig config.File
314+
315+
BeforeEach(func() {
316+
myConfig = config.File{}
317+
})
318+
280319
It("Test with empty object", func() {
281320
Expect(myConfig.VerifyNSP()).To(BeFalse())
282321
})
@@ -286,7 +325,12 @@ var _ = Describe("Config", func() {
286325
})
287326
})
288327
Describe("DebugNoSecurity", func() {
289-
var myConfig = config.File{}
328+
var myConfig config.File
329+
330+
BeforeEach(func() {
331+
myConfig = config.File{}
332+
})
333+
290334
It("Test with empty object", func() {
291335
Expect(myConfig.DebugNoSecurity()).To(BeFalse())
292336
})
@@ -296,7 +340,12 @@ var _ = Describe("Config", func() {
296340
})
297341
})
298342
Describe("DebugTicket", func() {
299-
var myConfig = config.File{}
343+
var myConfig config.File
344+
345+
BeforeEach(func() {
346+
myConfig = config.File{}
347+
})
348+
300349
It("Test with empty object", func() {
301350
Expect(myConfig.DebugTicket()).To(BeFalse())
302351
})
@@ -306,7 +355,12 @@ var _ = Describe("Config", func() {
306355
})
307356
})
308357
Describe("BannedTheme", func() {
309-
var myConfig = config.File{}
358+
var myConfig config.File
359+
360+
BeforeEach(func() {
361+
myConfig = config.File{}
362+
})
363+
310364
It("Test with empty object", func() {
311365
Expect(myConfig.BannedTheme()).To(HaveLen(0))
312366
})

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/onsi/gomega v1.17.0
1212
github.com/spf13/viper v1.10.1
1313
github.com/vmware/go-nfs-client v0.0.0-20190605212624-d43b92724c1b
14+
go.etcd.io/bbolt v1.3.6
1415
gopkg.in/fsnotify.v1 v1.4.7
1516
)
1617

0 commit comments

Comments
 (0)