27 Commits cb5e355800 ... 5456bcb7c5

Author SHA1 Message Date
  EUGENIO SOUZA CARVALHO 5456bcb7c5 a 3 years ago
  EUGENIO SOUZA CARVALHO 596ca4111b t1 3 years ago
  EUGENIO SOUZA CARVALHO 3b26fb80dc t1 3 years ago
  EUGENIO SOUZA CARVALHO cfd3ddaf16 t1 3 years ago
  EUGENIO SOUZA CARVALHO 3c539c2103 t1 3 years ago
  EUGENIO SOUZA CARVALHO 8f76684761 t1 3 years ago
  EUGENIO SOUZA CARVALHO 8c85158d01 t1 3 years ago
  EUGENIO SOUZA CARVALHO 383933d794 t1 3 years ago
  EUGENIO SOUZA CARVALHO c0cb0bee63 t1 3 years ago
  EUGENIO SOUZA CARVALHO cc1d2348a2 t1 3 years ago
  EUGENIO SOUZA CARVALHO 371454edb2 fix mongo 3 years ago
  EUGENIO SOUZA CARVALHO b70e6c4d76 mod debug 3 years ago
  EUGENIO SOUZA CARVALHO 1d38f38230 set params in context 4 years ago
  EUGENIO SOUZA CARVALHO ab1e83dbe6 set params in context 4 years ago
  EUGENIO SOUZA CARVALHO 16ad9ec1f1 set params in context 4 years ago
  EUGENIO SOUZA CARVALHO 3b4ccf4570 set params in context 4 years ago
  EUGENIO SOUZA CARVALHO bed13248e0 set params in context 4 years ago
  EUGENIO SOUZA CARVALHO 1b63f07a93 set params in context 4 years ago
  EUGENIO SOUZA CARVALHO 91e3353476 set params in context 4 years ago
  EUGENIO SOUZA CARVALHO 87d0cbb44e set params in context 4 years ago
  EUGENIO SOUZA CARVALHO 01a2cf9608 first erro details replace message of error 4 years ago
  EUGENIO SOUZA CARVALHO 9be0b8aba8 first erro details replace message of error 4 years ago
  EUGENIO SOUZA CARVALHO 1bb7d9fdf3 first erro details replace message of error 4 years ago
  EUGENIO SOUZA CARVALHO 6234cab41c aa 4 years ago
  EUGENIO SOUZA CARVALHO 5a62e5c91d aa 4 years ago
  EUGENIO SOUZA CARVALHO 463093b28f aa 4 years ago
  EUGENIO SOUZA CARVALHO e7f27f762f teste 4 years ago

+ 0 - 0
.gitignore


+ 101 - 0
Dockerfile

@@ -0,0 +1,101 @@
+FROM golang:1.14.6-alpine3.12 as builder
+LABEL maintainer Eugenio Carvalho <eugeniucarvalho@gmail.com>
+ENV NODE_VERSION 12.13.0
+ENV YARN_VERSION 1.19.1
+ENV GIT_TERMINAL_PROMPT=1
+ENV GONOSUMDB=git.eugeniocarvalho.dev/eugeniucarvalho
+ENV GO111MODULE=on
+
+RUN apk --update add git less openssh
+RUN addgroup -g 1000 node \
+    && adduser -u 1000 -G node -s /bin/sh -D node \
+    && apk add --no-cache \
+        libstdc++ \
+    && apk add --no-cache --virtual .build-deps \
+        curl \
+    && ARCH= && alpineArch="$(apk --print-arch)" \
+      && case "${alpineArch##*-}" in \
+        x86_64) \
+          ARCH='x64' \
+          CHECKSUM="f1c73636c4d345c4aefd65cc959f793d7bb795200d43e19e418d7811670b03dd" \
+          ;; \
+        *) ;; \
+      esac \
+  && if [ -n "${CHECKSUM}" ]; then \
+    set -eu; \
+    curl -fsSLO --compressed "https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz"; \
+    echo "$CHECKSUM  node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" | sha256sum -c - \
+      && tar -xJf "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
+      && ln -s /usr/local/bin/node /usr/local/bin/nodejs; \
+  else \
+    echo "Building from source" \
+    # backup build
+    && apk add --no-cache --virtual .build-deps-full \
+        binutils-gold \
+        g++ \
+        gcc \
+        gnupg \
+        libgcc \
+        linux-headers \
+        make \
+        python \
+    # gpg keys listed at https://github.com/nodejs/node#release-keys
+    && for key in \
+      94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
+      FD3A5288F042B6850C66B31F09FE44734EB7990E \
+      71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
+      DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
+      C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
+      B9AE9905FFD7803F25714661B63B535A4C206CA9 \
+      77984A986EBC2AA786BC0F66B01FBB92821C587A \
+      8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
+      4ED778F539E3634C779C87C6D7062848A1AB005C \
+      A48C2BEE680E841632CD4E44F07496B3EB3C1762 \
+      B9E2F5981AA6E0CD28160D9FF13993A75599653C \
+    ; do \
+      gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" || \
+      gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" || \
+      gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key" ; \
+    done \
+    && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz" \
+    && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
+    && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
+    && grep " node-v$NODE_VERSION.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
+    && tar -xf "node-v$NODE_VERSION.tar.xz" \
+    && cd "node-v$NODE_VERSION" \
+    && ./configure \
+    && make -j$(getconf _NPROCESSORS_ONLN) V= \
+    && make install \
+    && apk del .build-deps-full \
+    && cd .. \
+    && rm -Rf "node-v$NODE_VERSION" \
+    && rm "node-v$NODE_VERSION.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt; \
+  fi \
+  && rm -f "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" \
+  && apk del .build-deps \
+  && apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \
+  && for key in \
+    6A010C5166006599AA17F08146C2130DFD2497F5 \
+  ; do \
+    gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" || \
+    gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" || \
+    gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key" ; \
+  done \
+  && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
+  && curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \
+  && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
+  && mkdir -p /opt \
+  && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \
+  && ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \
+  && ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \
+  && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
+  && apk del .build-deps-yarn \
+  && rm -rf /var/lib/apt/lists/* \
+  && rm /var/cache/apk/*
+
+RUN npm install -g typescript typescript-formatter
+
+COPY ./eon-amd64 /usr/bin/eon
+RUN mkdir /workdir
+VOLUME /workdir
+WORKDIR /workdir

+ 280 - 0
api/debug.go

@@ -0,0 +1,280 @@
+package api
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"os"
+	"time"
+
+	"git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
+	"git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/sse"
+	context "github.com/kataras/iris/v12/context"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type DebugStage struct {
+	DebugEvent `json:",inline"`
+	Events     []*DebugEvent `json:"events"`
+}
+
+type DebugEvent struct {
+	ID      string      `json:"id"`
+	Type    string      `json:"type"`
+	Status  string      `json:"status"`
+	Created int64       `json:"created"`
+	Error   *errs.Error `json:"error"`
+	Data    interface{} `json:"data"`
+}
+
+type Request struct {
+	ID      string `json:"id"`
+	Status  string `json:"status"`
+	Created int64  `json:"created"`
+}
+
+type DebugTaks struct {
+	ID           string        `json:"id"`
+	Status       string        `json:"status"`
+	Created      int64         `json:"created"`
+	Stages       []*DebugStage `json:"stages"`
+	CurrentStage *DebugStage   `json:"-"`
+	Debug        *Debugger     `json:"-"`
+	Request      interface{}   `json:"request"`
+}
+
+func NewDebugTaks() *DebugTaks {
+	return &DebugTaks{
+		Stages:       []*DebugStage{},
+		CurrentStage: &DebugStage{},
+	}
+}
+
+func (debug *DebugTaks) Stage(id string) *DebugStage {
+	stage := &DebugStage{}
+	stage.ID = id
+	stage.Events = []*DebugEvent{}
+	debug.Stages = append(debug.Stages, stage)
+	debug.CurrentStage = stage
+	return stage
+}
+
+func (stage *DebugStage) PushEvent(event *DebugEvent) {
+	stage.Events = append(stage.Events, event)
+}
+
+func (debug *DebugTaks) Event(eventType, eventId string) *DebugEvent {
+
+	event := &DebugEvent{
+		ID:      eventId,
+		Type:    eventType,
+		Created: time.Now().Unix(),
+	}
+	debug.CurrentStage.PushEvent(event)
+	return event
+}
+
+func (task *DebugTaks) Finalize() {
+	var (
+		debug = task.Debug
+		out   []byte
+		err   error
+	)
+
+	for _, stage := range task.Stages {
+		for _, event := range stage.Events {
+			if event.Error != nil  {
+				event.Status = "error"
+				stage.Status = "error"
+				task.Status = "error"
+				goto end
+			}
+		}
+	}
+	// task.Event()
+	end:
+	if out, err = json.Marshal(task); err != nil {
+		debug.Emit(&sse.Event{
+			Kind:    "debugger.error",
+			Payload: err.Error(),
+		})
+	} else {
+		debug.Emit(&sse.Event{
+			Kind:    "request",
+			Payload: string(out),
+		})
+	}
+}
+
+type Debugger struct {
+	ChannelID string
+	Tasks     []*DebugTaks
+	Hub       *sse.SSEHub
+}
+
+func (debug *Debugger) Handler() func(context.Context) {
+	return func(ctx context.Context) {
+		var err error
+		task := debug.CreateTask()
+
+		if task.Request, err = parseRequest(ctx.Request()); err != nil {
+			fmt.Println("Request debug error", err.Error())
+		}
+
+		ctx.Values().Set("#debug", task)
+		ctx.Next()
+	}
+}
+
+func (debug *Debugger) CreateTask() *DebugTaks {
+	task := &DebugTaks{
+		ID:      primitive.NewObjectID().Hex(),
+		Status:  "",
+		Created: time.Now().Unix(),
+		Debug:   debug,
+	}
+	debug.Tasks = append([]*DebugTaks{task}, debug.Tasks...)
+	return task
+}
+
+func (debug *Debugger) Emit(events ...*sse.Event) {
+	channel := debug.Hub.GetChannel(debug.ChannelID)
+	for _, event := range events {
+		channel.Emit(event)
+	}
+}
+
+func (debug *Debugger) EventStream() func(context.Context) (interface{}, *errs.Error) {
+	return func(ctx context.Context) (resp interface{}, err *errs.Error) {
+
+		go func() {
+			if len(debug.Tasks) > 0 {
+				time.Sleep(time.Second)
+				fmt.Println("initialize debug with ", len(debug.Tasks))
+				for _, task := range debug.Tasks[:10] {
+					task.Finalize()
+				}
+				debug.Tasks = debug.Tasks[:10]
+			}
+		}()
+
+		if err = debug.Hub.UpgradeConnection(
+			ctx,
+			debug.ChannelID,
+		); err != nil {
+			return
+		}
+
+		resp = true
+		return
+	}
+}
+
+func NewDebug() *Debugger {
+	return &Debugger{
+		ChannelID: "debug",
+		Tasks:     []*DebugTaks{},
+		Hub: sse.NewSSEHub(&sse.SSEOptions{
+			URI:               os.Getenv("REDIS_URI"),
+			Password:          os.Getenv("REDIS_PASSWD"),
+			ChannelCollection: "debugger",
+		}),
+	}
+}
+func parseRequest(req *http.Request) (result map[string]interface{}, err error) {
+	save := req.Body
+	defer func() {
+		req.Body = save
+	}()
+
+	result = map[string]interface{}{}
+	// if !body || req.Body == nil {
+	// 	req.Body = nil
+	// } else {
+	// 	save, req.Body, err = drainBody(req.Body)
+	// 	if err != nil {
+	// 		return nil, err
+	// 	}
+	// }
+	var (
+		reqURI    string
+		reqMethod string
+	)
+
+	if reqURI = req.RequestURI; reqURI == "" {
+		reqURI = req.URL.RequestURI()
+	}
+
+	if reqMethod = req.Method; req.Method == "" {
+		reqMethod = "GET"
+	}
+
+	result["URI"] = reqURI
+	result["URL"] = map[string]interface{}{
+		"Scheme":      req.URL.Scheme,
+		"Host":        req.URL.Hostname(),
+		"IsAbs":       req.URL.IsAbs(),
+		"EscapedPath": req.URL.EscapedPath(),
+		"Port":        req.URL.Port(),
+		"Uri":         req.URL.String(),
+		"query":       req.URL.Query(),
+	}
+	result["Method"] = reqMethod
+	result["ProtoMajor"] = req.ProtoMajor
+	result["ProtoMinor"] = req.ProtoMinor
+	result["Proto"] = req.Proto
+	result["RemoteAddr"] = req.RemoteAddr
+	result["Header"] = req.Header
+	result["Host"] = req.Host
+	result["Close"] = req.Close
+	result["ContentLength"] = req.ContentLength
+
+	// for name, value := range req.Header {
+	// 	fmt.Printf("%v: %v\n", name, value)
+	// }
+
+	// fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
+	// reqURI, req.ProtoMajor, req.ProtoMinor)
+
+	// absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://")
+	// if !absRequestURI {
+	// 	host := req.Host
+	// 	if host == "" && req.URL != nil {
+	// 		host = req.URL.Host
+	// 	}
+	// 	if host != "" {
+	// 		fmt.Fprintf(&b, "Host: %s\r\n", host)
+	// 	}
+	// }
+
+	// chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
+	// if len(req.TransferEncoding) > 0 {
+	// 	fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ","))
+	// }
+	// if req.Close {
+	// 	fmt.Fprintf(&b, "Connection: close\r\n")
+	// }
+
+	// err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
+	// if err != nil {
+	// 	return nil, err
+	// }
+	// io.WriteString(&b, "\r\n")
+	// if req.Body != nil {
+	// 	var dest io.Writer = &b
+	// 	if chunked {
+	// 		dest = NewChunkedWriter(dest)
+	// 	}
+	// 	_, err = io.Copy(dest, req.Body)
+	// 	if chunked {
+	// 		dest.(io.Closer).Close()
+	// 		io.WriteString(&b, "\r\n")
+	// 	}
+	// }
+	// req.Body = save
+	// if err != nil {
+	// 	return nil, err
+	// }
+	// return b.Bytes(), nil
+	return
+}

+ 6 - 1
api/errs/errs.go

@@ -3,6 +3,8 @@ package errs
 import (
 	"fmt"
 	"runtime"
+
+	"github.com/davecgh/go-spew/spew"
 )
 
 type ErroNil *Error
@@ -45,6 +47,9 @@ func RegisterMapErrorFunction(id string, fn ErrorMapFunction) {
 
 func (e *Error) Details(desc *Detail) *Error {
 	e.Errors = append(e.Errors, desc)
+	if len(e.Errors) == 1 {
+		e.Message = desc.Message
+	}
 	return e
 }
 
@@ -394,7 +399,7 @@ func Trace() (stack *ErrorStack) {
 }
 
 func FromError(source error) (err *Error) {
-
+	spew.Dump(source)
 	for _, fn := range errorMapFunction {
 
 		if err = fn(source); err != nil {

+ 466 - 213
api/mongo.go

@@ -4,25 +4,29 @@ import (
 	"bytes"
 	"context"
 	"encoding/base64"
+	"encoding/json"
 	"fmt" // "html/template"
 	"reflect"
 	"regexp"
 	"strconv"
 	"strings"
+	"sync"
 	"text/template"
 	"time"
 
 	"git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
+	"github.com/davecgh/go-spew/spew"
+	"github.com/jeremywohl/flatten"
+	"github.com/kataras/iris"
 	iriscontext "github.com/kataras/iris/v12/context"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson/primitive"
 	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo/options"
-	"go.mongodb.org/mongo-driver/mongo/readconcern"
-	"go.mongodb.org/mongo-driver/mongo/readpref"
-	"go.mongodb.org/mongo-driver/mongo/writeconcern"
 )
 
+type ParseQueryHandler = func(ctx iriscontext.Context) (err *errs.Error)
+
 const (
 	DefaultAddrConnect = "mongodb://localhost:27017"
 
@@ -32,8 +36,10 @@ const (
 )
 
 var (
-	QueriesMap       = map[string]*template.Template{}
-	EMPTYQUERY_REGEX = regexp.MustCompile(`{\s*}`)
+	QueriesMap         = map[string]*template.Template{}
+	ParseQueryHandlers = map[string]ParseQueryHandler{}
+	EMPTYQUERY_REGEX   = regexp.MustCompile(`{\s*}`)
+	NextPageTokenMap   = map[string]string{}
 )
 
 func GetSessionContext(ctx iriscontext.Context) mongo.SessionContext {
@@ -52,7 +58,8 @@ func init() {
 		switch {
 		case strings.Contains(message, "E11000"):
 			err = errs.AlreadyExists().Details(&errs.Detail{
-				Message: "DUPLICATED_ITEM",
+				Reason:  "DUPLICATED_ITEM",
+				Message: message,
 			})
 		case strings.Contains(message, "cannot decode"):
 			err = errs.DataCaps().Details(&errs.Detail{
@@ -61,7 +68,8 @@ func init() {
 
 		case strings.Contains(message, "no documents in result"):
 			errs.NotFound().Details(&errs.Detail{
-				Message: "NOT_FOUND",
+				Reason:  "NOT_FOUND",
+				Message: message,
 			})
 		}
 		return
@@ -78,10 +86,14 @@ type Mongo struct {
 	// Password string `json:"password"`
 	// DataBase string `json:"database"`
 	// Addrs    string `json:"addrs"`
-	subject *BehaviorSubjectStruct
-	client  *mongo.Client
-	Config  string `json:"config"`
-	Clients map[string]*mongo.Client
+	FindManyNextPaginationMux sync.Mutex
+	// NextPageTokenMap          map[string]string
+	Uniq       int64
+	subject    *BehaviorSubjectStruct
+	client     *mongo.Client
+	Credential *options.Credential
+	Config     string `json:"config"`
+	Clients    map[string]*mongo.Client
 }
 type execfn func(context.Context, *mongo.Collection)
 
@@ -95,16 +107,19 @@ type Filter struct {
 	UserId primitive.ObjectID
 	// NextPageToken primitive.ObjectID
 	// PageToken PageToken
-	Fields *bson.M
-	Query  *bson.M
-	Patchs *bson.A
-	Sort   *bson.M
+	Fields  *bson.M
+	Query   *bson.M
+	Patchs  *bson.A
+	Sort    *bson.M
+	Context iriscontext.Context
 	// Sort       []string
+	NextPageToken       string
 	Collection          string
 	QueryType           string
 	DB                  string
 	Format              string
 	Insertion           int
+	ResultSizeEstimate  int64
 	CheckQuery          bool
 	IgnoreNoEntityFound bool
 	MaxResults          int
@@ -114,21 +129,19 @@ type Filter struct {
 	// Aggregate  interface{}
 	Pipeline primitive.A
 	Options  interface{}
+	// PatchOptions = patch.Patch()
 }
 
 func (f *Filter) Aggregate(ctx context.Context, collection *mongo.Collection, opts *options.AggregateOptions) (cursor *mongo.Cursor, err *errs.Error) {
 	var errl error
 
 	if opts == nil {
-		opts = options.Aggregate().SetBatchSize(int32(f.MaxResults))
+		opts = options.Aggregate().SetBatchSize(int32(f.MaxResults)).SetCollation(&options.Collation{
+			Strength: 1,
+			Locale:   "en",
+		})
 	}
 
-	// cmd := command.Aggregate{
-	// 	NS:       ns,
-	// 	Pipeline: pipeline,
-	// 	ReadPref: rp,
-	// }
-
 	if cursor, errl = collection.Aggregate(ctx, f.Pipeline, opts); errl != nil {
 		err = errs.FromError(errl)
 	}
@@ -159,6 +172,11 @@ func (t *Filter) Check() (err *errs.Error) {
 	return
 }
 
+func (mongo *Mongo) uniqID() int64 {
+	mongo.Uniq++
+	return mongo.Uniq
+}
+
 func (t *Mongo) Ready() *BehaviorSubjectStruct {
 
 	if t.subject == nil {
@@ -169,20 +187,34 @@ func (t *Mongo) Ready() *BehaviorSubjectStruct {
 }
 
 func (t *Mongo) Init() (err error) {
-	var ()
+	defer func() {
+		spew.Dump(err)
+	}()
+
+	t.FindManyNextPaginationMux = sync.Mutex{}
+	NextPageTokenMap = map[string]string{}
 
 	fmt.Printf("Connection string '%s'", t.Config)
+	clientOpts := options.Client().ApplyURI(t.Config)
 
-	if t.client, err = mongo.NewClient(options.Client().ApplyURI(t.Config)); err != nil {
+	if t.Credential != nil {
+		clientOpts.SetAuth(*t.Credential)
+	}
+	connectContext, _ := context.WithTimeout(context.Background(), 10*time.Second)
+	// if t.client, err = mongo.NewClient(setOptions); err != nil {
+	if t.client, err = mongo.Connect(
+		connectContext,
+		clientOpts,
+	); err != nil {
 		return
 	}
 
-	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
-	defer cancel()
+	// ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+	// defer cancel()
 
-	if err = t.client.Connect(ctx); err != nil {
-		return
-	}
+	// if err = t.client.Connect(ctx); err != nil {
+	// 	return
+	// }
 
 	t.Ready().Next(true)
 
@@ -232,16 +264,16 @@ func (t *Mongo) Dispatch(f *Filter) {
 }
 
 func (t *Mongo) InsertOne(f *Filter) (res *mongo.InsertOneResult, err *errs.Error) {
-
+	defer func() {
+		createDebugEvent(f, "models.insert.one", func(event *DebugEvent) {
+			event.Error = err
+		})
+	}()
 	f.Insertion = InsertOne
-
 	t.exec(f, func(ctx context.Context, collection *mongo.Collection) {
 		var lerr error
-
 		if res, lerr = collection.InsertOne(ctx, f.Entity); lerr != nil {
-
 			err = errs.FromError(lerr)
-
 		}
 	}, func(e *errs.Error) { err = e })
 
@@ -249,6 +281,11 @@ func (t *Mongo) InsertOne(f *Filter) (res *mongo.InsertOneResult, err *errs.Erro
 }
 
 func (t *Mongo) InsertMany(f *Filter) (res *mongo.InsertManyResult, err *errs.Error) {
+	defer func() {
+		createDebugEvent(f, "models.insert.many", func(event *DebugEvent) {
+			event.Error = err
+		})
+	}()
 
 	f.Insertion = InsertMany
 
@@ -272,12 +309,20 @@ func (t *Mongo) InsertMany(f *Filter) (res *mongo.InsertManyResult, err *errs.Er
 
 //Remove os elementos da colecao, selecionados pela query
 func (t *Mongo) RemoveOne(f *Filter) (res *mongo.DeleteResult, err *errs.Error) {
+	defer func() {
+		createDebugEvent(f, "models.remove.one", func(event *DebugEvent) {
+			event.Error = err
+		})
+	}()
 
 	f.CheckQuery = true
 
 	t.exec(f, func(ctx context.Context, collection *mongo.Collection) {
 		var lerr error
 
+		fmt.Println("remove.one")
+		spew.Dump(f.Query)
+
 		if res, lerr = collection.DeleteOne(ctx, f.Query); lerr != nil {
 			err = errs.FromError(lerr)
 
@@ -285,7 +330,6 @@ func (t *Mongo) RemoveOne(f *Filter) (res *mongo.DeleteResult, err *errs.Error)
 			err = errs.NotFound().Details(&errs.Detail{
 				Message: "No entity has been deleted",
 			})
-
 		}
 
 	}, func(e *errs.Error) { err = e })
@@ -295,19 +339,34 @@ func (t *Mongo) RemoveOne(f *Filter) (res *mongo.DeleteResult, err *errs.Error)
 //Remove os elementos da colecao, selecionados pela query
 func (t *Mongo) RemoveMany(f *Filter) (res *mongo.DeleteResult, err *errs.Error) {
 	var lerr error
+	var deleteOptions *options.DeleteOptions
+
+	defer func() {
+		createDebugEvent(f, "models.remove.many", func(event *DebugEvent) {
+			event.Error = err
+		})
+	}()
 
 	f.CheckQuery = true
+	if (f.Options != nil ) {
+		deleteOptions = f.Options.(*options.DeleteOptions)
+	}
 
 	t.exec(f, func(ctx context.Context, collection *mongo.Collection) {
 
-		if res, lerr = collection.DeleteMany(ctx, f.Entities); lerr != nil {
+		if res, lerr = collection.DeleteMany(
+			ctx,
+			f.Query,
+			deleteOptions,
+		); lerr != nil {
 			err = errs.FromError(lerr)
 
-		} else if res.DeletedCount == 0 {
-			err = errs.NotFound().Details(&errs.Detail{
-				Message: "No entity has been deleted",
-			})
-		}
+		} 
+		// else if res.DeletedCount == 0 {
+		// 	err = errs.NotFound().Details(&errs.Detail{
+		// 		Message: "No entity has been deleted",
+		// 	})
+		// }
 
 	}, func(e *errs.Error) { err = e })
 
@@ -317,6 +376,12 @@ func (t *Mongo) RemoveMany(f *Filter) (res *mongo.DeleteResult, err *errs.Error)
 func (t *Mongo) UpdateOne(f *Filter) (res *mongo.UpdateResult, err *errs.Error) {
 	var lerr error
 
+	defer func() {
+		createDebugEvent(f, "models.update.one", func(event *DebugEvent) {
+			event.Error = err
+		})
+	}()
+
 	f.Insertion = InsertOne
 	f.CheckQuery = true
 
@@ -343,6 +408,12 @@ func (t *Mongo) UpdateMany(f *Filter) (res *mongo.UpdateResult, err *errs.Error)
 		value    = reflect.ValueOf(f.Entities)
 	)
 
+	defer func() {
+		createDebugEvent(f, "models.update.many", func(event *DebugEvent) {
+			event.Error = err
+		})
+	}()
+
 	f.Insertion = InsertMany
 	f.CheckQuery = true
 
@@ -370,6 +441,14 @@ func RegisterQuery(id, query string) {
 	QueriesMap[id] = template.Must(template.New(id).Parse(query))
 }
 
+func BeforeParseQueryHandler(id string, fn ParseQueryHandler) {
+	ParseQueryHandlers[id] = fn
+}
+
+func getQueryHandler(id string) ParseQueryHandler {
+	return ParseQueryHandlers[id]
+}
+
 func ParseQuery(ctx iriscontext.Context, filter *Filter, basequery string, data map[string]interface{}) (err *errs.Error) {
 	var (
 		found             bool
@@ -378,15 +457,13 @@ func ParseQuery(ctx iriscontext.Context, filter *Filter, basequery string, data
 		baseQueryTemplate *template.Template
 		// baseQeuryDocument  *bson.M
 		// errbq              *errs.Error
+		nextPageToken      string
 		queryDecodedString []byte
+		values             = ctx.Values()
 		userQueryString    = Q(ctx, "q", "e30=")                                  // default base64 encoded from "{}"
 		includeInTrash, _  = strconv.ParseBool(Q(ctx, "includeInTrash", "false")) // default base64 encoded from "{}"
 	)
 
-	// defer func() {
-	// 	spew.Dump(filter.Query)
-	// }()
-
 	// Faz o parse da userQueryString enviada pelo usuario que deve ser um base64
 	if queryDecodedString, templateErr = base64.StdEncoding.DecodeString(userQueryString); templateErr != nil {
 		err = errs.InvalidArgument().Details(&errs.Detail{
@@ -403,14 +480,25 @@ func ParseQuery(ctx iriscontext.Context, filter *Filter, basequery string, data
 	}
 
 	// transclude the query to base query
-	data["_transclude_"] = string(queryDecodedString)
+	fmt.Println("##########################[", filter.NextPageToken, "]")
+	// spew.Dump(t.NextPageTokenMap)
+	if nextPageToken, found = NextPageTokenMap[filter.NextPageToken]; !found {
+		nextPageToken = "{}"
+	}
+
+	data["_transclude_nextPageToken"] = nextPageToken
+	data["_transclude_true"] = string(queryDecodedString)
 	data["_deleted_"] = includeInTrash
 
-	fmt.Println(
-		basequery,
-		string(queryDecodedString),
-		data,
-	)
+	values.Set("eon.query.data", data)
+	values.Set("eon.query.id", basequery)
+	values.Set("eon.query.options", filter)
+
+	if handler := getQueryHandler(basequery); handler != nil {
+		if err = handler(ctx); err != nil {
+			return
+		}
+	}
 
 	if templateErr = baseQueryTemplate.Execute(&buf, data); templateErr != nil {
 		err = errs.InvalidArgument().Details(&errs.Detail{
@@ -418,9 +506,9 @@ func ParseQuery(ctx iriscontext.Context, filter *Filter, basequery string, data
 		})
 		return
 	}
-	
+
 	x := buf.Bytes()
-	
+
 	fmt.Println("buf.Bytes()", string(x))
 
 	if templateErr = bson.UnmarshalExtJSON(x, false, &filter.Query); templateErr != nil {
@@ -432,88 +520,141 @@ func ParseQuery(ctx iriscontext.Context, filter *Filter, basequery string, data
 	return
 }
 
-func (t *Mongo) PatchOne(f *Filter) (res *mongo.UpdateResult, err *errs.Error) {
-
-	f.Insertion = Patch
-	f.CheckQuery = true
-
-	// out, _ := json.Marshal()
-	// fmt.Println(string(out))
-	// spew.Dump(f.Patchs)
+func (this *Mongo) PatchOne(actionOptions *Filter) (res *mongo.UpdateResult, err *errs.Error) {
+	uniq := this.uniqID()
+	fmt.Println("mongo.patch.many", uniq)
+	defer func() {
+		createDebugEvent(actionOptions, "models.patch.many", func(event *DebugEvent) {
+			event.Error = err
+		})
+		fmt.Println("mongo.patch.many.end", uniq)
+	}()
+	actionOptions.Insertion = Patch
+	actionOptions.CheckQuery = true
 
-	t.exec(f, func(ctx context.Context, collection *mongo.Collection) {
+	this.exec(actionOptions, func(ctx context.Context, collection *mongo.Collection) {
 
 		var (
 			lerr error
 			// client *mongo.Client
 		)
 
-		// if client, err = t.GetClient(f.DB); err != nil {
-		// 	return
-		// }
-		// spew.Dump(f.Query)
+		for _, p := range *actionOptions.Patchs {
 
-		// spew.Dump(f.Patchs)
-		// id := primitive.NewObjectID()
+			if actionOptions.Options == nil {
+				actionOptions.Options = options.Update()
+			}
 
-		t.client.UseSessionWithOptions(
-			ctx,
-			options.Session().SetDefaultReadPreference(readpref.Primary()),
-			func(sctx mongo.SessionContext) error {
-
-				defer func() {
-					if err != nil {
-						// fmt.Println("abort patch ", id.Hex())
-						sctx.AbortTransaction(sctx)
-					} else {
-						// fmt.Println("commit patch ", id.Hex())
-						sctx.CommitTransaction(sctx)
-					}
-					sctx.EndSession(sctx)
-				}()
+			if res, lerr = collection.UpdateOne(ctx, actionOptions.Query, p, actionOptions.Options.(*options.UpdateOptions)); lerr != nil {
+				// ce := lerr.(mongo.CommandError)
+				// fmt.Println("----------------------------",
+				// ce.Message,
+				// ce.Labels,
+				// ce.Code,
+				// ce.Name,
+				// )
+				// f.Context.Application().Logger().Errorf(lerr.Error())
+				err = errs.FromError(lerr)
+				return
+			}
 
-				sctx.StartTransaction(options.Transaction().
-					SetReadConcern(readconcern.Snapshot()).
-					SetWriteConcern(writeconcern.New(writeconcern.WMajority())))
+			if !actionOptions.IgnoreNoEntityFound && (res.ModifiedCount+res.UpsertedCount) == 0 {
 
-				for _, p := range *f.Patchs {
+				err = errs.NotFound().Details(&errs.Detail{
 
-					if f.Options == nil {
-						f.Options = options.Update()
-					}
+					Message: "No entity has been updated",
+				})
+				return
+			}
+		}
 
-					if res, lerr = collection.UpdateOne(ctx, f.Query, p, f.Options.(*options.UpdateOptions)); lerr != nil {
-						err = errs.FromError(lerr)
-						return lerr
-					}
+		// if client, err = t.GetClient(f.DB); err != nil {
+		// 	return
+		// }
+		// spew.Dump(f.Query)
 
-					if !f.IgnoreNoEntityFound && (res.ModifiedCount+res.UpsertedCount) == 0 {
+		// spew.Dump(f.Patchs)
+		// id := primitive.NewObjectID()
 
-						err = errs.NotFound().Details(&errs.Detail{
-							Message: "No entity has been updated",
-						})
-						return fmt.Errorf("No entity has been updated")
-					}
-				}
-				return nil
-			},
-		)
+		// t.client.UseSessionWithOptions(
+		// 	ctx,
+		// 	options.Session().SetDefaultReadPreference(readpref.Primary()),
+		// 	func(sctx mongo.SessionContext) error {
+
+		// 		defer func() {
+		// 			if err != nil {
+		// 				sctx.AbortTransaction(sctx)
+		// 			} else {
+		// 				sctx.CommitTransaction(sctx)
+		// 			}
+		// 			sctx.EndSession(sctx)
+		// 		}()
+
+		// 		sctx.StartTransaction(options.Transaction().
+		// 			SetReadConcern(readconcern.Snapshot()).
+		// 			SetWriteConcern(writeconcern.New(writeconcern.WMajority())))
+
+		// 		for _, p := range *f.Patchs {
+
+		// 			if f.Options == nil {
+		// 				f.Options = options.Update()
+		// 			}
+
+		// 			if res, lerr = collection.UpdateOne(ctx, f.Query, p, f.Options.(*options.UpdateOptions)); lerr != nil {
+		// 				err = errs.FromError(lerr)
+		// 				return lerr
+		// 			}
+
+		// 			if !f.IgnoreNoEntityFound && (res.ModifiedCount+res.UpsertedCount) == 0 {
+
+		// 				err = errs.NotFound().Details(&errs.Detail{
+		// 					Message: "No entity has been updated",
+		// 				})
+		// 				return fmt.Errorf("No entity has been updated")
+		// 			}
+		// 		}
+		// 		return nil
+		// 	},
+		// )
 	}, func(e *errs.Error) { err = e })
 	return
 }
 
-func (t *Mongo) PatchMany(f *Filter) (res *mongo.UpdateResult, err *errs.Error) {
+func (this *Mongo) PatchMany(actionOptions *Filter) (res *mongo.UpdateResult, err *errs.Error) {
+	uniq := this.uniqID()
+	fmt.Println("mongo.patch.many", uniq)
+	defer func() {
+		createDebugEvent(actionOptions, "models.patch.many", func(event *DebugEvent) {
+			event.Error = err
+		})
+		fmt.Println("mongo.patch.many.end", uniq)
+	}()
 
-	f.Insertion = Patch
-	f.CheckQuery = true
+	actionOptions.Insertion = Patch
+	actionOptions.CheckQuery = true
 
-	t.exec(f, func(ctx context.Context, collection *mongo.Collection) {
+	this.exec(actionOptions, func(ctx context.Context, collection *mongo.Collection) {
 
 		var (
 			lerr error
 			// client *mongo.Client
 		)
+		for _, p := range *actionOptions.Patchs {
+
+			if res, lerr = collection.UpdateMany(ctx, actionOptions.Query, p); lerr != nil {
+				err = errs.FromError(lerr)
+				return
+			}
 
+			if !actionOptions.IgnoreNoEntityFound && (res.ModifiedCount+res.UpsertedCount) == 0 {
+				err = errs.NotFound().Details(&errs.Detail{
+					Message: "No entity has been updated",
+				})
+				err.CodeText = "noEntityUpdated"
+				// fmt.Errorf("No entity has been updated")
+				return
+			}
+		}
 		// if client, err = t.GetClient(f.DB); err != nil {
 		// 	return
 		// }
@@ -523,44 +664,44 @@ func (t *Mongo) PatchMany(f *Filter) (res *mongo.UpdateResult, err *errs.Error)
 		// o, _ = json.MarshalIndent(f.Patchs, "", "  ")
 		// fmt.Println("patch", o)
 
-		t.client.UseSessionWithOptions(
-			ctx,
-			options.Session().SetDefaultReadPreference(readpref.Primary()),
-			func(sctx mongo.SessionContext) error {
-
-				defer func() {
-					if err != nil {
-						sctx.AbortTransaction(sctx)
-					} else {
-						sctx.CommitTransaction(sctx)
-					}
-					sctx.EndSession(sctx)
-				}()
-
-				sctx.StartTransaction(
-					options.Transaction().
-						SetReadConcern(readconcern.Snapshot()).
-						SetWriteConcern(writeconcern.New(writeconcern.WMajority())),
-				)
-
-				for _, p := range *f.Patchs {
-
-					if res, lerr = collection.UpdateMany(ctx, f.Query, p); lerr != nil {
-						err = errs.FromError(lerr)
-						return lerr
-					}
-
-					if !f.IgnoreNoEntityFound && (res.ModifiedCount+res.UpsertedCount) == 0 {
-						err = errs.NotFound().Details(&errs.Detail{
-							Message: "No entity has been updated",
-						})
-						err.CodeText = "noEntityUpdated"
-						return fmt.Errorf("No entity has been updated")
-					}
-				}
-				return nil
-			},
-		)
+		// t.client.UseSessionWithOptions(
+		// 	ctx,
+		// 	options.Session().SetDefaultReadPreference(readpref.Primary()),
+		// 	func(sctx mongo.SessionContext) error {
+
+		// 		defer func() {
+		// 			if err != nil {
+		// 				sctx.AbortTransaction(sctx)
+		// 			} else {
+		// 				sctx.CommitTransaction(sctx)
+		// 			}
+		// 			sctx.EndSession(sctx)
+		// 		}()
+
+		// 		sctx.StartTransaction(
+		// 			options.Transaction().
+		// 				SetReadConcern(readconcern.Snapshot()).
+		// 				SetWriteConcern(writeconcern.New(writeconcern.WMajority())),
+		// 		)
+
+		// 		for _, p := range *f.Patchs {
+
+		// 			if res, lerr = collection.UpdateMany(ctx, f.Query, p); lerr != nil {
+		// 				err = errs.FromError(lerr)
+		// 				return lerr
+		// 			}
+
+		// 			if !f.IgnoreNoEntityFound && (res.ModifiedCount+res.UpsertedCount) == 0 {
+		// 				err = errs.NotFound().Details(&errs.Detail{
+		// 					Message: "No entity has been updated",
+		// 				})
+		// 				err.CodeText = "noEntityUpdated"
+		// 				return fmt.Errorf("No entity has been updated")
+		// 			}
+		// 		}
+		// 		return nil
+		// 	},
+		// )
 	}, func(e *errs.Error) { err = e })
 	return
 }
@@ -590,9 +731,17 @@ func (mongo *Mongo) FindOneRx(options *Filter) *ObservableStruct {
 }
 
 func (t *Mongo) FindOne(f *Filter) (res *mongo.SingleResult, err *errs.Error) {
+	uniq := t.uniqID()
+	fmt.Println("mongo.find.one", uniq)
+	defer func() {
+		createDebugEvent(f, "models.find.one", func(event *DebugEvent) {
+			event.Error = err
+		})
+		fmt.Println("mongo.find.one.end", uniq, f.Collection)
+		spew.Dump(f.Query)
+	}()
 
 	f.CheckQuery = (f.QueryType != "aggregate")
-
 	t.exec(f, func(ctx context.Context, collection *mongo.Collection) {
 		var (
 			lerr   error
@@ -636,112 +785,192 @@ func findIds(filter *Filter) (ids []primitive.ObjectID) {
 	return
 }
 
+// if f.QueryType == "aggregate" {
+// cursor, lerr = f.Aggregate(ctx, collection, nil)
+// fmt.Println("-------------------------------------------------------------------------------after aggregate")
+// } else {
+// 	findOptions := options.Find()
+// 	findOptions.SetLimit(int64(f.MaxResults)).SetCollation(&options.Collation{
+// 		Locale:   "en",
+// 		Strength: 1,
+// 	})
+
+// 	// fmt.Printf("sort of query '%s'\n", f.Format)
+// 	// spew.Dump(f.Sort)
+// 	// spew.Dump(f.Fields)
+// 	// spew.Dump(f.Query)
+
+// 	if f.Sort != nil {
+// 		findOptions.SetSort(f.Sort)
+// 	}
+
+// 	if f.Fields != nil {
+// 		findOptions.SetProjection(f.Fields)
+// 	}
+
+// 	cursor, lerr = collection.Find(ctx, f.Query, findOptions)
+// }
+
 func (t *Mongo) FindMany(f *Filter) (cursor *mongo.Cursor, err *errs.Error) {
 
-	// f.CheckQuery = true
+	uniq := t.uniqID()
+	fmt.Println("mongo.find.many", uniq)
+	defer func() {
+		createDebugEvent(f, "models.find.many", func(event *DebugEvent) {
+			event.Error = err
+		})
+		fmt.Println("mongo.find.many.end", uniq, f.Collection)
+	}()
+
+	if f.Entities == nil {
+		err = errs.Internal().Details(&errs.Detail{
+			Message: "Entities can't be nil",
+		})
+		return
+	}
+
+	entitiesValue := reflect.ValueOf(f.Entities)
+	if entitiesValue.Kind() != reflect.Ptr || entitiesValue.Elem().Kind() != reflect.Slice {
+		err = errs.Internal().Details(&errs.Detail{
+			Message: "Entities argument must be a slice address",
+		})
+	}
 
 	t.exec(f, func(ctx context.Context, collection *mongo.Collection) {
 		var (
 			lerr  error
+			err   *errs.Error
 			elemp reflect.Value
-			// ids   = []primitive.ObjectID{}
-			// limit = int64(f.MaxResults)
-			// fields = f.Fields
 		)
 
-		// defer func() {
-		// 	if errd := recover(); errd != nil {
-		// 		// fmt.Println(errd)
-		// 		// spew.Dump(Trace())
-		// 		// fmt.Println(errors.WithStack(errd.(error)) )
-		// 		LogError(0, fmt.Sprintf("FindMany - %v", errd))
-		// 	}
-		// }()
-		// Carrega os ids e verifica o intervalo de paginacao da consulta.
-		// f.Fields = &bson.M{"_id": 1}
-
-		// ids = findIds(f)
-
-		// Atualiza a consulta para o conjunto de ids da paginacao
-		// f.Fields = fields
-		// f.Query = &bson.M{"_id": bson.M{"$in": ids}}
-
-		// fmt.Println("Query Type ", f.QueryType)
+		if f.QueryType != "aggregate" {
+			f.Pipeline = bson.A{}
 
-		if f.QueryType == "aggregate" {
-			cursor, lerr = f.Aggregate(ctx, collection, nil)
-		} else {
-			findOptions := options.Find()
-			findOptions.SetLimit(int64(f.MaxResults))
+			if f.Sort != nil && len(*f.Sort) > 0 {
+				f.Pipeline = append(f.Pipeline, bson.M{"$sort": f.Sort})
+			}
 
-			// fmt.Printf("sort of query '%s'\n", f.Format)
-			// spew.Dump(f.Sort)
-			// spew.Dump(f.Fields)
-			// spew.Dump(f.Query)
+			f.Pipeline = append(f.Pipeline, bson.M{"$match": f.Query})
 
-			if f.Sort != nil {
-				findOptions.SetSort(f.Sort)
+			if f.Fields != nil && len(*f.Fields) > 0 {
+				f.Pipeline = append(f.Pipeline, bson.M{"$project": f.Fields})
 			}
 
-			if f.Fields != nil {
-				findOptions.SetProjection(f.Fields)
+			if f.MaxResults > 0 {
+				f.Pipeline = append(f.Pipeline, bson.M{"$limit": f.MaxResults})
 			}
-
-			cursor, lerr = collection.Find(ctx, f.Query, findOptions)
+			f.QueryType = "aggregate"
 		}
 
-		if lerr == nil {
-			defer cursor.Close(ctx)
-
-			// spew.Dump(cursor)
+		wg := sync.WaitGroup{}
+		wg.Add(2)
 
-			if f.Entities == nil {
-				lerr = fmt.Errorf("Entities can't be nil")
-				goto FindManyError
+		go func() {
+			var countError error
+			defer wg.Done()
+			if f.ResultSizeEstimate, countError = collection.CountDocuments(nil, f.Query); countError != nil {
+				err = errs.FromError(countError)
 			}
+		}()
 
-			entitiesValue := reflect.ValueOf(f.Entities)
-			if entitiesValue.Kind() != reflect.Ptr || entitiesValue.Elem().Kind() != reflect.Slice {
-				lerr = fmt.Errorf("Entities argument must be a slice address")
-				goto FindManyError
+		go func() {
+			defer wg.Done()
+			if cursor, err = f.Aggregate(ctx, collection, nil); err != nil {
+				return
 			}
 
+			defer cursor.Close(ctx)
+
 			slicev := entitiesValue.Elem()
-			// fmt.Println("aaaaaaa000000000000001")
 			slicev = slicev.Slice(0, slicev.Cap())
-			// fmt.Println("aaaaaaa000000000000002")
 			typ := slicev.Type().Elem()
-			// fmt.Println("aaaaaaa000000000000003")
 
 			for cursor.Next(ctx) {
-
 				elemp = reflect.New(typ)
-
-				// fmt.Println("aqui")
 				if lerr = cursor.Decode(elemp.Interface()); lerr != nil {
-
-					// spew.Dump(elemp)
-					goto FindManyError
+					err = errs.FromError(lerr)
+					return
 				}
 				slicev = reflect.Append(slicev, elemp.Elem())
 			}
 
-			// spew.Dump(slicev)
+			if elemp.IsValid() {
+				var (
+					data          = map[string]interface{}{}
+					nextPageQuery = map[string]interface{}{}
+					out           []byte
+					variablesJson string
+				)
+				if out, lerr = json.Marshal(elemp.Elem().Interface()); lerr != nil {
+					err = errs.FromError(lerr)
+					return
+				}
+				if variablesJson, lerr = flatten.FlattenString(string(out), ".", flatten.DotStyle); lerr != nil {
+					err = errs.FromError(lerr)
+					return
+				}
+				if lerr = json.Unmarshal([]byte(variablesJson), &data); err != nil {
+					err = errs.FromError(lerr)
+					return
+				}
+				orderOperator := map[int]string{
+					1:  "$gt",
+					-1: "$lt",
+				}
+
+				if f.Sort == nil {
+					f.Sort = &bson.M{}
+				}
+
+				if (*f.Sort)["_id"] == nil {
+					(*f.Sort)["_id"] = 1
+				}
+
+				for prop, order := range *f.Sort {
+					value := data[fmt.Sprintf(".%s", prop)]
+					if prop == "_id" {
+						value = bson.M{"$oid": value}
+					}
+					nextPageQuery[prop] = bson.M{
+						orderOperator[order.(int)]: value,
+					}
+				}
+
+				if out, lerr = json.Marshal(nextPageQuery); lerr != nil {
+					err = errs.FromError(lerr)
+					return
+				}
+				f.NextPageToken = primitive.NewObjectID().Hex()
+				t.FindManyNextPaginationMux.Lock()
+				NextPageTokenMap[f.NextPageToken] = string(out)
+				t.FindManyNextPaginationMux.Unlock()
+			}
 			entitiesValue.Elem().Set(slicev)
-			return
-		}
+		}()
 
-	FindManyError:
-		err = errs.FromError(lerr)
-	}, func(e *errs.Error) { err = e })
+		wg.Wait()
+
+		return
+	},
+		func(e *errs.Error) { err = e },
+	)
 	return
 }
 
-func (models *Mongo) Exists(options *Filter) (exists bool, err *errs.Error) {
+func (this *Mongo) Exists(actionOptions *Filter) (exists bool, err *errs.Error) {
+	uniq := this.uniqID()
+	fmt.Println("mongo.exists", uniq)
+	defer func() {
+		createDebugEvent(actionOptions, "models.exists", func(event *DebugEvent) {
+			event.Error = err
+		})
+		fmt.Println("mongo.exists.end", uniq)
+	}()
 
-	options.Fields = &bson.M{"_id": 1}
+	actionOptions.Entity = map[string]interface{}{}
+	actionOptions.Fields = &bson.M{"_id": 1}
 
-	if _, err = models.FindOne(options); err != nil {
+	if _, err = this.FindOne(actionOptions); err != nil {
 
 		return
 	}
@@ -815,12 +1044,36 @@ func (t *Mongo) exec(f *Filter, execAction execfn, errorAction func(*errs.Error)
 		ctx, _ = context.WithTimeout(context.Background(), time.Minute)
 	}
 
+	
+	fmt.Println("###############", t.client == nil)
+
 	collection := t.client.Database(f.DB).Collection(f.Collection)
 
 	execAction(ctx, collection)
 	return
 }
 
+func createDebugEvent(options *Filter, eventType string, fn func(event *DebugEvent)) {
+	// debug := options.Context.Values().Get("#debug")
+	if options.Context != nil {
+		debug, defined := options.Context.Values().Get("#debug").(*DebugTaks)
+		if defined {
+			event := debug.Event(eventType, "")
+			event.Data = iris.Map{
+				"database":   options.DB,
+				"collection": options.Collection,
+				"query":      options.Query,
+				"aggregate":  options.Pipeline,
+				"sort":       options.Sort,
+				"fields":     options.Fields,
+				"maxResults": options.MaxResults,
+				"patchs":     options.Patchs,
+			}
+			fn(event)
+		}
+	}
+}
+
 func DeletedPatch() *bson.A {
 	return &bson.A{
 		bson.M{

+ 137 - 66
api/sse/hub.go

@@ -2,6 +2,7 @@ package sse
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
 	"time"
 
@@ -17,12 +18,12 @@ type Event struct {
 }
 
 type ChannelClient struct {
-	Context context.Context
-	Flusher http.Flusher
+	// Context context.Context
+	Flusher  http.Flusher
+	Response http.ResponseWriter
+	Request  *http.Request
 }
 
-// type  *event
-
 type Channel struct {
 	ID      string
 	Clients map[*ChannelClient]bool
@@ -37,11 +38,24 @@ func (channel *Channel) AddClient(client *ChannelClient) {
 }
 
 func (channel *Channel) Emit(event *Event) {
+
+	var (
+		err error
+	)
 	for client := range channel.Clients {
+
 		if event.Kind != "" {
-			client.Context.Writef("event: %s\n", event.Kind)
+			if _, err = fmt.Fprintf(client.Response, "event: %s\n", event.Kind); err != nil {
+				fmt.Println(fmt.Sprintf("%+v", err))
+				continue
+			}
 		}
-		client.Context.Writef("data: %s\n\n", event.Payload)
+
+		if _, err = fmt.Fprintf(client.Response, "data: %s\n\n", event.Payload); err != nil {
+			fmt.Println(fmt.Sprintf("%+v", err))
+			continue
+		}
+
 		client.Flusher.Flush()
 	}
 }
@@ -53,6 +67,7 @@ type SSEHub struct {
 	// Closed client connections
 	closingClients chan chan []byte
 
+	consume chan bool
 	// Client connections registry
 	Channels map[string]*Channel
 
@@ -72,6 +87,7 @@ func NewSSEHub(options *SSEOptions) *SSEHub {
 	return &SSEHub{
 		newClients:        make(chan chan []byte),
 		closingClients:    make(chan chan []byte),
+		consume:    make(chan bool),
 		Channels:          make(map[string]*Channel),
 		ChannelCollection: options.ChannelCollection,
 		RedisPool: &redis.Pool{
@@ -113,14 +129,14 @@ func (hub *SSEHub) GetChannel(channelID string) *Channel {
 }
 
 func (hub *SSEHub) Dispatch(event *Event, channels ...string) {
-
 	var (
 		exists bool
 		err    error
 		conn   = hub.RedisPool.Get()
 	)
-
 	defer conn.Close()
+	
+	eventBytes, _ := json.Marshal(event)
 
 	for _, channel := range channels {
 
@@ -128,66 +144,98 @@ func (hub *SSEHub) Dispatch(event *Event, channels ...string) {
 			continue
 		}
 
-		eventBytes, _ := json.Marshal(event)
-
 		conn.Do("RPUSH", channel, string(eventBytes))
 	}
+	go func(){ hub.consume <- true }()
+}
+
+func (hub *SSEHub) Close(channels ...string) {
+	var (
+		exists bool
+		count int
+		err    error
+		conn   = hub.RedisPool.Get()
+	)
+
+	for _, channel := range channels {
+		if exists, err = redis.Bool(conn.Do("HEXISTS", hub.ChannelCollection, channel)); err != nil || !exists {
+			continue
+		}
+		go func(channelID string) {
+			for {
+				if count, err = redis.Int(conn.Do("LLEN", channelID)); count > 0 {
+					continue
+				}
+				conn.Do("HDEL", hub.ChannelCollection, channelID)
+			}
+		}(channel)
+	}
 }
 
 func (hub *SSEHub) UpgradeConnection(ctx context.Context, channelId string) (err *errs.Error) {
 
 	var (
-		conn    = hub.RedisPool.Get()
-		payload string
-		count   int
-		// ok, closed bool
+		conn     = hub.RedisPool.Get()
+		payload  string
+		count    int
 		ok       bool
+		consuming bool
 		redisErr error
 		flusher  http.Flusher
+		response = ctx.ResponseWriter().Naive()
+		request  = ctx.Request()
 	)
 
 	defer conn.Close()
 
-	if flusher, ok = ctx.ResponseWriter().Flusher(); !ok {
+	if flusher, ok = response.(http.Flusher); !ok {
 		err = errs.HTTPVersionNotSupported().Details(&errs.Detail{
 			Dominio:      "",
 			Reason:       "Streaming unsupported",
-			Location:     "hook.beforePersist/caracterizationForm",
+			Location:     "hook.beforePersist",
 			LocationType: "",
 		})
 		return
 	}
 
-	// Each connection registers its own message channel with the Broker's connections registry.
-	// messageChan := make(chan []byte)
-
-	// Signal the broker that we have a new connection.
-	// hub.newClients <- messageChan
-
 	if _, redisErr = redis.Int(conn.Do("HSET", hub.ChannelCollection, channelId, true)); redisErr != nil {
 		err = errs.Internal().Details(&errs.Detail{
 			Dominio:      "",
 			Reason:       "Fail on register channel",
-			Location:     "hook.beforePersist/caracterizationForm",
+			Location:     "hook.beforePersist",
 			LocationType: "",
 		})
 		return
 	}
 
+	header := response.Header()
+	header.Set("Access-Control-Allow-Origin", "*")
+	header.Set("Content-Type", "text/event-stream")
+	header.Set("Cache-Control", "no-cache")
+	header.Set("Connection", "keep-alive")
+
+	flusher.Flush()
+
 	client := &ChannelClient{
-		Flusher: flusher,
-		Context: ctx,
+		Flusher:  flusher,
+		Response: response,
+		Request:  request,
 	}
 
 	channel := hub.GetChannel(channelId)
 	channel.AddClient(client)
 
 	finalizeMain := make(chan bool)
-	finalizePing := make(chan bool)
+	// finalizePing := make(chan bool)
 	finalized := false
 
 	// Listen to connection close and when the entire request handler chain exits(this handler here) and un-register messageChan.
-	ctx.OnClose(func() {
+
+	notify := response.(http.CloseNotifier).CloseNotify()
+
+	go func() {
+		<-notify
+		fmt.Println("SSE request end")
 		defer func() {
 			// recover from panic caused by writing to a closed channel
 			recover()
@@ -205,57 +253,32 @@ func (hub *SSEHub) UpgradeConnection(ctx context.Context, channelId string) (err
 		redis.Int(conn.Do("HDEL", hub.ChannelCollection, channelId))
 		// closesd = true
 		finalizeMain <- finalized
-		finalizePing <- finalized
+		// finalizePing <- finalized
 		channel.RemoveClient(client)
-	})
-
-	// Set the headers related to event streaming, you can omit the "application/json" if you send plain text.
-	// If you develop a go client, you must have: "Accept" : "application/json, text/event-stream" header as well.
-	ctx.ContentType("text/event-stream")
-	// ctx.Header("Access-Control-Allow-Origin", "*")
-	ctx.Header("Cache-Control", "no-cache")
-	ctx.Header("Connection", "keep-alive")
-	ctx.ResponseWriter().Header().Set("Access-Control-Allow-Origin", "*")
-
-	flusher.Flush()
-	// ctx.Request().Response.Header.Set("Access-Control-Allow-Origin", "*")
-
-	// Block waiting for messages broadcast on this connection's messageChan.
-	// check notification in redis
-	// go func() {
-
-	// ctx.Application().Logger().Warnf("init.notification.loop %v", closesd)
-	// Essa thread dispara um ping para manter a conexão ativa com o client
-	go func() {
-		ticker := time.NewTicker(3 * time.Second)
-		defer ticker.Stop()
-
-		for {
-			select {
-			case <-ticker.C:
-				// ctx.Application().Logger().Warnf("init.notification.loop 2s")
-				flusher.Flush()
-
-			case <-finalizePing:
-				// ctx.Application().Logger().Warnf("finalize init.notification.loop 2s")
-				close(finalizePing)
-				return
-			}
-		}
 	}()
 
 	ticker5Second := time.NewTicker(5 * time.Second)
 	defer ticker5Second.Stop()
 
+	flusher.Flush()
+	
 reset:
 	for {
 		select {
 		case <-ticker5Second.C:
+			go func ()  {
+				if !consuming {
+					hub.consume <- true
+				}	
+			}()
+		case <-hub.consume:
 			// ctx.Application().Logger().Warnf("init.notification.loop 5s")
-
+			
 			if count, redisErr = redis.Int(conn.Do("LLEN", channelId)); count == 0 || redisErr != nil {
 				continue reset
 			}
+			
+			consuming = true
 
 			for ; count > 0; count-- {
 
@@ -269,12 +292,60 @@ reset:
 				}
 			}
 
+			consuming = false
+
 		case <-finalizeMain:
+			
+			for {
+				if !consuming {
+					break
+				}
+				time.Sleep(time.Second * 2)
+			}
 			// ctx.Application().Logger().Infof("notification.finalize.disconect(%s)", channelId)
 			close(finalizeMain)
 			return
 		}
-
 	}
-	return
 }
+
+// ctx.OnClose(func() {
+// 	defer func() {
+// 		// recover from panic caused by writing to a closed channel
+// 		recover()
+// 		return
+// 	}()
+
+// 	if finalized {
+// 		return
+// 	}
+
+// 	finalized = true
+// 	// ctx.Application().Logger().Infof("notification.channel.disconect(%s)", channelId)
+// 	// Remove this client from the map of connected clients
+// 	// when this handler exits.
+// 	redis.Int(conn.Do("HDEL", hub.ChannelCollection, channelId))
+// 	// closesd = true
+// 	finalizeMain <- finalized
+// 	finalizePing <- finalized
+// 	channel.RemoveClient(client)
+// })
+
+// Set the headers related to event streaming, you can omit the "application/json" if you send plain text.
+// If you develop a go client, you must have: "Accept" : "application/json, text/event-stream" header as well.
+// header := ctx.ResponseWriter().Header()
+// header.Set("Access-Control-Allow-Origin", "*")
+// header.Set("Content-Type", "text/event-stream")
+// header.Set("Cache-Control", "no-cache")
+// header.Set("Connection", "keep-alive")
+// ctx.ContentType("text/event-stream")
+// ctx.Header("Cache-Control", "no-cache")
+// ctx.Header("Connection", "keep-alive")
+
+// ctx.Request().Response.Header.Set("Access-Control-Allow-Origin", "*")
+
+// Block waiting for messages broadcast on this connection's messageChan.
+// check notification in redis
+// go func() {
+
+// ctx.Application().Logger().Warnf("init.notification.loop %v", closesd)

+ 84 - 10
api/utils.go

@@ -16,6 +16,7 @@ import (
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson/primitive"
 	"go.mongodb.org/mongo-driver/mongo"
+	"golang.org/x/crypto/bcrypt"
 )
 
 var (
@@ -23,6 +24,8 @@ var (
 
 	Environment = map[string]interface{}{}
 	ToReference = references{}
+	Format      = fmt.Sprintf
+	Dump        = spew.Dump
 )
 
 type references struct {
@@ -46,6 +49,26 @@ func (this *references) Bool(value bool) *bool {
 	return &value
 }
 
+func (this *references) Int(value int) *int {
+	return &value
+}
+
+func (this *references) Int32(value int32) *int32 {
+	return &value
+}
+
+func (this *references) Int64(value int64) *int64 {
+	return &value
+}
+
+func (this *references) Float32(value float32) *float32 {
+	return &value
+}
+
+func (this *references) Float64(value float32) *float32 {
+	return &value
+}
+
 type ModeInterface interface {
 	Mode() string
 }
@@ -59,6 +82,7 @@ type PatchHistoryRegister struct {
 }
 
 type EntityModel struct {
+	// Id        primitive.ObjectID `bson:"_id" json:"-"`
 	Mode               string  `bson:"-" json:"-"`
 	ApplicationVersion *string `bson:"appVersion,omitempty" json:"-"`
 	Deleted            *bool   `bson:"deleted,omitempty" json:"-"`
@@ -77,6 +101,28 @@ type EntityModel struct {
 // 	return model.Rules
 // }
 
+
+func PasswordBcryptHash(password string)(hash string, err *errs.Error) { 
+	var (
+		bytes  []byte
+		errgen error
+	)
+
+	if bytes, errgen = bcrypt.GenerateFromPassword(
+		[]byte(password),
+		14,
+	); errgen != nil {
+		err = errs.Internal().Details(&errs.Detail{
+			Reason:  "passwordHashFailed",
+			Message: "Falha ao processar a senha",
+		})
+		return
+	}
+
+	hash = string(bytes)
+	return
+}
+
 func UserIDString(ctx context.Context) (id string, err *errs.Error) {
 
 	if user, ok := ctx.Values().Get("$user.ref").(map[string]interface{}); ok {
@@ -166,7 +212,7 @@ var (
 		AllowOrigin: []string{"*"},
 		// AllowOrigin:   []string{"http://localhost:4200"},
 		AllowMethods:  []string{"OPTIONS", "GET", "POST", "PUT", "DELETE", "PATCH"},
-		AllowHeaders:  []string{"Authorization", "Content-Type", "Origin", "Host", "x-api-build"},
+		AllowHeaders:  []string{"Accept", "Authorization", "Content-Type", "Origin", "Host", "x-api-build"},
 		ExposeHeaders: []string{"X-total-count"},
 	}
 
@@ -313,6 +359,13 @@ func finalizeRequest(ctx context.Context, resp interface{}, err *errs.Error) {
 
 	defer func() {
 		ctx.StopExecution()
+
+		if debug := ctx.Values().Get("#debug"); debug != nil {
+			go func() {
+				debug.(*DebugTaks).Finalize()
+			}()
+		}
+
 		if err != nil {
 
 			ctx.Application().Logger().Error(err.Error())
@@ -329,6 +382,7 @@ func finalizeRequest(ctx context.Context, resp interface{}, err *errs.Error) {
 		if r := recover(); r != nil {
 			fmt.Println("Recovered in f", r)
 		}
+		// fmt.Println(string(ctx.Values().Serialize()))
 	}()
 
 	if err != nil {
@@ -361,7 +415,7 @@ func finalizeRequest(ctx context.Context, resp interface{}, err *errs.Error) {
 		}
 	}
 	// default response case
-	ctx.WriteString("")
+	ctx.WriteString("invalid accept header value: " + accept)
 }
 
 // Call encapsula e trata os erros para cada requisição.
@@ -371,9 +425,15 @@ func CallAction(id string, fn func(context.Context) (interface{}, *errs.Error))
 			err      *errs.Error
 			resp     interface{}
 			finalize = true
+			values   = ctx.Values()
 		)
 
+		if debug, activeDebug := values.Get("#debug").(*DebugTaks); activeDebug {
+			debug.Stage(id)
+		}
+
 		defer func() {
+
 			if !ctx.IsStopped() {
 
 				if _err := recover(); _err != nil {
@@ -436,7 +496,16 @@ func transactionHandler(ctx context.Context, action string) (err *errs.Error) {
 	case "commit":
 		operation = func() error { return contextSession.CommitTransaction(contextSession) }
 	}
+
+	defer func() {
+		if localErr != nil {
+			err = errs.Internal().Details(&errs.Detail{
+				Message: localErr.Error(),
+			})
+		}
+	}()
 	try := 4
+
 	for {
 		if localErr = operation(); localErr == nil {
 			fmt.Println(action, "executed ")
@@ -458,13 +527,6 @@ func transactionHandler(ctx context.Context, action string) (err *errs.Error) {
 			}
 		}
 	}
-
-	if localErr != nil {
-		err = errs.Internal().Details(&errs.Detail{
-			Message: localErr.Error(),
-		})
-	}
-	return
 }
 
 func ReadJson(ctx context.Context, entity interface{}) (err *errs.Error) {
@@ -585,7 +647,7 @@ func DefaultCorsHandler() func(ctx context.Context) {
 
 func Cors(opt CorsOptions) func(ctx context.Context) {
 	return func(ctx context.Context) {
-		
+
 		ctx.Header("Access-Control-Allow-Credentials", "true")
 
 		if len(opt.AllowOrigin) > 0 {
@@ -660,6 +722,18 @@ func log(color string, m string) {
 	fmt.Printf("\x1b[%s;1m%s\x1b[0m\n", color, m)
 }
 
+func ObjectIDFromHex(objectId string) (id primitive.ObjectID, err *errs.Error) {
+	var errd error
+
+	if id, errd = primitive.ObjectIDFromHex(objectId); errd != nil {
+		err = errs.InvalidArgument().Details(&errs.Detail{
+			Message: errd.Error(),
+			Reason:  "InvalidArgument",
+		})
+	}
+	return
+}
+
 // func ParseRequestTest(ctx context.Context) {
 // 	var (
 // 		err    error

+ 3 - 0
build.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o eon-amd64 && \
+docker build -t eon-compile:0.0.1 .

+ 46 - 38
common/build_commands.go

@@ -1,18 +1,16 @@
 package common
 
 import (
-	"encoding/json"
 	"fmt"
 	"os/exec"
 	"regexp"
 	"strings"
-
-	"github.com/jeremywohl/flatten"
 )
 
 type Command struct {
 	Id          string `json:"id"`
 	Cmd         string `json:"cmd"`
+	Description string `json:"description"`
 	IgnoreErros bool   `json:"ignore.errors"`
 }
 
@@ -21,6 +19,8 @@ type BuildSet struct {
 	Cmd     string                `json:"cmd"`
 	Workdir string                `json:"workdir"`
 	Steps   []*Command            `json:"steps"`
+	Before  []*Command            `json:"beforeCompile"`
+	After   []*Command            `json:"afterCompile"`
 	Options *BuildOptions         `json:"options"`
 }
 
@@ -80,43 +80,34 @@ type BuildMode struct {
 }
 
 var (
-	buildSet = &BuildSet{
-		Steps: []*Command{},
-		Modes: map[string]*BuildMode{
-			"default": &BuildMode{},
-		},
-	}
+	expressionRegexp = regexp.MustCompile(`\{([\w\.]+)\}`)
 )
 
 func NewBuildOptions() *BuildOptions {
 	return &BuildOptions{
-		Mode: "default",
+		Mode:               "default",
+		ProjectDotNotation: map[string]interface{}{},
 	}
 }
 
-func RunBuildCommads(project *Project, buildOptions *BuildOptions) (err error) {
+func runBuildCommads(project *Project, client *Client, buildOptions *BuildOptions) (err error) {
 	var (
-		instance           *exec.Cmd
-		variables          = map[string]interface{}{}
-		variablesJson, cmd string
-		step               int
-		out                []byte
-		buildfile          = "build/build"
-		mode               = project.Mode
+		instance         *exec.Cmd
+		translateOptions *BuildSet
+		step             int
+		cmd              string
+		out              []byte
+		// variables        = map[string]interface{}{}
+		buildfile = fmt.Sprintf("build/%s", client.Id)
+		mode      = project.Mode
+		buildSet  = &BuildSet{
+			Steps: []*Command{},
+			Modes: map[string]*BuildMode{
+				"default": {},
+			},
+		}
 	)
 
-	if out, err = json.Marshal(project); err != nil {
-		return
-	}
-
-	if variablesJson, err = flatten.FlattenString(string(out), "project.", flatten.DotStyle); err != nil {
-		return
-	}
-
-	if err = json.Unmarshal([]byte(variablesJson), &variables); err != nil {
-		return
-	}
-
 	if buildOptions == nil {
 		buildOptions = NewBuildOptions()
 	}
@@ -124,33 +115,54 @@ func RunBuildCommads(project *Project, buildOptions *BuildOptions) (err error) {
 	buildOptions.Parse()
 
 	if err = JsonParseMode(buildfile, mode, &buildSet); err != nil {
-		return
+		err = nil
+		fmt.Printf("[warning] running '%s' without build file", client.Id)
+		buildOptions.Mode = "default"
 	}
 
 	buildSet.Options = buildOptions
 
+	if bo, found := project.TranslatorBuildOptionsMap[client.Id]; found {
+		translateOptions, err = bo(project, buildOptions)
+		if translateOptions.Before != nil {
+			buildSet.Steps = append(translateOptions.Before, buildSet.Steps...)
+		}
+		if translateOptions.After != nil {
+			buildSet.Steps = append(buildSet.Steps, translateOptions.After...)
+		}
+	}
+	if buildSet.After != nil {
+		buildSet.Steps = append(buildSet.Steps, buildSet.After...)
+	}
+
 	fmt.Println("Running build commands... on mode ", mode)
+	fmt.Println("RUN translate ", client.Id)
 
 	cmds := buildSet.Commands()
 	total := len(cmds)
 
 	for k, command := range cmds {
 
-		if cmd, err = parseCommand(command, variables); err != nil {
+		if cmd, err = parseCommand(command, buildOptions.ProjectDotNotation); err != nil {
 			return
 		}
 		step = k + 1
 
+		// goterm.MoveCursor(1, 1)
+
 		if buildOptions.IgnoreStep(step) {
-			fmt.Printf("Step (%d / %d) - [ Skiped ] %s\n", step, total, cmd)
+			// goterm.Printf("\tStep (%d / %d) - [ Skiped ] %s\n", step, total, cmd)
+			fmt.Printf("\tStep (%d / %d) - [ Skiped ] %s\n", step, total, cmd)
 			continue
 		}
 
-		fmt.Printf("Step (%d / %d) - %s\n", step, total, cmd)
+		// goterm.Printf("\tStep (%d / %d) - %s\n", step, total, cmd)
+		fmt.Printf("\tStep (%d / %d) - %s\n", step, total, cmd)
 
 		instance = exec.Command("sh", "-c", cmd)
 		instance.Dir = buildSet.Workdir
 
+		// goterm.Flush() // Call it every time at the end of rendering
 		if out, err = instance.CombinedOutput(); err != nil {
 			fmt.Println(string(out))
 			return
@@ -160,10 +172,6 @@ func RunBuildCommads(project *Project, buildOptions *BuildOptions) (err error) {
 	return
 }
 
-var (
-	expressionRegexp = regexp.MustCompile(`\{([\w\.]+)\}`)
-)
-
 func parseCommand(c *Command, variables map[string]interface{}) (string, error) {
 	var (
 		expression, replace, dotPathName string

+ 206 - 84
common/models.go

@@ -12,6 +12,7 @@ import (
 	"git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api"
 	"git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
 	ts "git.eugeniocarvalho.dev/eugeniucarvalho/gg/generators/typescript"
+	"github.com/jeremywohl/flatten"
 	"github.com/kataras/iris/v12/context"
 	"go.mongodb.org/mongo-driver/bson/primitive"
 )
@@ -56,6 +57,7 @@ type BuildOptions struct {
 	Mode                   string
 	IgnoreBuildSteps       string
 	IgnoreBuildStepsValues map[int]bool
+	ProjectDotNotation     map[string]interface{}
 }
 
 func (b *BuildOptions) IgnoreStep(step int) bool {
@@ -90,46 +92,49 @@ func ImportMap(base string) string {
 }
 
 type Project struct {
-	OutPath           string                   `json:"outPath"`
-	Package           string                   `json:"package"`
-	Kind              string                   `json:"kind"`
-	Etag              string                   `json:"etag"`
-	Version           string                   `json:"version"`
-	BuildVersion      string                   `json:"buildVersion"`
-	ID                string                   `json:"id"`
-	Name              string                   `json:"name"`
-	DataBaseSufix     string                   `json:"dataBaseSufix"`
-	Mode              string                   `json:"mode"`
-	Revision          string                   `json:"revision"`
-	Title             string                   `json:"title"`
-	Description       string                   `json:"description"`
-	OwnerDomain       string                   `json:"ownerDomain"`
-	OwnerName         string                   `json:"ownerName"`
-	DocumentationLink string                   `json:"documentationLink"`
-	Protocol          string                   `json:"protocol"`
-	BaseURL           string                   `json:"baseUrl"`
-	BasePath          string                   `json:"basePath"`
-	Middlewares       []string                 `json:"middlewares"`
-	ServicePath       string                   `json:"servicePath"`
-	GitRepository     string                   `json:"git.repository"`
-	Environment       Environment              `json:"environment"`
-	Variables         map[string]interface{}   `json:"variables"`
-	Resource          *Resource                `json:"-"`
-	Schemas           []*Entity                `json:"schemas"`
-	SchemasRef        map[string]*Entity       `json:"-"`
-	Resources         []*Resource              `json:"resources"`
-	Auth              Auth                     `json:"auth"`
-	TypeScriptSource  *ts.File                 `json:"-"`
-	Icons             map[string]string        `json:"icons"`
-	ReplaceWhenEmpty  map[string]bool          `json:"ReplaceWhenEmpty"`
-	OmitEmpty         map[string]bool          `json:"omitempty"`
-	Clients           []*Client                `json:"clients,omitempty"`
-	Translators       map[string]TranslationFn `json:"-"`
-	FormatMap         map[string]string        `json:"-"`
-	Queries           *QueryDef                `json:"queries"`
-	ACL               *ACL                     `json:"acl"`
-	Custom            map[string]interface{}   `json:"custom"`
+	OutPath                   string                                                      `json:"outPath"`
+	CurrentDirectory          string                                                      `json:"outPath"`
+	Package                   string                                                      `json:"package"`
+	Kind                      string                                                      `json:"kind"`
+	Etag                      string                                                      `json:"etag"`
+	Version                   string                                                      `json:"version"`
+	BuildVersion              string                                                      `json:"buildVersion"`
+	ID                        string                                                      `json:"id"`
+	Name                      string                                                      `json:"name"`
+	DataBaseSufix             string                                                      `json:"dataBaseSufix"`
+	Mode                      string                                                      `json:"mode"`
+	Revision                  string                                                      `json:"revision"`
+	Title                     string                                                      `json:"title"`
+	Description               string                                                      `json:"description"`
+	OwnerDomain               string                                                      `json:"ownerDomain"`
+	OwnerName                 string                                                      `json:"ownerName"`
+	DocumentationLink         string                                                      `json:"documentationLink"`
+	Protocol                  string                                                      `json:"protocol"`
+	BaseURL                   string                                                      `json:"baseUrl"`
+	BasePath                  string                                                      `json:"basePath"`
+	Middlewares               []string                                                    `json:"middlewares"`
+	ServicePath               string                                                      `json:"servicePath"`
+	GitRepository             string                                                      `json:"git.repository"`
+	Environment               Environment                                                 `json:"environment"`
+	Variables                 map[string]interface{}                                      `json:"variables"`
+	Resource                  *Resource                                                   `json:"-"`
+	Schemas                   []*Entity                                                   `json:"schemas"`
+	SchemasRef                map[string]*Entity                                          `json:"-"`
+	Resources                 []*Resource                                                 `json:"resources"`
+	Auth                      Auth                                                        `json:"auth"`
+	TypeScriptSource          *ts.File                                                    `json:"-"`
+	Icons                     map[string]string                                           `json:"icons"`
+	ReplaceWhenEmpty          map[string]bool                                             `json:"ReplaceWhenEmpty"`
+	OmitEmpty                 map[string]bool                                             `json:"omitempty"`
+	Clients                   []*Client                                                   `json:"clients,omitempty"`
+	Translators               map[string]TranslationFn                                    `json:"-"`
+	FormatMap                 map[string]string                                           `json:"-"`
+	Queries                   *QueryDef                                                   `json:"queries"`
+	ACL                       *ACL                                                        `json:"acl"`
+	Custom                    map[string]interface{}                                      `json:"custom"`
+	TranslatorBuildOptionsMap map[string]func(*Project, *BuildOptions) (*BuildSet, error) `json:"-"`
 }
+
 type ACL struct {
 	Roles       []*Role       `json:"roles"`
 	Permissions []*Permission `json:"permissions"`
@@ -235,6 +240,7 @@ type Method struct {
 	Parameters          map[string]*Parameter  `json:"parametersmap"`
 	Preconditions       []Action               `json:"preconditions"`
 	BeforeResponse      []Action               `json:"beforeResponse"`
+	BeforeParseRequest  []Action               `json:"beforeParseRequest"`
 	Custom              map[string]interface{} `json:"custom"`
 	// Parameters     map[string]*Parameter `json:"parameters"`
 }
@@ -287,6 +293,7 @@ type Propertie struct {
 	Autogenerate      map[string]AutoGenDef  `json:"-"`
 	Targets           string                 `json:"targets"`
 	Array             bool                   `json:"array"`
+	Required          bool                   `json:"required"`
 	Relation          bool                   `json:"relation"`
 	TagVisited        bool                   `json:"-"`
 	Reference         bool                   `json:"reference"`
@@ -354,24 +361,36 @@ func RequestParams(args string, params map[string]*Parameter) func(ctx context.C
 			switch param.Location {
 
 			case "query":
-				id = "q." + arg
+				id = "query." + arg
 				value = api.Q(ctx, arg, param.Default)
 			case "path":
-				id = "p." + arg
+				id = "path." + arg
 				value = api.P(ctx, arg, param.Default)
 			}
 			sourceValue = value
 
-			if param.Required && (value == "" || value == nil) {
+			emptyValue := (value == "" || value == nil)
+
+			if param.Required && emptyValue {
 				invalidArgument := errs.InvalidArgument()
-				invalidArgument.Message = fmt.Sprintf("ParamRequired:'%s'", param.ID)
+				invalidArgument.Message = fmt.Sprintf(
+					"ParamRequired: param '%s' in '%s'",
+					param.ID,
+					param.Location,
+				)
 				return nil, invalidArgument
 			}
 
-			if param.ConvertTo != "" {
+			if !emptyValue && param.ConvertTo != "" {
 				if value, err = convertValueByType(param.ConvertTo, value); err != nil {
 					invalidArgument := errs.InvalidArgument()
-					invalidArgument.Message = fmt.Sprintf("TypeConversionError:'%v'. Waiting a %s ", value, param.ConvertTo)
+					invalidArgument.Message = fmt.Sprintf(
+						"ParamTypeConversionError: param '%s' in '%s' with value '%v'. Waiting a %s ",
+						param.ID,
+						param.Location,
+						value,
+						param.ConvertTo,
+					)
 					return nil, invalidArgument
 				}
 			}
@@ -379,14 +398,17 @@ func RequestParams(args string, params map[string]*Parameter) func(ctx context.C
 			if param.Validation != nil {
 				for validator, args := range param.Validation {
 					if fn, found := validationParamFunctions[validator]; found {
-						if err = fn(value, args); err != nil {
+						ctx.Application().Logger().Info(fmt.Sprintf("validadete[%s][%s][%v]", validator, args, value))
+						if err = fn(param, value, args); err != nil {
 							return nil, err
 						}
 					}
 				}
 			}
 
-			values.Set(id, value)
+			values.Set(fmt.Sprintf("%s_conv", id), value)
+			values.Set(id, sourceValue)
+
 			paramsMap[fmt.Sprintf("%s_conv", arg)] = value
 			paramsMap[arg] = sourceValue
 		}
@@ -397,71 +419,119 @@ func RequestParams(args string, params map[string]*Parameter) func(ctx context.C
 
 var (
 	convertionTypeFunctions = map[string]func(interface{}) (interface{}, *errs.Error){
-		"ObjectID": stringToObjectId,
-		"bool":     stringToBool,
-		"int":      stringToInt,
-		"number":   stringToFloat,
-	}
+		"ObjectID":      stringToObjectId,
+		"array(string)": stringToArrayString,
+		"bool":          stringToBool,
+		"int":           stringToInt,
+		"number":        stringToFloat,
+	}
+
+	validationParamFunctions = map[string]func(*Parameter, interface{}, interface{}) *errs.Error{
+		"min": func(param *Parameter, value interface{}, minString interface{}) *errs.Error {
+			var input float64
+
+			if v, ok := value.(int64); ok {
+				input = float64(v)
+			} else if v, ok := value.(float64); ok {
+				input = v
+			} else if v, ok := value.(string); ok {
+				input = float64(len(v))
+			} else {
+				invalidArgument := errs.InvalidArgument()
+				invalidArgument.Message = fmt.Sprintf(
+					"[%s] ValueRestriction: mim validation requires (int,float,string)",
+					param.ID,
+				)
+				return invalidArgument
+			}
 
-	validationParamFunctions = map[string]func(interface{}, interface{}) *errs.Error{
-		"min": func(value interface{}, minString interface{}) *errs.Error {
-			min, err := strconv.Atoi(minString.(string))
-			if err != nil || value.(int) < min {
+			if min, convert := minString.(float64); !convert || input < min {
 				invalidArgument := errs.InvalidArgument()
-				invalidArgument.Message = fmt.Sprintf("ValueRestriction: value > %s. Received (%d)", minString, value)
+				invalidArgument.Message = fmt.Sprintf(
+					"[%s] ValueRestriction: value > %v. Received (%v)",
+					param.ID,
+					minString,
+					value,
+				)
 				return invalidArgument
 			}
 			return nil
 		},
-		"max": func(value interface{}, maxString interface{}) *errs.Error {
-			max, err := strconv.Atoi(maxString.(string))
-			if err != nil || value.(int) > max {
+		"max": func(param *Parameter, value interface{}, maxString interface{}) *errs.Error {
+
+			var input float64
+
+			if v, ok := value.(int64); ok {
+				input = float64(v)
+			} else if v, ok := value.(float64); ok {
+				input = v
+			} else if v, ok := value.(string); ok {
+				input = float64(len(v))
+			} else {
+				invalidArgument := errs.InvalidArgument()
+				invalidArgument.Message = fmt.Sprintf(
+					"[%s] ValueRestriction: mim validation requires (int,float,string)",
+					param.ID,
+				)
+				return invalidArgument
+			}
+
+			if max, convert := maxString.(float64); !convert || input > max {
 				invalidArgument := errs.InvalidArgument()
-				invalidArgument.Message = fmt.Sprintf("ValueRestriction: value < %s. Received (%d)", maxString, value)
+				invalidArgument.Message = fmt.Sprintf(
+					"[%s] ValueRestriction: value < %v. Received (%v)",
+					param.ID,
+					maxString,
+					value,
+				)
 				return invalidArgument
 			}
+
 			return nil
 		},
-		"accept": func(input interface{}, accept interface{}) *errs.Error {
+		"accept": func(param *Parameter, input interface{}, accept interface{}) *errs.Error {
 			var (
-				acceptValues = accept.([]string)
-				value        = input.(string)
+				acceptValues       = accept.([]interface{})
+				acceptValuesString = []string{}
+				value              = fmt.Sprintf("%v", input)
 			)
 
 			for _, acceptValue := range acceptValues {
-				if value == acceptValue {
+				if value == acceptValue.(string) {
 					return nil
 				}
+				acceptValuesString = append(acceptValuesString, acceptValue.(string))
 			}
 
 			invalidArgument := errs.InvalidArgument()
 			invalidArgument.Message = fmt.Sprintf(
-				"ValueRestriction: '%s' isn't accept. Accept [%s]",
+				"[%s] ValueRestriction: '%s' isn't accept. Accept [%s]",
+				param.ID,
 				value,
-				strings.Join(acceptValues, ","),
+				strings.Join(acceptValuesString, ","),
 			)
 			return invalidArgument
 		},
-		"reject": func(input interface{}, reject interface{}) *errs.Error {
+		"reject": func(param *Parameter, input interface{}, reject interface{}) *errs.Error {
 			var (
-				rejectValues = reject.([]string)
-				value        = input.(string)
+				rejectValues = reject.([]interface{})
+				value        = fmt.Sprintf("%v", input)
 			)
 
 			for _, rejectValue := range rejectValues {
-				if value == rejectValue {
+				if value == rejectValue.(string) {
 					invalidArgument := errs.InvalidArgument()
 					invalidArgument.Message = fmt.Sprintf(
-						"ValueRestriction: '%s' isn't accept. Rejected terms [%s]",
+						"[%s] ValueRestriction: '%s' isn't accept",
+						param.ID,
 						value,
-						strings.Join(rejectValues, ","),
 					)
 					return invalidArgument
 				}
 			}
 			return nil
 		},
-		"regex": func(input interface{}, regex interface{}) *errs.Error {
+		"regex": func(param *Parameter, input interface{}, regex interface{}) *errs.Error {
 			var (
 				regexString = regex.(string)
 				value       = input.(string)
@@ -473,7 +543,8 @@ var (
 
 				invalidArgument := errs.InvalidArgument()
 				invalidArgument.Message = fmt.Sprintf(
-					"ValueRestriction: '%s' isn't accept",
+					"[%s] ValueRestriction: '%s' isn't accept",
+					param.ID,
 					value,
 				)
 
@@ -559,6 +630,21 @@ func convertValueByType(typ string, value interface{}) (interface{}, *errs.Error
 	return value, nil
 }
 
+func stringToArrayString(value interface{}) (stringArray interface{}, err *errs.Error) {
+	var (
+		valueString string
+		ok          bool
+	)
+	if valueString, ok = value.(string); !ok {
+		err = errs.InvalidArgument().Details(&errs.Detail{
+			Reason: "StringToArrayString expect a string value",
+		})
+		return
+	}
+	stringArray = strings.Split(valueString, ",")
+	return
+}
+
 // func validateParam(param *Parameter, value interface{}) (interface{}, *errs.Error) {
 // 	var err *errs.Error
 
@@ -618,21 +704,50 @@ type EntityInfo struct {
 
 type TranslationFn func(p *Project) error
 
-func (p *Project) Build(b *BuildOptions) error {
-	var err error
+func (project *Project) Build(options *BuildOptions) (err error) {
+	var (
+		variablesJson string
+		out           []byte
+	)
 
-	for _, c := range p.Clients {
-		if fn, found := p.Translators[c.Id]; found {
-			if err = fn(p); err != nil {
-				fmt.Println("error on ", c.Id)
+	options.ProjectDotNotation = map[string]interface{}{}
+
+	if out, err = json.Marshal(project); err != nil {
+		return
+	}
+
+	if variablesJson, err = flatten.FlattenString(
+		string(out),
+		"project.",
+		flatten.DotStyle,
+	); err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(
+		[]byte(variablesJson),
+		&options.ProjectDotNotation,
+	); err != nil {
+		return
+	}
+
+	for _, client := range project.Clients {
+		if fn, found := project.Translators[client.Id]; found {
+
+			if err = fn(project); err != nil {
 				return err
 			}
+
+			if err = runBuildCommads(project, client, options); err != nil {
+				return
+			}
 		} else {
-			return fmt.Errorf("Middleware '%s' not defined!", c.Id)
+			err = fmt.Errorf("compiler translation model '%s' not defined", client.Id)
+			return
 		}
 	}
-	// fmt.Println("--- RunBuildCommads")
-	return RunBuildCommads(p, b)
+
+	return
 }
 
 func (p *Project) OutDirectory(path string) {
@@ -665,6 +780,13 @@ func (p *Project) GetCollection(entity string) string {
 	return "undefined"
 }
 
+func getCustom(options map[string]interface{}, path string) (resp interface{}) {
+	if options != nil {
+		resp = options[path]
+	}
+	return
+}
+
 func (p *Project) GetEntityDB(entity string) string {
 	if en, found := p.SchemasRef[entity]; found {
 		return en.DB + p.DataBaseSufix

+ 5 - 2
common/utils.go

@@ -97,6 +97,11 @@ func Mkdir(perm os.FileMode, paths ...string) (err error) {
 }
 
 func JsonParseMode(path, mode string, v interface{}) (err error) {
+	defer func() {
+		if rec := recover(); rec != nil {
+			err = fmt.Errorf("%s", rec)
+		}
+	}()
 	if err = ParseJson(path+".json", v); err != nil {
 		return
 	}
@@ -124,8 +129,6 @@ func ParseJson(filename string, v interface{}, ignoreFileExists ...bool) error {
 	return nil
 }
 
-
-
 func CamelToUnder(input string) string {
 	out := camelToUnderRegex.ReplaceAllStringFunc(input, func(m string) string {
 		return "_" + strings.ToLower(m)

BIN
eon-amd64


+ 1 - 0
flag/flag.go

@@ -56,6 +56,7 @@ func Initialize() (err error) {
 	Command = os.Args[1]
 
 	switch Command {
+	case "init":
 	case "g":
 		err = generateCommandParse()
 	case "compile":

+ 10 - 2
gen/gen.go

@@ -36,6 +36,10 @@ func CreateProject(mode string) (*Project, error) {
 				"go":       GO.Translate,
 				"angular6": TS.Translate,
 			},
+			TranslatorBuildOptionsMap: map[string]func(*Project, *BuildOptions) (*BuildSet, error){
+				"go":       GO.Build,
+				"angular6": TS.Build,
+			},
 		}
 	)
 
@@ -43,6 +47,8 @@ func CreateProject(mode string) (*Project, error) {
 		return nil, err
 	}
 
+	p.CurrentDirectory = directory
+
 	// Carrega a configuracao default do projeto
 	// root := []string{fmt.Sprintf("%s/project.json", directory)}
 
@@ -323,7 +329,7 @@ func interpolateProject(project *Project) (err error) {
 }
 
 var (
-	expandTranscludeExpression = regexp.MustCompile(`"\$\._transclude_":true`)
+	expandTranscludeExpression = regexp.MustCompile(`"\$\._transclude_":(true|"\w+")`)
 	expandVarExpression        = regexp.MustCompile(`"\$(\.\w+)"`)
 	removeAllSpacesExpression  = regexp.MustCompile(`(\s|\t|\r|\n)+`)
 )
@@ -352,7 +358,9 @@ func loadQueries(project *Project, directory string) (err error) {
 			data = removeAllSpacesExpression.ReplaceAllString(data, "")
 
 			data = expandTranscludeExpression.ReplaceAllStringFunc(data, func(input string) string {
-				return "{._transclude_}"
+				parts := strings.Split(input, ":")
+				selector := strings.Trim(parts[len(parts)-1], `"`)
+				return fmt.Sprintf("{._transclude_%s}", selector)
 			})
 
 			data = expandVarExpression.ReplaceAllStringFunc(data, func(input string) string {

+ 5 - 9
go.mod

@@ -3,17 +3,16 @@ module git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen
 go 1.13
 
 require (
-	git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.1
+	git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.2
 	git.eugeniocarvalho.dev/eugeniucarvalho/utils v1.0.2
 	github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a // indirect
 	github.com/DataDog/zstd v1.4.4 // indirect
+	github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
 	github.com/dave/jennifer v1.4.0
 	github.com/davecgh/go-spew v1.1.1
 	github.com/eugeniucarvalho/validator v0.0.0-20190212223505-9efa4c9aff1a
 	github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4 // indirect
 	github.com/fsnotify/fsnotify v1.4.7
-	github.com/go-stack/stack v1.8.0 // indirect
-	github.com/golang/snappy v0.0.1 // indirect
 	github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38
 	github.com/gorilla/schema v1.1.0 // indirect
 	github.com/gorilla/websocket v1.4.1
@@ -23,16 +22,13 @@ require (
 	github.com/kataras/golog v0.0.10
 	github.com/kataras/iris v11.1.1+incompatible
 	github.com/kataras/iris/v12 v12.1.3
-	github.com/klauspost/compress v1.9.4 // indirect
 	github.com/pascaldekloe/jwt v1.7.0
 	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
-	github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
 	github.com/xdg/stringprep v1.0.0 // indirect
-	go.mongodb.org/mongo-driver v1.2.0
-	golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 // indirect
-	golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
+	go.mongodb.org/mongo-driver v1.3.2
+	golang.org/x/crypto v0.0.0-20191219195013-becbf705a915
+	golang.org/x/net v0.0.0-20200528225125-3c3fba18258b // indirect
 	golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
-	golang.org/x/text v0.3.2 // indirect
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 	gopkg.in/yaml.v2 v2.2.7 // indirect
 )

+ 80 - 0
go.sum

@@ -2,6 +2,8 @@ git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.0 h1:BcjI7XjCPLrcFeGeV3gJzt8Mf98
 git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.0/go.mod h1:MsGB35SSYCc7sCff9PcM08TAd9UvFhFhXMvU3STh82k=
 git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.1 h1:vMtnzkyW0NdUtL7/M0ihbLkE06zXzwyGKmLNmfxnRNs=
 git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.1/go.mod h1:Uw49/w2AVJNPUDR8uiRVb0cyGg5vy1bThuggLwnziFo=
+git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.2 h1:oqhoKd/7GD0oim7Oy+08mBWqkiWf2w1kkTjs310+XcU=
+git.eugeniocarvalho.dev/eugeniucarvalho/gg v1.0.2/go.mod h1:Uw49/w2AVJNPUDR8uiRVb0cyGg5vy1bThuggLwnziFo=
 git.eugeniocarvalho.dev/eugeniucarvalho/utils v1.0.2 h1:pi9aCd86+20NXV+QYITfUuEhY9rqv14mWeuBoT9sLq0=
 git.eugeniocarvalho.dev/eugeniucarvalho/utils v1.0.2/go.mod h1:GoSSifraiFtzF/IVzcqFhy5DKR/npEiTNvpKeL1/Jc4=
 github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
@@ -21,6 +23,8 @@ github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqR
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns=
 github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
+github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0pdTRcr/YEbBoxY+3GOH3gWvl4=
+github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -49,6 +53,30 @@ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclK
 github.com/go-fsnotify/fsnotify v0.0.0-20180321022601-755488143dae h1:PeVNzgTRtWGm6fVic5i21t+n5ptPGCZuMcSPVMyTWjs=
 github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
 github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
 github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
@@ -57,6 +85,7 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38 h1:y0Wmhvml7cGnzPa9nocn/fMraMH/lMDdeG+rkx4VgYY=
 github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
 github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
@@ -77,6 +106,7 @@ github.com/iris-contrib/schema v0.0.1 h1:10g/WnoRR+U+XXHWKBHeNy/+tZmM2kcAVGLOsz+
 github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
 github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs=
 github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -84,6 +114,8 @@ github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKM
 github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
 github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
 github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
+github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
 github.com/kataras/golog v0.0.10 h1:vRDRUmwacco/pmBAm8geLn8rHEdc+9Z4NAr5Sh7TG/4=
 github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
 github.com/kataras/iris v11.1.1+incompatible h1:c2iRKvKLpTYMXKdVB8YP/+A67NtZFt9kFFy+ZwBhWD0=
@@ -95,13 +127,20 @@ github.com/kataras/pio v0.0.2 h1:6NAi+uPJ/Zuid6mrAKlgpbI11/zK/lV4B2rxWaJN98Y=
 github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
 github.com/kataras/sitemap v0.0.5 h1:4HCONX5RLgVy6G4RkYOV3vKNcma9p236LdGOipJsaFE=
 github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.9.4 h1:xhvAeUPQ2drNUhKtrGdTGNvV9nNafHMUkRyLkzxJoB4=
 github.com/klauspost/compress v1.9.4/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
+github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
 github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
 github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
 github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
@@ -113,15 +152,22 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
 github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
 github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 github.com/pascaldekloe/jwt v1.7.0 h1:0vNebf7Whqyv8yrly+BSm4B4Y0aAprLTACaX5eLBng8=
 github.com/pascaldekloe/jwt v1.7.0/go.mod h1:TKhllgThT7TOP5rGr2zMLKEDZRAgJfBbtKyVeRsNB9A=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s=
 github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -129,26 +175,39 @@ github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiy
 github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
 github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
 github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 go.mongodb.org/mongo-driver v1.2.0 h1:6fhXjXSzzXRQdqtFKOI1CDw6Gw5x6VflovRpfbrlVi0=
 go.mongodb.org/mongo-driver v1.2.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.3.2 h1:IYppNjEV/C+/3VPbhHVxQ4t04eVW0cLp0/pNdW++6Ug=
+go.mongodb.org/mongo-driver v1.3.2/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 h1:aJ0ex187qoXrJHPo8ZasVTASQB7llQP6YeNzgDALPRk=
 golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -163,20 +222,41 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8ou
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs=
+golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
 golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf h1:++r/Kj1CfG42p6XntDItK1TfB5V6Vq/baDeKvV1q5gY=
 golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d h1:bt+R27hbE7uVf7PY9S6wpNg9Xo2WRe/XQT0uGq9RQQw=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
 gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=

+ 52 - 0
translate/got/build.go

@@ -0,0 +1,52 @@
+package got
+
+import (
+	"fmt"
+	"os"
+
+	. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common"
+)
+
+var Build = func(project *Project, buildOptions *BuildOptions) (fn *BuildSet, err error) {
+	var (
+		files  []os.FileInfo
+		before = []*Command{
+			{
+				Cmd:         "rm -f go.*",
+				Description: "Remove os arquivos do módulo do GO",
+			},
+			{
+				Cmd:         "go mod init {project.custom.go.package.repository}/build",
+				Description: "Inicializa o módulo do GO",
+			},
+		}
+	)
+
+	path := fmt.Sprintf("%s/include/go", project.CurrentDirectory)
+	if files, err = GetFiles(path); err == nil {
+		for _, file := range files {
+			if file.Name()[0] == '*' {
+				continue
+			}
+			if file.IsDir() {
+				name := file.Name()
+				before = append(before, &Command{
+					Cmd:         fmt.Sprintf("go mod edit -replace {project.custom.go.package.repository}/build/v1/%s=./v1/%s", name, name),
+					Description: "Inicializa o módulo do GO",
+				})
+			}
+		}
+	}
+
+	fn = &BuildSet{
+		Before: before,
+		After: []*Command{
+			{
+				Id:          "build",
+				Cmd:         "GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o server",
+				Description: "Compila a aplicação server da api",
+			},
+		},
+	}
+	return
+}

+ 9 - 7
translate/got/constants.go

@@ -34,17 +34,19 @@ func CreateVariables(p *Project) error {
 			).Params(
 				G.Id("ctx").Qual(IRIS_CTX, "Context"),
 			).Id(` *`).Qual(API_URL, "Filter").Id(`{
-				var session `).Qual(MONGO, "SessionContext").Id(`
+				var (
+					options = &api.Filter{
+						DB:`).Id(dbid).Id(`,
+						Collection:`).Id(collectionid).Id(`,
+					}
+				)
 				
 				if ctx != nil {
-					session = api.GetSessionContext(ctx)
+					options.Context = ctx
+					options.SessionContext = api.GetSessionContext(ctx)
 				}
 
-				return &api.Filter{
-					DB:`).Id(dbid).Id(`,
-					Collection:`).Id(collectionid).Id(`,
-					SessionContext: session,
-				}
+				return options
 			} 
 			`).Line())
 

+ 27 - 5
translate/got/functions.go

@@ -52,11 +52,11 @@ func generateHookCall(project *Project, method *Method, hookId string) {
 
 func parseMethodActions(actions []Action) string {
 	actionsCallStmt := []string{}
-	var actionStmt string
+	var actionStmt, actionId string
 
 	for _, action := range actions {
-
-		actionStmt = fmt.Sprintf("actions.%s", strings.Title(action.ID))
+		actionId = strings.Title(action.ID)
+		actionStmt = fmt.Sprintf("actions.%s", actionId)
 
 		if action.Context != nil {
 			actionStmt = fmt.Sprintf(
@@ -66,8 +66,30 @@ func parseMethodActions(actions []Action) string {
 			)
 		}
 
-		actionsCallStmt = append(actionsCallStmt, actionStmt)
+		actionsCallStmt = append(
+			actionsCallStmt, 
+			fmt.Sprintf(`{"%s", %s}`, actionId,actionStmt),
+		)
+	}
+
+	if len(actionsCallStmt) > 0{
+		return fmt.Sprintf(`[]Action {
+			%s,
+			}`, strings.Join(actionsCallStmt, ",\n"))
 	}
+	return ""
 
-	return strings.Join(actionsCallStmt, ",\n")
+}
+
+func getCustom(options map[string]interface{}, path string, fallback ...interface{}) (resp interface{}) {
+	found := false
+	if options != nil  {
+		if resp, found = options[path]; found {
+			return
+		}
+	} 
+	for _, value := range fallback {
+		resp = value
+	}
+	return
 }

+ 19 - 8
translate/got/middleware_delete.go

@@ -17,8 +17,12 @@ var (
 func init() {
 
 	deleteStmtsTmpl, deleteStmtsErr = ParseTemplate(`
+		var (
+			values = ctx.Values()
+		)
+		values.Set("hard.deletion", {{.hardDeletion}})
 
-		filter := ctx.Values().Get("$filter").(*api.Filter)
+		filter := values.Get("$filter").(*api.Filter)
 		filter.DB = "{{.dbName}}"
 		filter.Collection = "{{.collectionName}}"
 			
@@ -31,6 +35,7 @@ func init() {
 		}
 		{{end}}
 
+
 		{{if .preconditions}}
 			if _, err = executeAction(
 				ctx,
@@ -39,19 +44,24 @@ func init() {
 				return
 			}
 		{{end}}
-
-		if replacement := ctx.Values().Get("replace.default.patch"); replacement != nil  {
-			filter.Patchs = replacement.(*bson.A)
+		
+		if hardDeletion, _ := values.GetBool("hard.deletion"); hardDeletion {
+			_, err = models.Api.RemoveOne(filter)
 		} else {
-			filter.Patchs = api.DeletedPatch()
+			if replacement := values.Get("replace.default.patch"); replacement != nil  {
+				filter.Patchs = replacement.(*bson.A)
+			} else {
+				filter.Patchs = api.DeletedPatch()
+			}
+			_, err = models.Api.PatchOne(filter)
 		}
-	
-		if _, err = models.Api.PatchOne(filter); err != nil {
+
+		if err != nil {
 			err.Details(&errs.Detail{
 				Dominio:      "",
 				Reason:       "",
 				Location:     "middleware.path",
-				LocationType: "middleware.operation/Caracterization",
+				LocationType: "middleware.operation",
 			})
 		}
 
@@ -137,6 +147,7 @@ var (
 					"dependenceMethod": dependenceMethod,
 					"beforePersist":    beforePersist,
 					"beforeSend":       beforeSend,
+					"hardDeletion":     getCustom(method.Custom, "go.entiy.hard.deletion", false),
 					"preconditions":    parseMethodActions(method.Preconditions),
 					"beforeResponse":   parseMethodActions(method.BeforeResponse),
 				}

+ 4 - 4
translate/got/middleware_get_list.go

@@ -18,9 +18,9 @@ func init() {
 
 	getListStmtsTmpl, getListStmtsErr = ParseTemplate(`
 	var (
-		ntoken   string
+		// ntoken   string
+		// count    = 0
 		entities = []*models.{{.entity}}{}
-		count    = 0
 		values = ctx.Values()
 	)
 
@@ -61,8 +61,8 @@ func init() {
 
 	resp = &models.{{.responseStruct}}{
 		Itens:              entities,
-		NextPageToken:      ntoken,
-		ResultSizeEstimate: count,
+		NextPageToken:      options.NextPageToken,
+		ResultSizeEstimate: options.ResultSizeEstimate,
 	}
 
 	return`)

+ 3 - 1
translate/got/middleware_main.go

@@ -78,7 +78,9 @@ func CreateMainFile(project *Project) (err error) {
 		return
 	}
 
-	path := fmt.Sprintf("%s/v1/app_initialize.go", project.OutPath)
+	// path := fmt.Sprintf("%s/v1/app_initialize.go", project.OutPath)
+	path := fmt.Sprintf("../project/include/go/app_initialize.go")
+
 	if !utils.FileExists(path) {
 
 		AppInitialize := G.NewFile("v1")

+ 18 - 9
translate/got/middleware_patch.go

@@ -32,6 +32,10 @@ func init() {
 	user := values.Get("$user.ref").(*models.UserReference)
 	entity.UpdatedBy = user
 	values.Set("entity", entity)
+	{{if .entityAlias }} 
+	values.Set("{{.entityAlias}}", entity) 
+	values.Set("{{.entityAlias}}.patch", patchs)
+	{{end}}
 	
 	{{if .preconditions}}
 		if _, err = executeAction(
@@ -42,13 +46,14 @@ func init() {
 		}
 	{{end}}
 
-	filter := values.Get("$filter").(*api.Filter)
+	options := values.Get("$filter").(*api.Filter)
 
-	filter.DB = "{{.dbName}}"
-	filter.Collection = "{{.collectionName}}"
-	filter.Patchs = patchs.Patch()
+	options.DB = "{{.dbName}}"
+	options.Collection = "{{.collectionName}}"
+	options.Patchs, options.Options = patchs.Patch()
+	api.Dump(options.Options)
 
-	if _, err = models.Api.PatchOne(filter); err != nil {
+	if _, err = models.Api.PatchOne(options); err != nil {
 
 		err.Details(&errs.Detail{
 			Dominio:      "",
@@ -60,9 +65,9 @@ func init() {
 		return
 	}
 
-	filter.Entity = &models.{{.entity}}{}
+	options.Entity = &models.{{.entity}}{}
 
-	if _, err = models.Api.FindOne(filter); err != nil {
+	if _, err = models.Api.FindOne(options); err != nil {
 
 		err.Details(&errs.Detail{
 			Dominio:      "",
@@ -73,9 +78,12 @@ func init() {
 
 		return
 	}
+
+	values.Set("{{.entityAlias}}.after.patch", options.Entity)
+
 	{{if .hasUpdateRelation}} // Cria uma thread que executa a atualizaca das referências.
 	go func() {
-		UpdateRelation{{.entity}}(filter)
+		UpdateRelation{{.entity}}(options)
 	}()	{{end}}
 
 	{{if .beforeResponse}}
@@ -86,7 +94,7 @@ func init() {
 		return
 	}
 	{{end}}
-	resp = filter.Entity
+	resp = options.Entity
 	return`)
 
 	if patchStmtsErr != nil {
@@ -118,6 +126,7 @@ var (
 					"hasUpdateRelation": SR.Has(method.Entity),
 					"preconditions":     parseMethodActions(method.Preconditions),
 					"beforeResponse":    parseMethodActions(method.BeforeResponse),
+					"entityAlias":       getCustom(method.Custom, "go.entity.alias"),
 				}
 			)
 

+ 167 - 0
translate/got/middleware_pipe.go

@@ -0,0 +1,167 @@
+package got
+
+import (
+	"text/template"
+
+	. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common"
+	G "github.com/dave/jennifer/jen"
+)
+
+var (
+	pipeStmtsTmpl *template.Template
+	pipeStmtsErr  error
+)
+
+func init() {
+
+	pipeStmtsTmpl, pipeStmtsErr = ParseTemplate(`
+	{{if .preconditions}}
+		if resp, err = executeAction(
+			ctx,
+			{{.preconditions}},
+		); err != nil {
+			return
+		}
+	{{end}}
+	return`)
+
+	if pipeStmtsErr != nil {
+		panic(pipeStmtsErr)
+	}
+}
+
+var (
+	GenPipeStmts = &Middleware{
+		Id:   "post",
+		Type: "method",
+		Fn: func(ctx *MiddlewareContext) error {
+
+			var (
+				method  = ctx.Method
+				context = map[string]interface{}{
+					"preconditions": parseMethodActions(method.Preconditions),
+				}
+			)
+			// Nome do metodo que verifica se a entidade tem dependencias
+			out, _ := TemplateToString(pipeStmtsTmpl, context)
+			ctx.Statement.Block(G.Id(out)).Line()
+			return nil
+		},
+	}
+)
+
+// var (
+// 	project        = ctx.Project
+// 	method         = ctx.Method
+// 	responseEntity = GenericPart.ReplaceAllString(method.Response, "")
+// )
+
+// if responseEntity == "" {
+// 	panic("Modelo de resposta não definido")
+// }
+
+// ctx.Statement.Block(
+// 	// Declaracao das variaveis
+// 	G.Var().Defs(
+// 		// G.Id("err").Op("*").Qual(API_ERROR, "Error"),
+// 		G.Id("entity").Op("=").Id("New"+method.Entity).Call(),
+// 	).Line(),
+
+// 	// Fazendo o parse do body
+// 	G.If(
+// 		G.Id("err").Op("=").Qual(API_URL, "ReadJson").Call(G.Id("ctx"), G.Id("entity")),
+// 		G.Id("err").Op("!=").Id("nil"),
+// 	).Block(
+// 		// G.Id("api").Dot("Falha").Call(
+// 		// 	G.Id("ctx"),
+// 		// 	G.Id("api").Dot("ErrGeneral"),
+// 		// 	G.Id("err").Dot("Error").Call(),
+// 		// 	G.Nil(),
+// 		// ),
+// 		// G.Return(G.Id("err")),
+// 		G.Return(),
+// 	).Line(),
+
+// 	G.Id("values").Op(":=").Id("ctx").Dot("Values()").Line(),
+
+// 	G.Do(func(part *G.Statement) {
+// 		entity := ctx.Project.GetSchema(method.Entity)
+// 		user := false
+// 		for _, prop := range entity.Properties {
+// 			if def, ok := prop.Autogenerate["create_action"]; ok {
+// 				switch def.Type {
+// 				case "user":
+// 					if !user {
+// 						part.Add(G.Id(`user := values.Get("$user.ref").(*UserReference)`).Line())
+// 						user = true
+// 					}
+// 					part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Id("user").Line())
+// 				case "now":
+// 					part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Qual("time", "Now").Call().Id(".Unix()").Line())
+// 				case "fn":
+// 					part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Id(def.Args[0]).Call().Line())
+// 				default:
+// 					// fmt.Println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
+// 					// spew.Dump(def)
+// 					// parts := strings.Split(def, "#")
+// 					// part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Qual("time", "Now").Call().Id(".Unix()").Line())
+// 				}
+// 			}
+// 		}
+// 	}).Line(),
+
+// 	G.Do(func(s *G.Statement) {
+// 		entity := ctx.Project.EntityDesc(method.Entity)
+// 		if entity != nil && entity.HasMode {
+// 			s.Add(G.Id(`entity.SetMode("create")`))
+// 		}
+// 	}),
+// 	G.If(
+// 		G.Id("err").Op("=").Qual(API_URL, "Validate").Call(G.Id("entity")),
+// 		G.Id("err").Op("!=").Id("nil"),
+// 	).Block(
+// 		// G.Return(G.Id("err")),
+// 		G.Return(),
+// 	).Line(),
+
+// 	// Carrega o filtro do contexto para adicionar a entidade
+// 	G.Id("filter").Op(":=").Id("values").Dot("Get").Call(
+// 		G.Lit("$filter"),
+// 	).Assert(G.Op("*").Qual(API_URL, "Filter")).Line(),
+
+// 	// Adiciona a entidade
+// 	G.Id("filter").Dot("DB").Op("=").Lit(ctx.Project.GetEntityDB(method.Entity)),
+// 	G.Id("filter").Dot("Collection").Op("=").Lit(ctx.Project.GetCollection(method.Entity)),
+// 	G.Id("filter").Dot("Entity").Op("=").Id("entity"),
+// 	generateHookCall(project, method, "beforePersist"),
+// 	// Inseri a entidade na coleção e verifica se a operação ocorreu com exito
+// 	G.If(
+// 		G.List(G.Id("_"), G.Id("err")).Op("=").Id("Models").Dot("InsertOne").Call(
+// 			G.Id("filter"),
+// 		),
+// 		G.Id("err").Op("!=").Id("nil"),
+// 	).Block(
+// 		// G.Id("api").Dot("Falha").Call(
+// 		// 	G.Id("ctx"),
+// 		// 	G.Id("api").Dot("ErrGeneral"),
+// 		// 	G.Id("err").Dot("Error").Call(),
+// 		// 	G.Nil(),
+// 		// ),
+// 		// bson.IsObjectIdHex(m.Id.Hex())
+// 		// G.Return(G.Id("err")),
+// 		G.Return(),
+// 	),
+
+// 	// Envia a resposta pro usuario em caso de sucesso
+// 	// G.Line().Id("ctx").Dot("JSON").Call(G.Qual(APIC, "Map").Values(G.Dict{
+// 	// 	G.Lit("entity"): G.Id("entity"),
+// 	// })),
+// 	// G.Line().Id("resp").Dot("Entity").Op("=").Id("entity"),
+// 	// G.Line().Id("resp").Op("=").Id(responseEntity).Values(G.Dict{
+// 	// 	G.Id("Entity"): G.Id("entity"),
+// 	// }),
+// 	G.Line().Id("resp").Op("=").Id("entity"),
+// 	generateHookCall(project, method, "beforeSend"),
+// 	// G.Return(G.Nil()),
+// 	G.Return(),
+// )

+ 25 - 13
translate/got/middleware_post.go

@@ -18,21 +18,30 @@ func init() {
 	createStmtsTmpl, createStmtsErr = ParseTemplate(`
 	var (
 		entity = models.New{{.entity}}()
+		values = ctx.Values()
 	)
+	values.Set("entity", entity)
+	{{if .entityAlias }} values.Set("{{.entityAlias}}", entity) {{end}}
+
+	{{if .beforeRequestParse}}
+		if _, err = executeAction(
+			ctx,
+			{{.beforeRequestParse}},
+		); err != nil {
+			return
+		}
+	{{end}}
 
 	if err = api.ReadJson(ctx, entity); err != nil {
 		return
 	}
 
-	values := ctx.Values()
-
 	user := values.Get("$user.ref").(*models.UserReference)
 	entity.CreatedBy = user
 	entity.UpdatedBy = user
 	entity.SetMode("create")
 
-	values.Set("entity", entity)
-	
+
 	if err = api.Validate(entity); err != nil {
 		return
 	}
@@ -79,6 +88,7 @@ var (
 		Id:   "post",
 		Type: "method",
 		Fn: func(ctx *MiddlewareContext) error {
+
 			var (
 				project          = ctx.Project
 				method           = ctx.Method
@@ -88,15 +98,17 @@ var (
 				beforeSend       = method.Hook("beforeSend")
 				beforePersist    = method.Hook("beforePersist")
 				context          = map[string]interface{}{
-					"dbName":           dbName,
-					"collectionName":   collectionName,
-					"entity":           method.Entity,
-					"dependenceMethod": dependenceMethod,
-					"beforePersist":    beforePersist,
-					"beforeSend":       beforeSend,
-					"methodName":       strings.Title(method.ID),
-					"preconditions":    parseMethodActions(method.Preconditions),
-					"beforeResponse":   parseMethodActions(method.BeforeResponse),
+					"dbName":             dbName,
+					"collectionName":     collectionName,
+					"entity":             method.Entity,
+					"dependenceMethod":   dependenceMethod,
+					"beforePersist":      beforePersist,
+					"beforeSend":         beforeSend,
+					"methodName":         strings.Title(method.ID),
+					"preconditions":      parseMethodActions(method.Preconditions),
+					"beforeResponse":     parseMethodActions(method.BeforeResponse),
+					"beforeParseRequest": parseMethodActions(method.BeforeParseRequest),
+					"entityAlias":        getCustom(method.Custom, "go.entity.alias"),
 				}
 			)
 			// Nome do metodo que verifica se a entidade tem dependencias

+ 1 - 1
translate/got/middleware_undelete.go

@@ -44,7 +44,7 @@ func init() {
 	options := values.Get("$filter").(*api.Filter)
 	options.DB = "{{.dbName}}"
 	options.Collection = "{{.collectionName}}"
-	options.Patchs = patchs.Patch()
+	options.Patchs, options.Options = patchs.Patch()
 
 	if _, err = models.Api.PatchOne(options); err != nil {
 		return

+ 2 - 2
translate/got/middleware_update_relation.go

@@ -17,6 +17,7 @@ var (
 func init() {
 	updateRelationStmtsTmpl, updateRelationStmtsErr = ParseTemplate(`
 	func UpdateRelation{{.entity}}(options *api.Filter) {
+		time.Sleep(time.Second * 1)
 		var (
 			filter *api.Filter
 			err    *errs.Error
@@ -44,11 +45,10 @@ func init() {
 			}
 			
 			if _, err = models.Api.PatchMany(filter); err != nil {
-				fmt.Println(err.Error())
+				fmt.Println("[WARNING]", err.Error())
 			}
 		}()
 		{{end}}
-
 		wg.Wait()
 	}`)
 

+ 55 - 3
translate/got/resources.go

@@ -34,6 +34,7 @@ var (
 		"patch":     GenPatchStmts,
 		"delete":    GenDeleteStmts,
 		"get_one":   GenGetStmtsOne,
+		"pipe":      GenPipeStmts,
 		"filter":    GenGetFilter,
 		"get_list":  GenGetStmtsList,
 		"undelete":  GenUndeleteStmts,
@@ -56,7 +57,9 @@ func InitFile(file *G.File, resource *Resource, p *Project) {
 	}
 
 	file.Var().Defs(
+		G.Id(fmt.Sprintf("_%s_spew_%d_ =", resource.Entity, now)).Qual("github.com/davecgh/go-spew/spew", "Dump"),
 		G.Id(fmt.Sprintf("_%s_bson_%d_ *", resource.Entity, now)).Qual(BSON, "M"),
+		G.Id(fmt.Sprintf("_%s_time_%d_ *", resource.Entity, now)).Qual("time", "Duration"),
 		G.Id(fmt.Sprintf("_%s_error_%d_ *", resource.Entity, now)).Qual(API_ERROR, "Error"),
 		G.Id(fmt.Sprintf("_%s_sync_%d_ *", resource.Entity, now)).Qual("sync", "WaitGroup"),
 		G.Id(fmt.Sprintf("_%s_primitive_%d_ *", resource.Entity, now)).Qual("go.mongodb.org/mongo-driver/bson/primitive", "ObjectID"),
@@ -81,7 +84,12 @@ func CreateDummy(p *Project, resource *Resource, method *Method) error {
 	// Verifica se existe um arquivo na pasta de include.
 	// Caso o arquivo não exista um novo arquivo é criado.
 
-	outputfile := fmt.Sprintf("%s/include/go/%s_%s.go", CurrentDirectory, strings.ToLower(resource.ID), strings.ToLower(method.ID))
+	// outputfile := fmt.Sprintf("%s/include/go/api_%s_%s.go", CurrentDirectory, strings.ToLower(resource.ID), strings.ToLower(method.ID))
+	outputfile := fmt.Sprintf(
+		"../project/include/go/api_%s_%s_gen.go",
+		strings.ToLower(resource.ID),
+		strings.ToLower(method.ID),
+	)
 
 	if _, err := os.Stat(outputfile); os.IsNotExist(err) {
 
@@ -277,9 +285,16 @@ func GenIndexApi(p *Project) error {
 
 	// Inicializa o mapa de filtros da api
 	Index.Id(`
+		import(
+			// "reflect"
+			// "runtime"
+			// "strings"
+			"fmt"
+		)
 		var (
 			filtersApiReference = map[string]*`).Qual(CODE_GEN_V2_COMMON, "ApiFilter").Id(`{}
 			FormatSelection = map[string]string{}
+			Debug = api.NewDebug()
 		)
 		func init(){
 			var (
@@ -300,11 +315,39 @@ func GenIndexApi(p *Project) error {
 					filtersApiReference[entity.Id] = entity
 				}
 			}
+
+		}
+
+		type Action struct {
+			Name string
+			Fn func(context.Context) (interface{}, *`).Qual(API_ERROR, "Error").Id(`)
 		}
 
-		func executeAction(ctx context.Context, actions ...func(context.Context) (interface{}, *`).Qual(API_ERROR, "Error").Id(`)) (resp interface{},err *errs.Error){
+		func executeAction(ctx context.Context, actions []Action) (resp interface{},err *errs.Error){
+			var (
+				stopPropagation bool
+				// parts []string
+				event *api.DebugEvent
+			)
+			debug, debugActive := ctx.Values().Get("#debug").(*api.DebugTaks);
+			
 			for _, action := range actions {
-				if resp, err = action(ctx); err != nil || resp != nil {
+				
+				if debugActive {
+					fmt.Println("\texecuteAction -> ", action.Name)
+					event = debug.Event("execute.action", action.Name)
+				}
+
+				resp, err = action.Fn(ctx)
+		
+				if debugActive {
+					event.Data = resp
+					event.Error = err
+				}
+
+				stopPropagation,_ = ctx.Values().GetBool("stop.propagation")
+				switch {
+				case stopPropagation, err != nil, resp != nil:
 					return
 				}
 			}
@@ -321,6 +364,7 @@ func GenIndexApi(p *Project) error {
 			args := []G.Code{
 				G.Lit(method.HttpMethod),
 				G.Lit(p.GetPath(method)),
+				G.Line().Id("Debug.Handler()"),
 			}
 
 			middlewares = []string{}
@@ -401,6 +445,10 @@ func GenIndexApi(p *Project) error {
 		),
 	))
 
+	statments = append(statments, G.Line().Comment("Debug eventstream").Line().Id("app").Dot("Get").Call(
+		G.Lit("/api/v1/debug"),
+		G.Id(`apply("debug", Debug.EventStream())`),
+	))
 	// Cria a funcao que registra as urls da api no arquivo api_index_gen.go
 	Index.Func().Id("Register").Params(
 		G.Id("app").Op("*").Qual(IRIS, "Application"),
@@ -476,6 +524,10 @@ func generateActionsFiles(p *Project, f *G.File, r *Resource, method *Method) {
 		actions = append(actions, method.BeforeResponse...)
 	}
 
+	if method.BeforeParseRequest != nil {
+		actions = append(actions, method.BeforeParseRequest...)
+	}
+
 	for _, action := range actions {
 
 		path := fmt.Sprintf(

+ 196 - 18
translate/got/schemas.go

@@ -23,9 +23,24 @@ var (
 	// float32 float64
 	// complex64 complex128
 
-	primitiveRegex = regexp.MustCompile(`^(bool|string|u?int\d{0,2}|byte|rune|float\d{0,2}|complex\d{2,3})$`)
+	primitiveRegex = regexp.MustCompile(`^(bool|string|u?int\d{0,2}|byte|rune|float\d{0,2}|interface|complex\d{2,3})$`)
 )
 
+func hasReplaceMethod(typ string) bool{ 
+	typ = strings.Replace(typ, "*","", 1)
+	return !primitiveRegex.MatchString(typ)
+}
+
+func replaceAccessType(typ string) (accessType string) {
+	typ = strings.Replace(typ, "*","", 1)
+	if strings.Contains(typ, "map"){
+		accessType = "map"
+	} else {
+		accessType = "id"
+	}
+	return
+}
+
 func GenSchemas(p *Project) error {
 	var (
 		// requiredOnCreate bool
@@ -43,6 +58,7 @@ func GenSchemas(p *Project) error {
 		// values                  G.Dict
 		cproperties         map[string]*G.Statement
 		addUpdate           map[string]string
+		replaceUpdate       map[string]map[string]string
 		removeUpdate        map[string]map[string]string
 		entityName          string
 		entityPatchName     string
@@ -66,6 +82,10 @@ func GenSchemas(p *Project) error {
 		return err
 	}
 
+	if err = GenEntityHelpers(p); err != nil {
+		return err
+	}
+
 	for _, entity := range entities {
 
 		entityInfo = p.ResponseEntity(entity.ID)
@@ -74,7 +94,7 @@ func GenSchemas(p *Project) error {
 			continue
 		}
 
-		inputName = ""
+		inputName = entity.ID
 		entityPatchName = ""
 		hasAddOrRemove = false
 		// requiredOnCreate = false
@@ -121,6 +141,7 @@ func GenSchemas(p *Project) error {
 		referenceProperties = G.Statement{}
 
 		addUpdate = map[string]string{}
+		replaceUpdate = map[string]map[string]string{}
 		removeUpdate = map[string]map[string]string{}
 
 		for _, meta := range entity.Properties {
@@ -133,9 +154,7 @@ func GenSchemas(p *Project) error {
 
 			propName = strings.Title(meta.ID)
 			propertie = G.Id(propName)
-
 			meta.FillTags(p, propName)
-
 			posfix = ""
 			// Registra a relaao entre as entidades
 			if meta.Relation {
@@ -171,6 +190,22 @@ func GenSchemas(p *Project) error {
 
 					patchProperties = append(patchProperties, extend)
 
+					if hasReplaceMethod(typ) {
+						extName = "Replace" + propName
+
+						replaceUpdate[extName] = map[string]string{
+							"name": meta.ID,
+							"type": replaceAccessType(typ),
+						}
+
+						extend = G.Id(extName).Index().Id(ConvType(typ)).Tag(map[string]string{
+							"json": extName + ",omitempty",
+							"bson": extName + ",omitempty",
+						})
+						patchProperties = append(patchProperties, extend)
+					}
+
+
 					extName = "Remove" + propName
 					removeUpdate[extName] = map[string]string{
 						"type":   ConvType(typ),
@@ -397,21 +432,27 @@ func GenSchemas(p *Project) error {
 
 			f.Func().Params(
 				G.Id("t").Op("*").Id(entityPatchName),
-			).Id("Patch").Params().Op("*").Qual(BSON, "A").Block(
-				G.Id("updt").Op(":=").Qual(BSON, "A").Values(
-					G.Qual(BSON, "M").Values(G.Lit("$set").Id(": t.Set")),
-				).Line(),
+			).Id("Patch").Params().Params(
+				G.Op("*").Qual(BSON, "A"),
+				G.Op("*").Qual("go.mongodb.org/mongo-driver/mongo/options", "UpdateOptions"),
+			).Block(
+				
+				G.Id("updt").Op(":=").Qual(BSON, "A").Values(),
+				G.Id("patchOptions := ").Qual("go.mongodb.org/mongo-driver/mongo/options","Update").Call(),
+
 				G.Do(func(y *G.Statement) {
 					if !hasAddOrRemove {
 						return
 					}
 					stmts := G.Statement{
 						G.Id("hasAdd").Op(":=").Lit(0).Line(),
+						G.Id("hasReplace").Op(":=").Lit(0).Line(),
 						G.Id("hasRemove").Op(":=").Lit(0).Line(),
 						G.Id("pull").Op(":=").Qual(BSON, "M").Values().Line(),
 						G.Id("push").Op(":=").Qual(BSON, "M").Values().Line(),
+						G.Id("dotNotation := map[string]interface{}{}").Line(),
+						G.Id("arrayFilters := []interface{}{}").Line(),
 					}
-
 					// add entitys
 					// spew.Dump(addUpdate)
 					for prop, value := range addUpdate {
@@ -422,7 +463,39 @@ func GenSchemas(p *Project) error {
 							}),
 						).Line())
 					}
-					// spew.Dump(removeUpdate)
+					// replace entitys
+					// spew.Dump(addUpdate)
+					for prop, value := range replaceUpdate {
+						prop = fmt.Sprintf("t.%s", prop)
+
+						stmts = append(stmts, G.If(G.Len(G.Id(prop)).Op(">").Lit(0)).Block(
+							G.Id("hasReplace").Op("++"),
+							G.Do(func(replace *G.Statement) {
+								name := value["name"]
+								typi := value["type"]
+								arrayFiltersTemplates := map[string]string{
+									"id": `arrayFilters = append(arrayFilters, bson.M{
+											api.Format("%s._id", key): value.Id,
+										})`,
+									"map": `arrayFilters = append(arrayFilters, bson.M{
+											api.Format("%s._id", key): value["_id"],
+										})`,
+								}
+								arrayFilters := arrayFiltersTemplates[typi]
+								template := fmt.Sprintf(`for idx, value := range %s {
+									key := api.Format("%s%%d", idx)
+									dotNotation[api.Format("%s.$[%%s]", key)] = value
+									%s
+								}`,
+								prop,
+								name,
+								name,
+								arrayFilters,
+								)
+								replace.Add(G.Id(template))
+							}),
+						).Line())
+					}
 					// remove entitys
 					for prop, value := range removeUpdate {
 						stmts = append(stmts, G.If(G.Len(G.Id("t").Dot(prop)).Op(">").Lit(0)).Block(
@@ -441,7 +514,6 @@ func GenSchemas(p *Project) error {
 										}),
 									}
 								}
-
 								hasRemove.Id("pull").Index(G.Lit(value["propId"])).Op("=").Qual(BSON, "M").Values(assign)
 							}),
 						).Line())
@@ -471,18 +543,43 @@ func GenSchemas(p *Project) error {
 
 						// .Index(G.Lit("$push")).Op("=").Id("push"),
 					).Line())
-
+					
 					stmts = append(stmts, G.If(G.Id("hasRemove").Op(">").Lit(0)).Block(
-						// G.Id("updt").Index(G.Lit("$pull")).Op("=").Id("pull"),
-						G.Id("updt").Op("=").Append(
-							G.Id("updt"),
-							G.Qual(BSON, "M").Values(G.Lit("$pull").Id(": pull")),
-						),
+					// G.Id("updt").Index(G.Lit("$pull")).Op("=").Id("pull"),
+					G.Id("updt").Op("=").Append(
+						G.Id("updt"),
+						G.Qual(BSON, "M").Values(G.Lit("$pull").Id(": pull")),
+					),
+					).Line())
+						
+					stmts = append(stmts, G.If(G.Id("hasReplace").Op(">").Lit(0)).Block(
+						G.Id(fmt.Sprintf(`if t.Set == nil {
+								t.Set = &%s{}
+							}
+							t.Set.DotNotation = dotNotation
+							
+							patchOptions.SetArrayFilters(options.ArrayFilters{
+								Filters: arrayFilters,
+							})
+							`, 
+							inputName,
+						)),
 					).Line())
 
 					y.Add(stmts...)
 				}),
-				G.Return(G.Op("&").Id("updt")),
+
+				G.Id(`if t.Set != nil {
+					updt = append(updt, bson.M{"$set": t.Set})
+				}`).Line(),
+
+				// []interface{}{bson.D{
+				// 	{"elem._id", 1},
+				// }}
+				G.Return(
+					G.Op("&").Id("updt"),
+					G.Id("patchOptions"),
+				),
 			)
 
 		}
@@ -793,3 +890,84 @@ func generateIndexsEntity(file *G.File, entity *Entity) {
 	}
 
 }
+
+func GenEntityHelpers(project  *Project) error {
+
+	file := G.NewFile("models")
+	entities, err := Schemas(project)
+	if err != nil {
+		return err
+	}
+
+	file.Add(G.Id(`
+	import(
+		"reflect"
+		"git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
+		context "github.com/kataras/iris/v12/context"
+		"go.mongodb.org/mongo-driver/bson"
+		"go.mongodb.org/mongo-driver/bson/primitive"
+	)
+	`))
+
+	getStmtsTmpl, getStmtsErr := ParseTemplate(`
+	func Get{{.entity}}ByID(
+		ctx context.Context,
+		id interface{},
+	) (
+	   entity *{{.entity}}, 
+	   err *errs.Error,
+	) {
+		var errd error
+		entity = &{{.entity}}{}
+		value := reflect.ValueOf(id)
+
+		if value.Kind() == reflect.Ptr  {
+			id = value.Elem() 
+		}
+
+		if idString, ok := id.(string); ok {
+			if id, errd = primitive.ObjectIDFromHex(idString); errd != nil {
+				return
+			}
+		}
+
+		options := Filter{{.entity}}Options(ctx)
+		options.Query = &bson.M{"_id": id}
+		options.Entity = entity
+		
+		if _, err = Api.FindOne(options); err != nil {
+			return
+		}
+		return
+	}
+	`)
+
+	if getStmtsErr != nil {
+		return getStmtsErr
+	}
+
+	for _, entity := range entities {
+
+		entityInfo := project.ResponseEntity(entity.ID)
+
+		if entityInfo.IsGeneric || entity.Type != "object" {
+			continue
+		}
+
+		context := map[string]interface{}{
+			"entity": strings.Title(entity.ID),
+		}
+		out, _ := TemplateToString(getStmtsTmpl, context)
+		file.Add(G.Id(out).Line())
+	}
+	
+	if err := Write(
+		fmt.Sprintf("../project/include/go/models/get_helpers.go"),
+		file,
+	); err != nil {
+		return err
+	}
+
+	return nil
+}
+

+ 17 - 12
translate/got/translate.go

@@ -2,6 +2,7 @@ package got
 
 import (
 	"fmt"
+	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -61,6 +62,7 @@ func Translate(p *Project) (err error) {
 func createSymbolicLinks(basedir string) func(string, os.FileInfo, error) error {
 	var (
 		callback func(string, os.FileInfo, error) error
+		input    []byte
 		relative = func(path string) string {
 			return fmt.Sprintf("%s/%s%s", OutputDirectory, "v1", strings.Replace(filepath.Dir(path), basedir, "", 1))
 		}
@@ -84,7 +86,21 @@ func createSymbolicLinks(basedir string) func(string, os.FileInfo, error) error
 
 		} else {
 			// fmt.Println("crinado link symbolico aqui", relative(pathX))
-			os.Symlink(pathX, filepath.Join(relative(pathX), infoX.Name()))
+			// os.Symlink(pathX, filepath.Join(relative(pathX), infoX.Name()))
+
+			if input, err = ioutil.ReadFile(pathX); err != nil {
+				return
+			}
+
+			// fmt.Println("READ FILE", pathX, filepath.Join(relative(pathX), infoX.Name()))
+
+			if err = ioutil.WriteFile(
+				filepath.Join(relative(pathX), infoX.Name()),
+				input,
+				0644,
+			); err != nil {
+				return
+			}
 
 			// fmt.Printf("  dir ------ : 「%v」\n", pathX)
 			// fmt.Printf("  dir: 「%v」\n", strings.Replace(filepath.Dir(pathX), basedir, "", 1))
@@ -111,17 +127,6 @@ func include(p *Project) error {
 	if err = filepath.Walk(basedir, createSymbolicLinks(basedir)); err != nil {
 		return err
 	}
-
-	// args := fmt.Sprintf("ln -st %s/include/go/* %s/%s/", CurrentDirectory, OutputDirectory, p.Package)
-	// args := fmt.Sprintf("ln -st %s/include/go/* %s/%s/", CurrentDirectory, OutputDirectory, p.Package)
-	// command := exec.Command("sh", "-c", args)
-	// var out bytes.Buffer
-	// var out []byte
-	// // command.Stdout = &out
-	// // if err = command.Run(); err != nil {
-	// if out, err = command.Output(); err != nil {
-	// 	fmt.Println("exec command error ", err, args, string(out))
-	// }
 	return err
 }
 

+ 27 - 0
translate/tst/build.go

@@ -0,0 +1,27 @@
+package tst
+
+import (
+	"fmt"
+
+	. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common"
+)
+
+var Build = func(project *Project, buildOptions *BuildOptions) (fn *BuildSet, err error) {
+	var (
+		before = []*Command{}
+	)
+	// path := fmt.Sprintf("%s", ts.OutputDir, project.ID)
+	outputClientDir := fmt.Sprintf("%s/", project.Client("angular6").OutputDir)
+
+	fn = &BuildSet{
+		Before: before,
+		After: []*Command{
+			{
+				Id:          "format_ts",
+				Cmd:         fmt.Sprintf("tsfmt -r %s*.ts", outputClientDir),
+				Description: "Formata todos os arquivos ts",
+			},
+		},
+	}
+	return
+}

+ 142 - 61
translate/tst/resources.go

@@ -40,14 +40,16 @@ func GenResources(p *Project) {
 	// 	),
 	// ).Line()
 	module.Line().Raw(`
-	
-	export interface HttpOptions {
+	export type HttpOptions = {
 		headers?: HttpHeaders;
 		params?: any;
 		withCredentials?: boolean;
 		id?: string;
-	}
-	
+	} & {
+		[prop: string]: any;
+	};
+	  
+
 	export class ServiceStmt {
 		
 		// constructor(protected api: `).Id(ApiClassName(p)).Raw(`) {}
@@ -65,7 +67,7 @@ func GenResources(p *Project) {
 			return u;
 		}
 	
-		_event(url, opt: HttpOptions): Observable<EventSourcePolyfill> {
+		_event<T = any>(url, opt: HttpOptions): Observable<EventSourcePolyfill> {
 			opt = this.api.options(opt);
 			const _url = new URL(this.Url(url, opt)), { params = null } = opt;
 			if (params) {
@@ -77,29 +79,29 @@ func GenResources(p *Project) {
 			))).pipe(take(1));
 		}
 		
-		_get(url, opt: HttpOptions): Observable<ServiceResponse> {
+		_get<T = any>(url, opt: HttpOptions):Observable<T> {
 			opt = this.api.options(opt);
-			return this.api.http.get<ServiceResponse>(this.Url(url, opt), opt).pipe(take(1));
+			return this.api.http.get<any>(this.Url(url, opt), opt).pipe(take(1));
 		}
 	
-		_put(url, entity: any, opt: HttpOptions): Observable<ServiceResponse> {
+		_put<T = any>(url, entity: any, opt: HttpOptions):Observable<T> {
 			opt = this.api.options(opt);
-			return this.api.http.put<ServiceResponse>(this.Url(url, opt), entity, opt).pipe(take(1));
+			return this.api.http.put<any>(this.Url(url, opt), entity, opt).pipe(take(1));
 		}
 		
-		_patch(url, entity: any, opt: HttpOptions): Observable<ServiceResponse> {
+		_patch<T = any>(url, entity: any, opt: HttpOptions):Observable<T> {
 			opt = this.api.options(opt);
-			return this.api.http.patch<ServiceResponse>(this.Url(url, opt), entity, opt).pipe(take(1));
+			return this.api.http.patch<any>(this.Url(url, opt), entity, opt).pipe(take(1));
 		}
 	
-		_post(url, entity: any, opt: HttpOptions): Observable<ServiceResponse> {
+		_post<T = any>(url, entity: any, opt: HttpOptions):Observable<T> {
 			opt = this.api.options(opt);
-			return this.api.http.post<ServiceResponse>(this.Url(url, opt), entity, opt).pipe(take(1));
+			return this.api.http.post<any>(this.Url(url, opt), entity, opt).pipe(take(1));
 		}
 	
-		_delete(url, opt: HttpOptions): Observable<ServiceResponse> {
+		_delete<T = any>(url, opt: HttpOptions) {
 			opt = this.api.options(opt);
-			return this.api.http.delete<ServiceResponse>(this.Url(url, opt), opt).pipe(take(1));
+			return this.api.http.delete<any>(this.Url(url, opt), opt).pipe(take(1));
 		}
 	}`).Line()
 
@@ -180,7 +182,8 @@ func GenResources(p *Project) {
 
 		module.Line().Export().Class().Id(
 			angularResourceName,
-		).Extends().Id(ServiceStmt).Block(
+		// ).Extends().Id(fmt.Sprintf("%s<%s>",ServiceStmt, resource.Entity )).Block(
+		).Extends().Id(fmt.Sprintf("%s",ServiceStmt )).Block(
 			angularServiceStmtMethods...,
 		).Line()
 
@@ -209,11 +212,11 @@ func createAngularService(p *Project, module *TS.File) error {
 
 	// Define a class que representa uma resposta da api
 	// "Class gerencia todos os serviços da api.",
-	module.Export().Interface().Id("ServiceResponse").Block(
+	module.Export().Interface().Id("ServiceResponse<T = any>").Block(
 		TS.Raw("resultSizeEstimate: number;"),
 		TS.Raw("nextPageToken: string;"),
-		TS.Raw("entity?:object;"),
-		TS.Raw("itens?:object[];"),
+		TS.Raw("entity?:T;"),
+		TS.Raw("itens?:T[];"),
 	)
 
 	// Define todos os parametros atribuiveis as opcoes de uma requisicao da api
@@ -271,6 +274,57 @@ func createAngularService(p *Project, module *TS.File) error {
 			// }
 		}
 
+		// options(opts?: HttpOptions): HttpOptions {
+		// 	console.log({ ...this.httpOptions.headers });
+		// 	opts = Object.assign(
+		// 	  {
+		// 		headers: new HttpHeaders({
+		// 		  ...this.httpOptions.headers,
+		// 		}),
+		// 		params: {},
+		// 	  },
+		// 	  opts
+		// 	);
+		
+		// 	// const headers = {
+		// 	//   Authorization: this.Auth.Token(),
+		// 	// };
+		
+		// 	opts.headers.set("Authorization", this.Auth.Token());
+		
+		// 	//this.copyHeader(this.httpOptions.headers, headers);
+		// 	//this.copyHeader(opts.headers, headers);
+		
+		// 	const params = opts.params;
+		// 	// Converte a query para base64
+		// 	if (params.q) {
+		// 	  params.q = window.btoa(unescape(encodeURIComponent(params.q)));
+		// 	}
+		
+		// 	// delete opts.headers;
+		// 	// delete opts.params;
+		
+		// 	// const options = Object.assign(
+		// 	//   {
+		// 	//     headers: new HttpHeaders(headers),
+		// 	//     params: new HttpParams({ fromObject: params }),
+		// 	//   },
+		// 	//   opts
+		// 	// );
+		
+		// 	// const options = Object.assign({
+		// 	// 	headers: new HttpHeaders(hopts),
+		// 	// 	params: {}
+		// 	// }, opts);
+		
+		// 	// // Converte a query para base64
+		// 	// if (options.params.q) {
+		// 	// 	options.params.q = window.btoa(unescape(encodeURIComponent(options.params.q)));
+		// 	// }
+		
+		// 	return opts;
+		//   }
+		  
 		options(opts?: HttpOptions): HttpOptions {
 
 			opts = Object.assign({
@@ -438,46 +492,6 @@ func createClientClass(p *Project, file *TS.File) {
 
 }
 
-// func GenAngularMethod(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) {
-// 	params := []TS.CodeInterface{}
-// 	param := "entity"
-// 	methodId := method.ID
-
-// 	callOptionsParam := ""
-
-// 	switch strings.ToLower(method.HttpMethod) {
-// 	case "post":
-// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
-// 	case "filter":
-// 		// methodId = "get"
-// 		param = ""
-// 	case "get":
-// 		param = ""
-// 		// params = append(params, TS.Id(param).Op(":").String())
-// 	case "get_list":
-// 		param = ""
-// 		// params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
-// 	case "patch":
-// 		param = ""
-// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
-// 		// params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
-// 	case "put":
-// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
-// 		callOptionsParam = "{id: entity.id}"
-// 	case "delete":
-// 		// params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
-// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
-// 		callOptionsParam = "{id: entity.id}"
-// 		param = ""
-// 	default:
-// 		panic(fmt.Sprintf("O método http '%s' para o recurso %s não foi definido!", method.HttpMethod, method.ID))
-// 	}
-
-// 	// *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
-// 	*methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
-// 		TS.Return().This().Dot("options").Call(TS.Raw(callOptionsParam)).Dot(methodId).Call(TS.Id(param)).Endl(),
-// 	).Line())
-// }
 
 func GenAngularMethodStmt(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) {
 
@@ -536,8 +550,21 @@ func GenAngularMethodStmt(p *Project, r *Resource, method *Method, methods *[]TS
 		panic(fmt.Sprintf("Method '%s' template not defined!", template))
 	}
 
+	path := method.Path
+	for _, parameter := range method.Parameters {
+		
+	 alias := getCustom(parameter.Custom, "ts.api.alias")
+	 if aliasString, ok := alias.(string);ok {
+		path = strings.ReplaceAll(
+			path,
+			fmt.Sprintf("{%s}", parameter.ID),
+			fmt.Sprintf("{%s}", aliasString),
+		)
+	 }
+	}
+	
 	// paramsHttp = append(paramsHttp, TS.Raw("this.Url(url, opt.params)"))
-	paramsHttp = append(paramsHttp, TS.Raw(fmt.Sprintf("`${this.api.apiOptions.BASE_URL}%s`", method.Path)))
+	paramsHttp = append(paramsHttp, TS.Raw(fmt.Sprintf("`${this.api.apiOptions.BASE_URL}%s`", path)))
 
 	switch typ {
 	case "post", "put", "patch":
@@ -547,11 +574,65 @@ func GenAngularMethodStmt(p *Project, r *Resource, method *Method, methods *[]TS
 
 	paramsHttp = append(paramsHttp, TS.Id("opt"))
 
+	response := strings.Replace(method.Response, "*", "",-1)
+	
+	if response == ""  {
+		response = method.Entity
+	}
+
 	// *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
 	*methods = append(*methods, TS.Id(method.ID).Params(params...).Block(
 		// TS.Raw("let opt = this.api.Options(this.options)").Endl(),
 		// TS.Raw("let url = ").Lit(p.GetUrlFromMethod(method)).Endl(),
 		// TS.Raw("let url = ").Raw(fmt.Sprintf("`${this.api.BASE_URL}%s`", method.Path)).Endl(),
-		TS.Line().Raw(fmt.Sprintf("return this._%s", typ)).Call(paramsHttp...).Endl(),
+		TS.Line().Raw(
+			fmt.Sprintf(
+				"return this._%s<%s>",
+				 typ,
+				response,
+			),
+		).Call(paramsHttp...).Endl(),
+		// TS.Line().Raw(fmt.Sprintf("return this._%s", typ )).Call(paramsHttp...).Endl(),
 	).Line())
 }
+
+// func GenAngularMethod(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) {
+// 	params := []TS.CodeInterface{}
+// 	param := "entity"
+// 	methodId := method.ID
+
+// 	callOptionsParam := ""
+
+// 	switch strings.ToLower(method.HttpMethod) {
+// 	case "post":
+// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
+// 	case "filter":
+// 		// methodId = "get"
+// 		param = ""
+// 	case "get":
+// 		param = ""
+// 		// params = append(params, TS.Id(param).Op(":").String())
+// 	case "get_list":
+// 		param = ""
+// 		// params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
+// 	case "patch":
+// 		param = ""
+// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
+// 		// params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
+// 	case "put":
+// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
+// 		callOptionsParam = "{id: entity.id}"
+// 	case "delete":
+// 		// params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
+// 		params = append(params, TS.Id(param).Op(":").Id(method.Entity))
+// 		callOptionsParam = "{id: entity.id}"
+// 		param = ""
+// 	default:
+// 		panic(fmt.Sprintf("O método http '%s' para o recurso %s não foi definido!", method.HttpMethod, method.ID))
+// 	}
+
+// 	// *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
+// 	*methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
+// 		TS.Return().This().Dot("options").Call(TS.Raw(callOptionsParam)).Dot(methodId).Call(TS.Id(param)).Endl(),
+// 	).Line())
+// }

+ 38 - 206
translate/tst/schemas.go

@@ -27,10 +27,6 @@ func GenSchemas(p *Project) error {
 
 		entityInfo = p.ResponseEntity(entity.ID)
 
-		if entityInfo.IsGeneric {
-			continue
-		}
-
 		for _, meta := range entity.Properties {
 
 			propName = strings.Title(meta.ID)
@@ -76,9 +72,13 @@ func GenSchemas(p *Project) error {
 			}
 
 			if attribName != "" {
-
+				optional := "?"
+				// if getCustom(meta.Custom, "ts.optional").(bool) {
+				// if !meta.Required {
+				// 	options = "?"
+				// }
 				// attrib.Add(TS.Public().Id(attribName).Op(":").Id(TS.ConvType(meta.Type)))
-				attrib.Add(TS.Id(attribName).Op(":").Id(TS.ConvType(meta.Type)))
+				attrib.Add(TS.Id(attribName).Id(optional).Op(":").Id(TS.ConvType(meta.Type)))
 				if meta.Array {
 					attrib.Add(TS.Index())
 				}
@@ -88,14 +88,18 @@ func GenSchemas(p *Project) error {
 		} // Fim do loop dos atributos
 
 		// attribs = append(attribs, TS.Constructor(TS.Raw("opts: any")).Block(TS.Raw("opts && Object.assign(this,opts);")))
+		unshift := []TS.CodeInterface{TS.Id(`constructor(entity?:any){ if(entity){Object.assign(this, entity);}}`)}
+
+		attribs = append(unshift, attribs...)
 
 		module.Line().Export().Class().Id(strings.Title(entity.ID)).Block(
 			// module.Line().Export().Interface().Id(strings.Title(entity.ID)).Block(
-			// TS.Constructor(TS.Id(`options: `)),
 			attribs...,
 		).Line()
 
-		createPathClass(entity, arrayProperties)
+		if !entityInfo.IsGeneric {
+			createPathClass(entity, arrayProperties)
+		}
 
 	} // Fim do for de schema
 	return nil
@@ -158,7 +162,7 @@ func createSetInitializeMethod(g *TS.Group, entity *Entity, arrayProperties []*P
 
 			for _, meta := range arrayProperties {
 				// block.Add(TS.Raw(fmt.Sprintf("if ($instance.%s) { this._set(this.%s, $instance.%s, '',-1);}", meta.ID, meta.ID, meta.ID)).Line())
-				block.Add(TS.Raw(fmt.Sprintf("if (%s.length) { this._set(this.%s, %s, '',-1);}", meta.ID, meta.ID, meta.ID)).Line())
+				block.Add(TS.Raw(fmt.Sprintf("if (%s && %s.length) { this._set(this.%s, %s, '',-1);}", meta.ID, meta.ID, meta.ID, meta.ID)).Line())
 			}
 
 		}),
@@ -178,7 +182,9 @@ func createPatchMethod(g *TS.Group, entity *Entity, arrayProperties []*Propertie
 					Line().
 					Raw(fmt.Sprintf("Add%s : [],", strings.Title(meta.ID))).
 					Line().
-					Raw(fmt.Sprintf("Remove%s : [],", strings.Title(meta.ID))),
+					Raw(fmt.Sprintf("Remove%s : [],", strings.Title(meta.ID))).
+					Line().
+					Raw(fmt.Sprintf("Replace%s : [],", strings.Title(meta.ID))),
 				)
 			}
 
@@ -211,6 +217,12 @@ func createAddAndRemoveMethods(g *TS.Group, entity *Entity, arrayProperties []*P
 			// Params(TS.Id("itens"), TS.Id("timeout")).
 			Block(
 				TS.Raw(fmt.Sprintf("this._set(this.%s, itens, 'remove', timeout);return this;", meta.ID)),
+			).
+			Line().
+			Id(fmt.Sprintf("Replace%s", strings.Title(meta.ID))).
+			Params(TS.Id("itens"), TS.Id(fmt.Sprintf("timeout = %d", timeout))).
+			Block(
+				TS.Raw(fmt.Sprintf("this._set(this.%s, itens, 'replace', timeout);return this;", meta.ID)),
 			),
 		)
 	}
@@ -239,16 +251,13 @@ func appendBasePatchClass() {
 			this.$subscription = setTimeout(() => this.change$.emit(patch), timeout);
 		}
 
-		protected __set(list, add, rem) {
+		protected __set(list, add, rem, replace) {
 			list.forEach(element => {
-				const item = element.item;
+				const {item} = element;
 				switch (element.action) {
-					case 'add':
-					add.push(item);
-					break;
-					case 'remove':
-					if (element.type !== 'new') { rem.push(item._id || item); } 
-					break;
+					case 'add': add.push(item);	break;
+					case 'replace': replace.push(item);	break;
+					case 'remove': if (element.type !== 'new') { rem.push(item._id || item); }
 				}
 			});
 		}
@@ -273,10 +282,17 @@ func appendBasePatchClass() {
 					, el = _map.get(id);
 		
 				switch (action) {
+					case 'replace': 
+					_map.set(id, {
+						item,
+						action: 'replace',
+						type: 'new',
+					});
+					break;
 					case 'add':
 					if (!has) {
 						_map.set(id, {
-						item: item,
+						item,
 						action: 'add',
 						type: 'new',
 						});
@@ -317,15 +333,15 @@ func appendBasePatchClass() {
 				if (prop.startsWith('$')) {
 					return;
 				}
-				const adds = [], remove = [];
+				const adds = [],replace = [],remove = [];
 				const captalized = prop[0].toUpperCase() + prop.slice(1);
 				
 				if ($this[prop].size) {
 
-					this.__set($this[prop], adds, remove);
+					this.__set($this[prop], adds, remove, replace);
 					
 					if (adds.length){ patchs[`).Raw("`Add${captalized}`").Raw(`] = adds; }
-					
+					if (replace.length){ patchs[`).Raw("`Replace${captalized}`").Raw(`] = replace; }
 					if (remove.length){ patchs[`).Raw("`Remove${captalized}`").Raw(`] = remove; }
 				}
 			});
@@ -335,187 +351,3 @@ func appendBasePatchClass() {
 	}
 	`)
 }
-
-// 	public Patchs() {
-
-// 	  const patchs = {
-// 		AddMethods: [],
-// 		AddSamples: [],
-// 		AddCaracterizations: [],
-// 		RemoveMethods: [],
-// 		RemoveSamples: [],
-// 		RemoveCaracterizations: [],
-// 		$set: this.$set,
-// 	  };
-
-// 	  console.log('paths', this);
-
-// 	  this.__set(this._samples, patchs.AddSamples, patchs.RemoveSamples);
-// 	  this.__set(this._methods, patchs.AddMethods, patchs.RemoveMethods);
-
-// 	  patchs.RemoveSamples.map(s => {
-// 		this.RemoveCaracterizations(this._form.caracterizations.filter(c => {
-// 		  return c.sample === s.id;
-// 		}));
-// 	  });
-
-// 	  patchs.RemoveMethods.map(m => {
-// 		this.RemoveCaracterizations(this._form.caracterizations.filter(c => {
-// 		  return c.method === m.id;
-// 		}));
-// 	  });
-
-// 	  this.__set(this._caracterizations, patchs.AddCaracterizations, patchs.RemoveCaracterizations);
-
-// 	  return patchs;
-// 	}
-// public Patchs(patchs) {
-// 	const patchs = {
-// 		AddMethods: [],
-// 		AddSamples: [],
-// 		AddCaracterizations: [],
-// 		RemoveMethods: [],
-// 		RemoveSamples: [],
-// 		RemoveCaracterizations: [],
-// 		$set: this.$set,
-// 	};
-
-// 	console.log('paths', this);
-
-// 	this.__set(this._samples, patchs.AddSamples, patchs.RemoveSamples);
-// 	this.__set(this._methods, patchs.AddMethods, patchs.RemoveMethods);
-
-// 	patchs.RemoveSamples.map(s => {
-// 	this.RemoveCaracterizations(this._form.caracterizations.filter(c => {
-// 		return c.sample === s.id;
-// 	}));
-// 	});
-
-// 	patchs.RemoveMethods.map(m => {
-// 	this.RemoveCaracterizations(this._form.caracterizations.filter(c => {
-// 		return c.method === m.id;
-// 	}));
-// 	});
-
-// 	this.__set(this._caracterizations, patchs.AddCaracterizations, patchs.RemoveCaracterizations);
-
-// 	return patchs;
-// }
-
-// export class FormPatch {
-
-// 	$set = {};
-// 	_form: CaracterizationForm;
-// 	_samples = new Map<string, { item: Sample, action: string, type: string }>();
-// 	_methods = new Map<string, { item: CaracterizationMethod, action: string, type: string }>();
-// 	_caracterizations = new Map<string, { item: Caracterization, action: string, type: string }>();
-
-// 	setSolicitation(form: CaracterizationForm) {
-// 	  // this._set(this._methods, form.methods.map(m => { return { id: m.id } }), '');
-// 	  this._form = form;
-// 	  this._set(this._samples, form.samples, '');
-// 	  this._set(this._methods, form.methods, '');
-// 	  this._set(this._caracterizations, form.caracterizations, '');
-// 	}
-
-// 	protected __set(list, add, rem) {
-// 	  list.forEach((i) => {
-// 		switch (i.action) {
-// 		  case 'add':
-// 			add.push(i.item);
-// 			break;
-// 		  case 'remove':
-// 			(i.type !== 'new') && rem.push(i.item);
-// 			break;
-// 		}
-// 	  });
-// 	}
-
-// 	public Patchs() {
-
-// 	  const patchs = {
-// 		AddMethods: [],
-// 		AddSamples: [],
-// 		AddCaracterizations: [],
-// 		RemoveMethods: [],
-// 		RemoveSamples: [],
-// 		RemoveCaracterizations: [],
-// 		$set: this.$set,
-// 	  };
-
-// 	  console.log('paths', this);
-
-// 	  this.__set(this._samples, patchs.AddSamples, patchs.RemoveSamples);
-// 	  this.__set(this._methods, patchs.AddMethods, patchs.RemoveMethods);
-
-// 	  patchs.RemoveSamples.map(s => {
-// 		this.RemoveCaracterizations(this._form.caracterizations.filter(c => {
-// 		  return c.sample === s.id;
-// 		}));
-// 	  });
-
-// 	  patchs.RemoveMethods.map(m => {
-// 		this.RemoveCaracterizations(this._form.caracterizations.filter(c => {
-// 		  return c.method === m.id;
-// 		}));
-// 	  });
-
-// 	  this.__set(this._caracterizations, patchs.AddCaracterizations, patchs.RemoveCaracterizations);
-
-// 	  return patchs;
-// 	}
-
-// 	protected _set(_map: Map<string, any>, itens, action) {
-// 	  (itens || []).map(item => {
-// 		const id = item.id || (item.method + item.sample);
-// 		const has = _map.has(id)
-// 		  , el = _map.get(id);
-
-// 		switch (action) {
-// 		  case 'add':
-// 			if (!has) {
-// 			  _map.set(id, {
-// 				item: item,
-// 				action: 'add',
-// 				type: 'new',
-// 			  });
-// 			} else if (el.action === 'remove') {
-// 			  el.action = 'add';
-// 			}
-// 			break;
-// 		  case 'remove':
-// 			if (has) {
-// 			  el.action = 'remove';
-// 			}
-// 			break;
-// 		  default:
-// 			_map.set(id, {
-// 			  item: item,
-// 			  action: '',
-// 			  type: '',
-// 			});
-// 		}
-// 	  });
-// 	}
-
-// 	AddMethods(itens) {
-// 	  this._set(this._methods, itens, 'add');
-// 	}
-
-// 	AddSamples(itens) {
-// 	  this._set(this._samples, itens, 'add');
-// 	}
-// 	AddCaracterizations(itens) {
-// 	  this._set(this._caracterizations, itens, 'add');
-// 	}
-// 	RemoveMethods(itens) {
-// 	  this._set(this._methods, itens, 'remove');
-// 	}
-// 	RemoveSamples(itens) {
-// 	  this._set(this._samples, itens, 'remove');
-// 	}
-// 	RemoveCaracterizations(itens) {
-// 	  this._set(this._caracterizations, itens, 'remove');
-// 	}
-
-//   }

+ 8 - 2
translate/tst/translate.go

@@ -16,10 +16,9 @@ func Translate(project *Project) (err error) {
 	ts := project.Client("angular6")
 
 	path := fmt.Sprintf("%s/%s.module.ts", ts.OutputDir, project.ID)
-
+	// Write(fmt.Sprintf("%s/%s/params.go", p.OutPath, p.Package), file)
 	module = TS.NewFile(path)
 
-	// fmt.Printf("Store angular module '%s'\n", path)
 	// Adiciona os importes necessarios
 	imports(project, module)
 
@@ -90,3 +89,10 @@ func imports(project *Project, file *TS.File) {
 	
 	`).Line()
 }
+
+func getCustom(options map[string]interface{}, path string) (resp interface{}) {
+	if options != nil {
+		resp = options[path]
+	}
+	return
+}