Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add binding gen js for remote and local #12

Merged
merged 35 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a2c8c05
init check in for generating binding files based on flix
bthaile Sep 1, 2023
6f03fb0
add sub templates for js files, gen binding file in same dir as templ…
bthaile Sep 1, 2023
389069b
organization of files and ignores
bthaile Sep 1, 2023
7451e2a
ignore built file
bthaile Sep 1, 2023
4ab973f
ignore template files
bthaile Sep 1, 2023
016c059
ignore template files
bthaile Sep 1, 2023
5a50239
add file reader to config for unit testing
bthaile Sep 1, 2023
50d5bcb
verify that remote templates can gen js files
bthaile Sep 5, 2023
1b9b440
fix git ignore file
bthaile Sep 5, 2023
37a9f3d
ignore cmd dir and all .DS_Store files
bthaile Sep 5, 2023
6c242b7
remove .DS_Store files
bthaile Sep 5, 2023
f9d4bf4
add testing gen js binding with remote flix
bthaile Sep 5, 2023
d15e7ca
support array of simple types
bthaile Sep 5, 2023
d4848cf
first batch of changes based on feedback
bthaile Sep 6, 2023
2098974
collapse files and keep everything in flixkit package
bthaile Sep 7, 2023
530a385
clean up
bthaile Sep 7, 2023
b7a142c
add js docs to provide types for typescript
bthaile Sep 8, 2023
870b6ff
do not rely on message to be there and that us-en exists, update temp…
bthaile Sep 8, 2023
805a993
fix consistency for testing
bthaile Sep 8, 2023
dac9742
simplify conversion to js types, fcl uses strings for all numbers
bthaile Sep 14, 2023
4a9cf9a
add golden for unit tests
bthaile Sep 26, 2023
bd1a7a9
pass in generator method to generate code
bthaile Sep 26, 2023
5aef7f0
use interface and set specific port for testing remote and use golden…
bthaile Sep 27, 2023
e7cd1bc
fixed arguments key sorting issue, that was causing test failures
bthaile Sep 27, 2023
4932623
fix case typo in generator name
bthaile Sep 28, 2023
64e11a5
remove need to read from disk to get template
bthaile Sep 28, 2023
aa66b9f
tidy
bthaile Sep 29, 2023
9d27901
move js generator to bindings module and rename js generator to fcl j…
bthaile Oct 4, 2023
8c19379
formatting
bthaile Oct 16, 2023
0593679
pass in template directory to be more flexible
bthaile Oct 18, 2023
5a9f441
add comment that fcl version 1.3.0 or greater is needed to use templates
bthaile Oct 18, 2023
8857770
add helper func to create fcl js code generator
bthaile Oct 18, 2023
9cd4a66
removed todo: added named return values from method
bthaile Oct 21, 2023
5ad7e6a
update readme to include bindings module
bthaile Oct 21, 2023
6619788
use filepath.Walk to get files from template dir
bthaile Oct 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# ignore all .DS_Store files
.DS_Store
111 changes: 111 additions & 0 deletions bindings/fcl-js.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package bindings

import (
"bytes"
"embed"
"sort"
"text/template"

"github.com/onflow/flixkit-go"
"github.com/stoewer/go-strcase"
)

type SimpleParameter struct {
Name string
JsType string
Description string
FclType string
CadType string
}

type TemplateData struct {
Version string
Parameters []SimpleParameter
Title string
Description string
Location string
IsScript bool
IsLocalTemplate bool
}

//go:embed templates/*.tmpl
var templateFiles embed.FS
bthaile marked this conversation as resolved.
Show resolved Hide resolved

type FclJSGenerator struct{}

func (g FclJSGenerator) Generate(flix *flixkit.FlowInteractionTemplate, templateLocation string, isLocal bool) (string, error) {
tmpl, err := template.ParseFS(templateFiles, "templates/*.tmpl")
if err != nil {
return "", err
}

methodName := strcase.LowerCamelCase(flix.Data.Messages.GetTitleValue("Request"))
description := flix.GetDescription()
data := TemplateData{
Version: flix.FVersion,
Parameters: transformArguments(flix.Data.Arguments),
Title: methodName,
Description: description,
Location: templateLocation,
IsScript: flix.IsScript(),
IsLocalTemplate: isLocal,
}

var buf bytes.Buffer
err = tmpl.Execute(&buf, data)
return buf.String(), err
}

func transformArguments(args flixkit.Arguments) []SimpleParameter {
simpleArgs := []SimpleParameter{}
var keys []string
// get keys for sorting
for k := range args {
keys = append(keys, k)
}

sort.SliceStable(keys, func(i, j int) bool {
return args[keys[i]].Index < args[keys[j]].Index
})
for _, key := range keys {
arg := args[key]
isArray, cType, jsType := isArrayParameter(arg)
desciption := arg.Messages.GetTitleValue("")
if isArray {
simpleArgs = append(simpleArgs, SimpleParameter{Name: key, CadType: cType, JsType: jsType, FclType: "Array(t." + cType + ")", Description: desciption})
} else {
jsType := convertCadenceTypeToJS(arg.Type)
simpleArgs = append(simpleArgs, SimpleParameter{Name: key, CadType: arg.Type, JsType: jsType, FclType: arg.Type, Description: desciption})
}
}
return simpleArgs
}

func isArrayParameter(arg flixkit.Argument) (bool, string, string) {
bthaile marked this conversation as resolved.
Show resolved Hide resolved
if arg.Type == "" || arg.Type[0] != '[' {
return false, "", ""
}
cadenceType := arg.Type[1 : len(arg.Type)-1]
jsType := "Array<" + convertCadenceTypeToJS(cadenceType) + ">"
return true, cadenceType, jsType
}

func convertCadenceTypeToJS(cadenceType string) string {
// need to determine js type based on fcl supported types
// looking at fcl types and how arguments work as parameters
// https://github.com/onflow/fcl-js/blob/master/packages/types/src/types.js
switch cadenceType {
case "Bool":
return "boolean"
case "Void":
return "void" // return type only
case "Dictionary":
return "object" // TODO: support Collection type, test to see what fcl
bthaile marked this conversation as resolved.
Show resolved Hide resolved
case "Struct":
return "object" // TODO: support Composite type, test to see what fcl
bthaile marked this conversation as resolved.
Show resolved Hide resolved
case "Enum":
return "object" // TODO: support Composite type, test to see what fcl
bthaile marked this conversation as resolved.
Show resolved Hide resolved
default:
return "string"
}
}
213 changes: 213 additions & 0 deletions bindings/fcl-js_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package bindings

import (
"testing"

"github.com/hexops/autogold/v2"
"github.com/onflow/flixkit-go"
"github.com/stretchr/testify/assert"
)

var parsedTemplateTX = &flixkit.FlowInteractionTemplate{
FType: "InteractionTemplate",
FVersion: "1.0.0",
ID: "290b6b6222b2a77b16db896a80ddf29ebd1fa3038c9e6625a933fa213fce51fa",
Data: flixkit.Data{
Type: "transaction",
Interface: "",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "Transfer Tokens",
},
},
Description: &flixkit.Description{
I18N: map[string]string{
"en-US": "Transfer tokens from one account to another",
},
},
},
Cadence: "import FungibleToken from 0xFUNGIBLETOKENADDRESS\ntransaction(amount: UFix64, to: Address) {\nlet vault: @FungibleToken.Vault\nprepare(signer: AuthAccount) {\nself.vault <- signer\n.borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n.withdraw(amount: amount)\n}\nexecute {\ngetAccount(to)\n.getCapability(/public/flowTokenReceiver)!\n.borrow<&{FungibleToken.Receiver}>()!\n.deposit(from: <-self.vault)\n}\n}",
Dependencies: flixkit.Dependencies{
"0xFUNGIBLETOKENADDRESS": flixkit.Contracts{
"FungibleToken": flixkit.Networks{
"mainnet": flixkit.Network{
Address: "0xf233dcee88fe0abe",
FqAddress: "A.0xf233dcee88fe0abe.FungibleToken",
Contract: "FungibleToken",
Pin: "83c9e3d61d3b5ebf24356a9f17b5b57b12d6d56547abc73e05f820a0ae7d9cf5",
PinBlockHeight: 34166296,
},
"testnet": flixkit.Network{
Address: "0x9a0766d93b6608b7",
FqAddress: "A.0x9a0766d93b6608b7.FungibleToken",
Contract: "FungibleToken",
Pin: "83c9e3d61d3b5ebf24356a9f17b5b57b12d6d56547abc73e05f820a0ae7d9cf5",
PinBlockHeight: 74776482,
},
},
},
},
Arguments: flixkit.Arguments{
"amount": flixkit.Argument{
Index: 0,
Type: "UFix64",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "The amount of FLOW tokens to send",
},
},
},
Balance: "",
},
"to": flixkit.Argument{
Index: 1,
Type: "Address",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "The Flow account the tokens will go to",
},
},
},
Balance: "",
},
},
},
}

var parsedTemplateScript = &flixkit.FlowInteractionTemplate{
FType: "InteractionTemplate",
FVersion: "1.0.0",
ID: "290b6b6222b2a77b16db896a80ddf29ebd1fa3038c9e6625a933fa213fce51fa",
Data: flixkit.Data{
Type: "script",
Interface: "",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "Multiply Two Integers",
},
},
Description: &flixkit.Description{
I18N: map[string]string{
"en-US": "Multiply two numbers to another",
},
},
},
Cadence: "pub fun main(x: Int, y: Int): Int { return x * y }",
Arguments: flixkit.Arguments{
"x": flixkit.Argument{
Index: 0,
Type: "Int",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "number to be multiplied",
},
},
},
Balance: "",
},
"y": flixkit.Argument{
Index: 1,
Type: "Int",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "second number to be multiplied",
},
},
},
Balance: "",
},
},
},
}

var ArrayTypeScript = &flixkit.FlowInteractionTemplate{
FType: "InteractionTemplate",
FVersion: "1.0.0",
ID: "290b6b6222b2a77b16db896a80ddf29ebd1fa3038c9e6625a933fa213fce51fa",
Data: flixkit.Data{
Type: "script",
Interface: "",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "Multiply Numbers",
},
},
Description: &flixkit.Description{
I18N: map[string]string{
"en-US": "Multiply numbers in an array",
},
},
},
Cadence: "pub fun main(numbers: [Int]): Int { var total = 1; for x in numbers { total = total * x }; return total }",
Arguments: flixkit.Arguments{
"numbers": flixkit.Argument{
Index: 0,
Type: "[Int]",
Messages: flixkit.Messages{
Title: &flixkit.Title{
I18N: map[string]string{
"en-US": "Array of numbers to be multiplied",
},
},
},
Balance: "",
},
},
},
}

var minimumTemplate = &flixkit.FlowInteractionTemplate{
FType: "InteractionTemplate",
FVersion: "1.0.0",
ID: "290b6b6222b2a77b16db896a80ddf29ebd1fa3038c9e6625a933fa213fce51fa",
Data: flixkit.Data{
Type: "script",
Interface: "",
Cadence: "pub fun main(numbers: [Int]): Int { var total = 1; for x in numbers { total = total * x }; return total }",
Arguments: flixkit.Arguments{
"numbers": flixkit.Argument{
Index: 0,
Type: "[Int]",
},
},
},
}

func TestJSGenTransaction(t *testing.T) {
generator := FclJSGenerator{}
got, _ := generator.Generate(parsedTemplateTX, "./transfer_token.json", true)
autogold.ExpectFile(t, got)
}

func TestJSGenScript(t *testing.T) {
generator := FclJSGenerator{}
assert := assert.New(t)
got, err := generator.Generate(parsedTemplateScript, "./multiply_two_integers.template.json", true)
assert.NoError(err, "ParseTemplate should not return an error")
autogold.ExpectFile(t, got)
}

func TestJSGenArrayScript(t *testing.T) {
generator := FclJSGenerator{}
assert := assert.New(t)

out, err := generator.Generate(ArrayTypeScript, "./multiply-numbers.template.json", true)
assert.NoError(err, "ParseTemplate should not return an error")
autogold.ExpectFile(t, out)
}

func TestJSGenMinScript(t *testing.T) {
generator := FclJSGenerator{}
assert := assert.New(t)

out, err := generator.Generate(minimumTemplate, "./min.template.json", true)
assert.NoError(err, "ParseTemplate should not return an error")
autogold.ExpectFile(t, out)
}
39 changes: 39 additions & 0 deletions bindings/templates/js_fcl_main.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
This binding file was auto generated based on FLIX template v{{.Version}}.
Changes to this file might get overwritten
**/

import * as fcl from "@onflow/fcl"
{{- if .IsLocalTemplate }}
import flixTemplate from "{{.Location}}"
{{- else}}
const flixTemplate = "{{.Location}}"
{{- end}}

// TODO: add runtime check for parameters consistency
const parameterNames = [
{{- range $index, $ele := .Parameters -}}
{{if $index}}, {{end}}"{{$ele.Name}}"
{{- end -}}
];
{{"\n"}}/**
* {{.Description}}{{"\n"}}
{{- if gt (len .Parameters) 0 -}}
* @param {Object} Parameters - parameters for the cadence
{{- range $index, $ele := .Parameters -}}
{{"\n"}}* @param {{"{"}} {{$ele.JsType}} {{"}"}} Parameters.{{$ele.Name}} - {{$ele.Description}}: {{$ele.CadType}}
{{- end -}}
{{ end -}}
{{- if not .IsScript -}}
{{"\n"}}* @returns {Promise<string>} - returns a promise which resolves to the transaction id
{{- end -}}
{{"\n"}}*/

{{if .IsScript}}
{{- template "js_fcl_script.tmpl" .}}
{{else}}
{{- template "js_fcl_tx.tmpl" .}}
{{- end}}



20 changes: 20 additions & 0 deletions bindings/templates/js_fcl_script.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export async function {{.Title}}({
{{- if len .Parameters -}}
{{- range $index, $ele := .Parameters -}}
{{if $index}}, {{end}}{{.Name}}
{{- end -}}
{{- end -}}
}) {
const info = await fcl.query({
template: flixTemplate,
{{ if len .Parameters -}}
args: (arg, t) => [
{{- range $index, $ele := .Parameters -}}
{{if $index}}, {{end}}arg({{.Name}}, t.{{.FclType}})
{{- end -}}
]
{{- end }}
});

return info
}
Loading