Skip to content

Commit 73ceef1

Browse files
authored
Merge pull request #47 from HexmosTech/Wasm
Wasm integration
2 parents 4c47f65 + 4ebde26 commit 73ceef1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+5916
-1688
lines changed

.github/workflows/testapp.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ jobs:
1616
- name: Install dependencies
1717
run: go mod tidy
1818
- name: Build
19-
run: go build -v ./...
19+
run: go build -tags=cli -v ./...
2020
- name: Test with the Go CLI
2121
run: |
2222
go mod tidy
23-
go build -o build/l2 -ldflags "-X main.version=`git tag --sort=-version:refname | head -n 1`" l2.go
24-
go test -v ./tests/
23+
go build -tags=cli -o build/l2 -ldflags "-X main.version=`git tag --sort=-version:refname | head -n 1`" l2.go
24+
go test -tags=cli -v ./tests/
2525
- name: Deploy hexmos doc
2626
run: curl -X POST --fail -F token=${{ secrets.TRIGGER_TOKEN }} -F ref=main https://git.apps.hexmos.com/api/v4/projects/85/trigger/pipeline

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
build/
22
lama2
33
.vscode
4+
buildAndPublish.sh
5+
static/
6+
.env

cmdexec/cmdexec.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build cli
2+
13
// Package `cmdexec` provides a facility to execute
24
// l2 commands, stream output to stdout, while also
35
// providing ability to retrieve the command output as
@@ -11,7 +13,6 @@ import (
1113

1214
"github.com/HexmosTech/httpie-go"
1315
"github.com/HexmosTech/lama2/utils"
14-
"github.com/rs/zerolog/log"
1516
)
1617

1718
// ExecCommand changes directory to the given `apiDir`
@@ -25,10 +26,8 @@ func ExecCommand(cmdSlice []string, stdinBody string, apiDir string) (httpie.ExR
2526
utils.ChangeWorkingDir(apiDir)
2627
resp, err := httpie.Lama2Entry(cmdSlice, strings.NewReader(stdinBody))
2728
if err != nil {
28-
log.Fatal().Str("Error from the API executor", err.Error()).Msg("")
2929
return httpie.ExResponse{}, errors.New("Error from API executor: " + err.Error())
3030
}
31-
log.Debug().Str("Response body from API executor", resp.Body).Msg("")
3231
utils.ChangeWorkingDir(oldDir)
3332
return resp, nil
3433
}

cmdexec/cmdexec.wasm.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//go:build wasm
2+
3+
// Package `cmdexec` provides a facility to execute
4+
// l2 commands, stream output to stdout, while also
5+
// providing ability to retrieve the command output as
6+
// a string.
7+
package cmdexec
8+
9+
import (
10+
"errors"
11+
"fmt"
12+
"os"
13+
14+
"strings"
15+
16+
"github.com/HexmosTech/httpie-go"
17+
)
18+
19+
// ExecCommand changes directory to the given `apiDir`
20+
// and then executes the command specified in `cmdStr`
21+
// During command execution, ExecCommand streams output
22+
// to stdout.
23+
// Once execution finishes, previous CWD is restored,
24+
// and the command output is returned as a string
25+
func ExecCommand(cmdSlice []string, stdinBody string, apiDir string) (httpie.ExResponse, error) {
26+
proxyURL := os.Getenv("PROXY_URL")
27+
proxyUserName := os.Getenv("PROXY_USERNAME")
28+
proxyUserPassword := os.Getenv("PROXY_PASSWORD")
29+
allowRedirects := true
30+
resp, err := httpie.Lama2Entry(cmdSlice, strings.NewReader(stdinBody), proxyURL, proxyUserName, proxyUserPassword, allowRedirects)
31+
if err != nil {
32+
fmt.Println("Got error while executing", err)
33+
return httpie.ExResponse{}, errors.New("Error from API executor: " + err.Error())
34+
}
35+
return resp, nil
36+
}

cmdexec/js.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//go:build cli
2+
3+
14
package cmdexec
25

36
import (
@@ -9,7 +12,7 @@ import (
912

1013
// GetJSVm creates a new goja runtime instance
1114
// with console.log enabled
12-
func GetJSVm() *goja.Runtime {
15+
func GetJSVm() interface{} {
1316
vm := goja.New()
1417
new(require.Registry).Enable(vm)
1518
console.Enable(vm)
@@ -23,8 +26,11 @@ func GetJSVm() *goja.Runtime {
2326
// Note: the vm runtime remains modified; so if
2427
// you reuse the vm for other operations, the state
2528
// from previous invocations carry over
26-
func RunVMCode(jsCode string, vm *goja.Runtime) {
27-
_, err := vm.RunString(jsCode)
29+
func RunVMCode(jsCode string, vm interface{}) {
30+
if vm == nil {
31+
vm = GetJSVm()
32+
}
33+
_, err := vm.(*goja.Runtime).RunString(jsCode)
2834
if ex, ok := err.(*goja.Exception); ok {
2935
log.Fatal().Str("Error executing JS processor block", ex.String()).Msg("")
3036
}

cmdexec/js.wasm.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//go:build wasm
2+
3+
package cmdexec
4+
5+
import (
6+
"fmt"
7+
"syscall/js"
8+
"github.com/rs/zerolog/log"
9+
)
10+
11+
// RunVMCode takes in a JS snippet as a string, executes the code in a JS VM using Web Worker
12+
func RunVMCode(jsCode string, vm interface{}) {
13+
worker := vm.(js.Value)
14+
resultChan := make(chan js.Value)
15+
worker.Call("postMessage", map[string]interface{}{
16+
"code": jsCode,
17+
})
18+
19+
js.Global().Get("addEventListener").Invoke("message", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
20+
resultChan <- args[0].Get("data")
21+
return nil
22+
}))
23+
24+
result := <-resultChan
25+
err := result.Get("error").String()
26+
if err != "null" {
27+
log.Fatal().Str("Error executing JS processor block", err).Msg("")
28+
}
29+
}
30+
31+
// GenerateChainCode takes in an HTTP response body and comes up with some JS code to define the "magic variable" result.
32+
func GenerateChainCode(httpRespBody string) string {
33+
fmt.Println("WW: Value of httpRespBody is",httpRespBody)
34+
code := `try {
35+
result = JSON.parse(String.raw` + "`" + httpRespBody + "`" + `)
36+
console.log("Stored as JSON")
37+
} catch (e) {
38+
result = String.raw` + "`" + httpRespBody + "`" + `
39+
console.log(e)
40+
console.log("Stored as string")
41+
}`
42+
log.Debug().Str("Chain code generated", code).Msg("")
43+
return code
44+
}

cmdgen/cmdgen.cli.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build cli
2+
3+
package cmdgen
4+
5+
import (
6+
"github.com/HexmosTech/gabs/v2"
7+
"github.com/HexmosTech/lama2/lama2cmd"
8+
"github.com/rs/zerolog/log"
9+
)
10+
11+
func ConstructCommand(parsedInput *gabs.Container, o *lama2cmd.Opts) ([]string, string) {
12+
log.Info().Str("ParsedInput", parsedInput.String()).Msg("")
13+
httpv, url, jsonObj, headers, multipartBool, formBool := ConstructCommandHelper(parsedInput)
14+
res, stdinBody := assembleCmdString(httpv, url, jsonObj, headers, multipartBool, formBool, o)
15+
return res, stdinBody
16+
}

cmdgen/cmdgen.go

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,27 @@ import (
1212

1313
"github.com/HexmosTech/gabs/v2"
1414
"github.com/HexmosTech/lama2/lama2cmd"
15-
"github.com/rs/zerolog/log"
1615
)
1716

17+
// The assembleCmdString function constructs a httpie
18+
// command string for an HTTP request based on the provided
19+
// HTTP method, URL, JSON object, headers, and options. It also
20+
// handle multipart and form data.
1821
func assembleCmdString(httpv string, url string, jsonObj *gabs.Container, headers *gabs.Container, multipart bool, form bool, o *lama2cmd.Opts) ([]string, string) {
1922
command := make([]string, 0)
20-
log.Info().
21-
Str("Type", "Construct Command").
22-
Str("httpv", httpv).
23-
Str("url", url).
24-
Bool("multipart", multipart).
25-
Bool("form", form).
26-
Msg(fmt.Sprint("Construct parameters"))
27-
28-
log.Debug().
29-
Str("JSONObj", jsonObj.String()).
30-
Str("Headers", headers.String()).Msg("")
31-
3223
var files *gabs.Container
3324
if multipart {
3425
if jsonObj.ExistsP("@files") {
3526
files = jsonObj.S("@files")
36-
log.Debug().Str("Files", files.String()).Msg("")
3727
jsonObj.Delete("@files")
38-
log.Trace().Str("Shortened JsonObj", jsonObj.String()).Msg("")
3928
}
4029
}
4130

4231
jsonStr := ""
4332
if jsonObj != nil && !multipart && !form {
4433
dst := &bytes.Buffer{}
4534
if err := json.Compact(dst, []byte(jsonObj.String())); err != nil {
46-
log.Fatal().
47-
Str("Error", err.Error()).
48-
Msg("Couldn't minify JSON")
35+
fmt.Println(err)
4936
}
5037
jsonStr = dst.String()
5138
}
@@ -58,7 +45,7 @@ func assembleCmdString(httpv string, url string, jsonObj *gabs.Container, header
5845
}*/
5946

6047
command = append(command, "ht ")
61-
if o.Nocolor {
48+
if o != nil && o.Nocolor {
6249
command = append(command, "--pretty=none ")
6350
}
6451
if multipart || form {
@@ -105,20 +92,20 @@ func assembleCmdString(httpv string, url string, jsonObj *gabs.Container, header
10592
// API file inputs, figures out the type of target command
10693
// and finally generates a string representing the generated
10794
// command
108-
func ConstructCommand(parsedInput *gabs.Container, o *lama2cmd.Opts) ([]string, string) {
109-
log.Info().Str("ParsedInput", parsedInput.String()).Msg("")
95+
func ConstructCommandHelper(parsedInput *gabs.Container) (string, string, *gabs.Container, *gabs.Container, bool, bool) {
96+
fmt.Println("WW parsedInput:", parsedInput.StringIndent("", " "))
11097
httpv := parsedInput.S("verb", "value")
11198
url := parsedInput.S("url", "value")
11299
jsonObj := parsedInput.S("details", "ip_data")
113100
headers := parsedInput.S("details", "headers")
114101
multipart := parsedInput.S("multipart", "value")
102+
form := parsedInput.S("form", "value")
115103
multipartBool := false
116104
if multipart != nil {
117105
multipartBool = true
118106
}
119-
form := parsedInput.S("form", "value")
120107
formBool := form != nil
121-
122-
res, stdinBody := assembleCmdString(httpv.Data().(string), url.Data().(string), jsonObj, headers, multipartBool, formBool, o)
123-
return res, stdinBody
108+
fmt.Println("WW httpv:", httpv.Data().(string))
109+
fmt.Println("WW url:", url.Data().(string))
110+
return httpv.Data().(string), url.Data().(string), jsonObj, headers, multipartBool, formBool
124111
}

cmdgen/cmdgen.wasm.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//go:build wasm
2+
3+
package cmdgen
4+
5+
import (
6+
"github.com/HexmosTech/gabs/v2"
7+
"github.com/HexmosTech/lama2/lama2cmd"
8+
)
9+
10+
func ConstructCommand(parsedInput *gabs.Container, o *lama2cmd.Opts) ([]string, string) {
11+
httpv, url, jsonObj, headers, multipartBool, formBool := ConstructCommandHelper(parsedInput)
12+
res, stdinBody := assembleCmdString(httpv, url, jsonObj, headers, multipartBool, formBool, nil)
13+
return res, stdinBody
14+
}

codegen/codegen.cli.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//go:build cli
2+
3+
package codegen
4+
5+
import (
6+
"bytes"
7+
_ "embed"
8+
"fmt"
9+
"strings"
10+
"text/template"
11+
12+
"github.com/HexmosTech/gabs/v2"
13+
"github.com/HexmosTech/lama2/cmdexec"
14+
"github.com/atotto/clipboard"
15+
"github.com/dop251/goja"
16+
"github.com/rs/zerolog/log"
17+
)
18+
19+
//go:embed httpsnippet.js
20+
var snippetcore string
21+
22+
var globalVM interface{}
23+
24+
func initialize() {
25+
globalVM = cmdexec.GetJSVm()
26+
}
27+
28+
var flag = 0
29+
30+
func GenerateTargetCode(targetLangLib string, parsedAPI *gabs.Container) {
31+
convertedSnippetFinal := generateConvertedSippet(targetLangLib, parsedAPI)
32+
fmt.Println("Converted snippet:\n", convertedSnippetFinal)
33+
clipboard.WriteAll(convertedSnippetFinal)
34+
fmt.Println("Code copied to clipboard")
35+
}
36+
37+
func PrepareHTTPSnippetGenerator(snippetArgs SnippetArgs) string {
38+
var templOutput bytes.Buffer
39+
templStr := `{{.SnippetCore}}
40+
41+
const snippet = new window.HTTPSnippet({{.HARRequest}});
42+
43+
convertedSnippet = snippet.convert('{{.Language}}'{{if .Library }}, '{{.Library}}'{{end}});
44+
`
45+
tmpl, _ := template.New("httpsnippet").Parse(templStr)
46+
tmpl.Execute(&templOutput, snippetArgs)
47+
return templOutput.String()
48+
}
49+
50+
func generateConvertedSippet(targetLangLib string, parsedAPI *gabs.Container) string {
51+
initialize()
52+
parsedAPIblocks := parsedAPI.S("value").Data().(*gabs.Container).Children()
53+
convertedSnippetList := make([]string, 0)
54+
blockLength := len(parsedAPIblocks)
55+
for i, block := range parsedAPIblocks {
56+
log.Debug().Int("Block num", i).Msg("")
57+
log.Debug().Str("Block getting processed", block.String()).Msg("")
58+
blockType := block.S("type").Data().(string)
59+
if blockType == "processor" {
60+
snippet := block.S("value").Data().(*gabs.Container).Data().(string)
61+
log.Debug().Str("Processor block incoming block", block.String()).Msg("")
62+
convertedSnippetList = append(convertedSnippetList, snippet)
63+
} else if blockType == "Lama2File" {
64+
harRequest, flag := GetRequestHARString(block,targetLangLib)
65+
snippetArgs := SnippetArgs{}
66+
lang, lib := SplitLangLib(targetLangLib)
67+
snippetArgs.Language = lang
68+
snippetArgs.Library = lib
69+
snippetArgs.HARRequest = harRequest
70+
snippetArgs.SnippetCore = snippetcore
71+
httpsnippetCode := PrepareHTTPSnippetGenerator(snippetArgs)
72+
vm := cmdexec.GetJSVm()
73+
_, e := vm.(*goja.Runtime).RunString(httpsnippetCode)
74+
if e != nil {
75+
log.Fatal().
76+
Str("Type", "CodeGen").
77+
Str("Error", e.Error()).
78+
Msg("Code generator error")
79+
}
80+
convertedSnippet := vm.(*goja.Runtime).Get("convertedSnippet").String()
81+
convertedSnippetWithPostProcessing := postprocessURL(convertedSnippet, flag)
82+
flag = 0
83+
convertedSnippetList = append(convertedSnippetList, convertedSnippetWithPostProcessing)
84+
}
85+
if i != blockLength-1 {
86+
convertedSnippetList = append(convertedSnippetList, "\n---\n")
87+
}
88+
}
89+
90+
convertedSnippetFinal := strings.Join(convertedSnippetList, "\n")
91+
return convertedSnippetFinal
92+
}

0 commit comments

Comments
 (0)