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

auto generate scaffolded functions from types #440

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions sdk/go/examples/generate-func/build.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@echo off

:: This build script works best for examples that are in this repository.
:: If you are using this as a template for your own project, you may need to modify this script,
:: to invoke the hypbuild tool with the correct path to your project.

SET "PROJECTDIR=%~dp0"
pushd ..\..\tools\hypbuild > nul
go run . "%PROJECTDIR%"
popd > nul
12 changes: 12 additions & 0 deletions sdk/go/examples/generate-func/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# This build script works best for examples that are in this repository.
# If you are using this as a template for your own project, you may need to modify this script,
# to invoke the modus-go-build tool with the correct path to your project.

PROJECTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
pushd ../../tools/modus-go-build > /dev/null
go run . "$PROJECTDIR"
exit_code=$?
popd > /dev/null
exit $exit_code
7 changes: 7 additions & 0 deletions sdk/go/examples/generate-func/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module generate-hyp-example

go 1.23.0

require github.com/hypermodeinc/modus/sdk/go v0.0.0

replace github.com/hypermodeinc/modus/sdk/go => ../../
15 changes: 15 additions & 0 deletions sdk/go/examples/generate-func/hypermode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://manifest.hypermode.com/hypermode.json",
"models": {
// No models are used by this example, but if you add any, they would go here.
},
"hosts": {
// This defines the dgraph host that is used by the example functions.
// The {{API_KEY}} will be replaced by the secret provided in the Hypermode Console.
"dgraph": {
"type": "dgraph",
"grpcTarget": "frozen-mango.grpc.eu-central-1.aws.cloud.dgraph.io:443",
"key": "{{API_KEY}}"
}
}
}
41 changes: 41 additions & 0 deletions sdk/go/examples/generate-func/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"github.com/hypermodeinc/modus/sdk/go/pkg/dgraph"
)

func AlterSchema() error {
const schema = `
name: string @index(term) .
description: string @index(term) .
price: float .
items: [uid] @reverse .

type Product {
name
description
price
}

type ShoppingCart {
items
}
`
return dgraph.AlterSchema("dgraph", schema)
}

//hyp:generate
type Product struct {
Uid string `json:"uid,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Price float64 `json:"price,omitempty"`
DType []string `json:"dgraph.type,omitempty"`
}

//hyp:generate
type ShoppingCart struct {
Uid string `json:"uid,omitempty"`
Items []Product `json:"items,omitempty"`
DType []string `json:"dgraph.type,omitempty"`
}
2 changes: 2 additions & 0 deletions sdk/go/tools/modus-go-build/extractor/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ func CollectProgramInfo(config *config.Config, meta *metadata.Metadata, wasmFunc

requiredTypes := make(map[string]types.Type)

generateFunctions(config.SourceDir, pkgs)

for name, f := range getExportedFunctions(pkgs) {
if _, ok := wasmFunctions.Exports[name]; ok {
meta.FnExports[name] = transformFunc(name, f)
Expand Down
12 changes: 12 additions & 0 deletions sdk/go/tools/modus-go-build/extractor/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ func getImportedFunctions(pkgs map[string]*packages.Package) map[string]*types.F
return results
}

func getHypGenTypeName(genDecl *ast.GenDecl) (string, bool) {
if genDecl.Doc == nil {
return "", false
}
for _, c := range genDecl.Doc.List {
if strings.HasPrefix(c.Text, "//hyp:generate") {
return strings.TrimSpace(strings.TrimPrefix(c.Text, "//hyp:generate")), true
}
}
return "", false
}

func getExportedFuncName(fn *ast.FuncDecl) string {
/*
Exported functions must have a body, and are decorated in one of the following forms:
Expand Down
222 changes: 222 additions & 0 deletions sdk/go/tools/modus-go-build/extractor/gen_templates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package extractor

const upsertTmplStruct = `

// Upsert{{.StructName}}Input struct
type Upsert{{.StructName}}Input struct {
{{- range .Fields}}
{{- if and (not (eq (lower .Name) "uid")) (not (eq (lower .Name) "dtype"))}}
{{- if .IsComplexType}}
{{- if .IsSliceType}}
{{.Name}} []string
{{- else}}
{{.Name}} string
{{- end}}
{{- else}}
{{.Name}} {{.Type}}
{{- end}}
{{- end}}
{{- end}}
}

// Upsert{{$.StructName}} method
func Upsert{{$.StructName}}(input Upsert{{$.StructName}}Input) (map[string]string, error) {
// Convert input to the original struct type
{{- if $.HasComplexFields}}
type Temp{{$.StructName}} struct {
{{- range .Fields}}
{{- if .IsComplexType}}
{{- if .IsSliceType}}
{{.Name}} []string ` + "`{{.Tag}}`" + `
{{- else}}
{{.Name}} string ` + "`{{.Tag}}`" + `
{{- end}}
{{- else}}
{{.Name}} {{.Type}} ` + "`{{.Tag}}`" + `
{{- end}}
{{- end}}
}

p := Temp{{$.StructName}}{
{{- range .Fields}}
{{- if not (eq (lower .Name) "uid")}}
{{- if eq (.Name) "DType"}}
{{.Name}}: []string{"{{$.StructName}}"},
{{- else}}
{{.Name}}: input.{{.Name}},
{{- end}}
{{- end}}
{{- if eq (lower .Name) "uid"}}
{{.Name}}: "_:{{lower $.StructName}}",
{{- end}}
{{- end}}
}
{{- else}}
p := {{$.StructName}}{
{{- range .Fields}}
{{- if not (eq (lower .Name) "uid")}}
{{- if eq (.Name) "DType"}}
{{.Name}}: []string{"{{$.StructName}}"},
{{- else}}
{{.Name}}: input.{{.Name}},
{{- end}}
{{- end}}
{{- if eq (lower .Name) "uid"}}
{{.Name}}: "_:{{lower $.StructName}}",
{{- end}}
{{- end}}
}
{{- end}}

// Convert the struct to JSON
jsonBytes, err := json.Marshal(p)
if err != nil {
return nil, err
}

response, err := dgraph.Execute(hostName, &dgraph.Request{
Mutations: []*dgraph.Mutation{
{
SetJson: string(jsonBytes),
},
},
})
if err != nil {
return nil, err
}
return response.Uids, nil
}


`

const deleteTmplStruct = `
// Delete{{.StructName}}Input struct
type Delete{{.StructName}}Input struct {
Uid string
}

// Delete{{$.StructName}} method
func Delete{{$.StructName}}(input Delete{{$.StructName}}Input) (string, error) {
statement := fmt.Sprintf("<%s> * * .", input.Uid)

_, err := dgraph.Execute(hostName, &dgraph.Request{
Mutations: []*dgraph.Mutation{
{
DelNquads: statement,
},
},
})
if err != nil {
return "", err
}

return "success", nil
}


`

const getTmplStruct = `
// Get{{.StructName}} method
func Get{{.StructName}}(uid string) (*{{.StructName}}, error) {
statement := ` + "`" +
`
query query{{.StructName}}($uid: string) {
{{lower .StructName}}(func: uid($uid)) {
uid
dgraph.type
{{- range .Fields}}
{{- if and (not (eq (lower .Name) "uid")) (not (eq (lower .Name) "dtype")) }}
{{- if .IsComplexType}}
{{lower .Name}} {
uid
dgraph.type
expand(_all_)
}
{{- else}}
{{lower .Name}}
{{- end}}
{{- end}}
{{- end}}
}
}
` + "`" + `
variables := map[string]string{"$uid": uid}
response, err := dgraph.Execute(hostName, &dgraph.Request{
Query: &dgraph.Query{
Query: statement,
Variables: variables,
},
})
if err != nil {
return nil, err
}
type {{.StructName}}Data struct {
{{.StructName}}s []*{{.StructName}} ` + "`json:\"{{lower .StructName}}\"`" + `
}
var {{lower .StructName}}Data {{.StructName}}Data
if err := json.Unmarshal([]byte(response.Json), &{{lower .StructName}}Data); err != nil {
return nil, err
}

if len({{lower .StructName}}Data.{{.StructName}}s) == 0 {
return nil, fmt.Errorf("{{.StructName}} not found")
}

return {{lower .StructName}}Data.{{.StructName}}s[0], nil
}


`

const queryTmplStruct = `
// Query{{.StructName}} method
func Query{{.StructName}}(limit, offset int) ([]*{{.StructName}}, error) {
statement := ` + "`" +
`
query query{{.StructName}}($limit: int, $offset: int) {
{{lower .StructName}}(func: type({{.StructName}}), first: $limit, offset: $offset) {
uid
dgraph.type
{{- range .Fields}}
{{- if and (not (eq (lower .Name) "uid")) (not (eq (lower .Name) "dtype")) }}
{{- if .IsComplexType}}
{{lower .Name}} {
uid
dgraph.type
expand(_all_)
}
{{- else}}
{{lower .Name}}
{{- end}}
{{- end}}
{{- end}}
}
}
` + "`" + `
variables := map[string]string{"$limit": fmt.Sprintf("%d", limit), "$offset": fmt.Sprintf("%d", offset)}
response, err := dgraph.Execute(hostName, &dgraph.Request{
Query: &dgraph.Query{
Query: statement,
Variables: variables,
},
})
if err != nil {
return nil, err
}
type {{.StructName}}Data struct {
{{.StructName}}s []*{{.StructName}} ` + "`json:\"{{lower .StructName}}\"`" + `
}
var {{lower .StructName}}Data {{.StructName}}Data
if err := json.Unmarshal([]byte(response.Json), &{{lower .StructName}}Data); err != nil {
return nil, err
}

if len({{lower .StructName}}Data.{{.StructName}}s) == 0 {
return nil, fmt.Errorf("{{.StructName}} not found")
}

return {{lower .StructName}}Data.{{.StructName}}s, nil
}
`
Loading
Loading