Skip to content

Commit

Permalink
Merge branch 'feature/package-split' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasDoesThings committed Jul 10, 2023
2 parents 81423da + 19fc120 commit 1c4be11
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 41 deletions.
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true

[*.md]
charset = utf-8
indent_style = space
indent_size = 4
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ running/
.DS_STORE/
artifacts/
*.prof
*.iml
111 changes: 102 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,97 @@
# Plavatar
A stateless microservice that returns **pla**ceholder **avatar**s (=plavatars).
# plavatar
`plavatar/`: A library for generating **pla**ceholder **avatar**s (=plavatars). [-> JUMP TO DOCS](#plavatar-1)
`plavatar-rest/` A stateless REST microservice wrapping plavatar for you (docker image available) [-> JUMP TO DOCS](#plavatar-rest)

![docs/assets/readme-demo.png](docs/assets/readme-demo.png)

## API Endpoints
## plavatar
A library for generating **pla**ceholder **avatar**s (=plavatars).
Full Docs: https://pkg.go.dev/github.com/jonasdoesthings/plavatar

### Install
`go get github.com/jonasdoesthings/plavatar`
Then you can import the `"github.com/jonasdoesthings/plavatar"` package.

### Usage
Basic Example with a built-in generatorFunc:
```go
import (
"bytes"
"github.com/jonasdoesthings/plavatar"
)

func generateMyAvatar() (*bytes.Buffer, string) {
// Set-up a plavatar Generator instance
avatarGenerator := plavatar.Generator{}

// Configure the plavatar you want to generate
options := &plavatar.Options{
Name: "exampleSeed", // the seed to use
OutputShape: plavatar.ShapeSquare, // ShapeSquare or ShapeCircle
OutputFormat: plavatar.FormatSVG,
// OR if you want a PNG with the size of 512x512px:
// OutputFormat: plavatar.FormatPNG,
// OutputSize: 512,
}

// generate the avatar using the built-in Smiley generatorFunc and pass the options from above
avatar, rngSeed, err := avatarGenerator.GenerateAvatar(avatarGenerator.Smiley, options)
if err != nil {
panic(err)
}

// returns the avatar as *bytes.Buffer and the used rngSeed as string
return avatar, rngSeed
}
```

The plavatar Generator uses a passed generatorFunc to generate the avatar graphic.
The generatorFunc takes a svg canvas, a rng, the used rngSeed, and the generation options.
The generatorFunc then modifies the passed svg canvas.

Basic example with a custom generatorFunc:
```go
import (
"bytes"
svg "github.com/ajstarks/svgo"
"github.com/jonasdoesthings/plavatar"
"math/rand"
)

func CustomAvatar(canvas *svg.SVG, rng *rand.Rand, rngSeed int64, options *plavatar.Options) {
canvas.Def()
gradientColors := []svg.Offcolor{{0, "#FF0000", 1}}
canvas.LinearGradient("bg", 0, 0, 100, 100, gradientColors)
canvas.DefEnd()

plavatar.DrawCanvasBackground(canvas, options)
canvas.Line(-100, -10, 100, 10, "stroke: white; stroke-width: 23")
}

func generateMyCustomAvatar() (*bytes.Buffer, string) {
avatarGenerator := plavatar.Generator{}
options := &plavatar.Options{
Name: "exampleSeed",
OutputSize: 256,
OutputFormat: plavatar.FormatSVG,
OutputShape: plavatar.ShapeSquare,
}
avatar, rngSeed, err := avatarGenerator.GenerateAvatar(CustomAvatar, options)
if err != nil {
panic(err)
}

return avatar, rngSeed
}
```

### Testing
To run the go tests, use `go test -v ./...` in the root directory of the project.

## plavatar-rest
A stateless REST microservice wrapping plavatar for you (docker image available).

### API Endpoints
* `baseurl:port/laughing/<size>/<name>`
* `baseurl:port/smiley/<size>/<name>`
* `baseurl:port/happy/<size>/<name>`
Expand All @@ -20,23 +108,28 @@ With query params:
* `baseurl:port/laughing/<size>/<name>?format=svg&shape=square`
* `baseurl:port/laughing/<size>/<name>?shape=square` and so on

## Parameters
### Parameters
* `size` the image's size in pixels. has to be min 16, max 1024
* `name` **optional**, the random number generator seed to use. given the same name the same picture will be returned
### Query Params
#### Query Params
* `format`**optional**, either png (default) or svg. svg returns the raw svg
* `shape` **optional**, either circle (default) or square.

## **If possible use format=SVG.**
### **If possible, use format=SVG.**
Not only is format=SVG extremely faster, it also saves you a lot of bandwidth and latency. (A generated SVG is only ~2% the size of a 512px PNG)

## Configuration
### Docker Image
You can use our auto-built docker image `ghcr.io/jonasdoesthings/plavatar:latest`.
All versions and details can be found here:
https://github.com/JonasDoesThings/plavatar/pkgs/container/plavatar

### Configuration
You can optionally supply a config file if you are not happy with the preset settings.
By the default the program looks for a config file at `<running_folder>/config/plavatar.json`. If you want to use an
alternative location you can override this behaviour using the argument `--config <path_to_config>`. If there's neither
a config in the `config/` folder, nor you supply a path with `--config` the default configuration will be used.

### Default configuration file
#### Default configuration file

```json
{
Expand Down Expand Up @@ -74,7 +167,7 @@ a config in the `config/` folder, nor you supply a path with `--config` the defa
}
```

## Testing
### Testing
To run the go tests, use `go test -v ./...` in the root directory of the project.

To generate a self-signed certificate for testing purposes you can
Expand Down
3 changes: 3 additions & 0 deletions plavatar-rest/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/prometheus/client_golang v1.16.0
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.3
go.uber.org/zap v1.24.0
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.3 // indirect
Expand All @@ -29,6 +31,7 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
Expand Down
10 changes: 5 additions & 5 deletions plavatar-rest/internal/api/endpoint_avatar.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ func (server *Server) HandleGetAvatar(generatorFunc func(canvas *svg.SVG, rng *r
return context.Blob(http.StatusBadRequest, "application/json", []byte(`{"error": "invalid size"}`))
}

outputFormat := plavatar.PNG
outputFormat := plavatar.FormatPNG
mimeType := "image/png"
if strings.ToLower(context.QueryParam("format")) == "svg" {
outputFormat = plavatar.SVG
outputFormat = plavatar.FormatSVG
mimeType = "image/svg+xml"
}

outputShape := plavatar.Circle
outputShape := plavatar.ShapeCircle
if strings.ToLower(context.QueryParam("shape")) == "square" {
outputShape = plavatar.Square
outputShape = plavatar.ShapeSquare
}

generatedAvatar, rngSeed, err := server.avatarGenerator.GenerateAvatar(generatorFunc, &plavatar.Options{
Expand All @@ -39,7 +39,7 @@ func (server *Server) HandleGetAvatar(generatorFunc func(canvas *svg.SVG, rng *r
context.Response().Header().Add("Rng-Seed", rngSeed)

if err != nil {
return context.Blob(http.StatusInternalServerError, "application/json", []byte(err.Error()))
return context.JSONBlob(http.StatusInternalServerError, []byte(`{"error": "`+err.Error()+`"}`))
}

return context.Blob(http.StatusOK, mimeType, generatedAvatar.Bytes())
Expand Down
48 changes: 48 additions & 0 deletions plavatar-rest/internal/api/endpoint_avatar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package api

import (
"crypto/sha256"
"fmt"
"github.com/jonasdoesthings/plavatar"
"github.com/jonasdoesthings/plavatar/plavatar-rest/internal/utils"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)

var (
avatarGenerator = &plavatar.Generator{}
apiServer = &Server{
logger: utils.InitLogger(),
echoRouter: echo.New(),
avatarGenerator: avatarGenerator,
}
)

// todo: do a snapshot test for each avatar type
func TestServer_HandleGetAvatar(t *testing.T) {
minSize = 256
maxSize = 1024

req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()

c := apiServer.echoRouter.NewContext(req, rec)
c.SetPath("/smiley/:size/:name")
c.SetParamNames("size", "name")
c.SetParamValues("512", "6")

var shaHasher = sha256.New()

h := apiServer.HandleGetAvatar(avatarGenerator.Smiley)
if assert.NoError(t, h(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "6", rec.Header().Get("Rng-Seed"))

shaHasher.Write(rec.Body.Bytes())
hash := fmt.Sprintf("%x", shaHasher.Sum(nil))
assert.Equal(t, "45f2f471e4df40cf1fd2b424b1bb2275491e6b9198e3f70bbb9565519dcaa82b", hash)
}
}
3 changes: 1 addition & 2 deletions plavatar/avatar_pixel.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (
)

// Pixels
//
// TODO:
// Currently this outputs a wrong avatar in PNG / rasterized mode.
// Currently this always outputs a [ShapeCircle] in PNG output mode.
// oksvg does not support clippaths at the moment, so the whole square gets rasterized
// see: https://github.com/srwiley/oksvg/issues/10
func (generator *Generator) Pixels(canvas *svg.SVG, rng *rand.Rand, rngSeed int64, options *Options) {
Expand Down
Loading

0 comments on commit 1c4be11

Please sign in to comment.