diff --git a/pkg/processor/deserializer/json_deserializer.go b/pkg/processor/deserializer/json_deserializer.go index a673d299..5dcff938 100644 --- a/pkg/processor/deserializer/json_deserializer.go +++ b/pkg/processor/deserializer/json_deserializer.go @@ -3,6 +3,9 @@ package deserializer import ( "encoding/json" + "fmt" + "os" + "sync" "github.com/johnfercher/maroto/v2/pkg/processor/core" "github.com/johnfercher/maroto/v2/pkg/processor/loader" @@ -21,5 +24,48 @@ func NewJSONDeserializer() *jsonDeserializer { func (j *jsonDeserializer) Deserialize(documentJSON string) (map[string]interface{}, error) { var document map[string]interface{} err := json.Unmarshal([]byte(documentJSON), &document) - return document, err + if err != nil { + return nil, err + } + + resources, ok := document["Resources"].([]interface{}) + if !ok { + return document, nil + } + + document["Resources"] = j.loadResources(resources) + + return document, nil +} + +// loadResources method is responsible for loading each resource into +// memory via go routines +// `resources` should be type []map[string]interface{} with key "path" +// returns []map[string]interface{} with key "data" and the associated bytes of the file +func (j *jsonDeserializer) loadResources(resources []interface{}) []interface{} { + wg := sync.WaitGroup{} + for i, res := range resources { + resource, ok := res.(map[string]interface{}) + if !ok { + continue + } + path, ok := resource["path"].(string) + if !ok { + continue + } + + wg.Add(1) + go func(i int) { + defer wg.Done() + data, err := j.loader.Load(path) + if err != nil { + // TODO handle errors && io gracefully + fmt.Fprintln(os.Stderr, err.Error()) + } + resource["data"] = data + resources[i] = resource + }(i) + } + wg.Wait() + return resources } diff --git a/pkg/processor/deserializer/json_deserializer_test.go b/pkg/processor/deserializer/json_deserializer_test.go new file mode 100644 index 00000000..beea41e0 --- /dev/null +++ b/pkg/processor/deserializer/json_deserializer_test.go @@ -0,0 +1,69 @@ +package deserializer_test + +import ( + "io" + "os" + "testing" + + "github.com/johnfercher/maroto/v2/pkg/processor/deserializer" + "github.com/stretchr/testify/assert" +) + +func TestDeserializer(t *testing.T) { + t.Run( + "when Resources array is not empty, it should return a document with the resources & data for each resource", + func(t *testing.T) { + input := ` +{ + "Resources": [ + { + "name": "logo", + "path": "../../../docs/assets/images/logo.png" + }, + { + "name": "font", + "path": "../../../docs/assets/fonts/arial-unicode-ms.ttf" + } + ] +}` + + got, err := deserializer.NewJSONDeserializer().Deserialize(input) + assert.NoError(t, err) + assert.NotNil(t, got) + assert.Contains(t, got, "Resources") + + res, ok := got["Resources"].([]interface{}) + assert.True(t, ok) + assert.Len(t, res, 2) + + logoFile, err := os.Open("../../../docs/assets/images/logo.png") + if err != nil { + t.Fatal(err) + } + logoBytes, err := io.ReadAll(logoFile) + if err != nil { + t.Fatal(err) + } + + fontFile, err := os.Open("../../../docs/assets/fonts/arial-unicode-ms.ttf") + if err != nil { + t.Fatal(err) + } + fontBytes, err := io.ReadAll(fontFile) + if err != nil { + t.Fatal(err) + } + + assetBytes := [][]byte{logoBytes, fontBytes} + + for i, obj := range res { + resource, ok := obj.(map[string]interface{}) + assert.True(t, ok) + assert.Contains(t, resource, "data") + assert.IsType(t, resource["data"], []byte{}) + assert.NotNil(t, resource["data"]) + assert.Equal(t, assetBytes[i], resource["data"]) + } + }, + ) +}