Skip to content

Commit

Permalink
Merge pull request #30 from viant/handler_gen
Browse files Browse the repository at this point in the history
Handler gen
  • Loading branch information
adranwit authored Jun 23, 2023
2 parents d96c9ed + b1b263a commit 0305678
Show file tree
Hide file tree
Showing 69 changed files with 3,475 additions and 197 deletions.
153 changes: 143 additions & 10 deletions cmd/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import (
"github.com/viant/afs"
"github.com/viant/afs/file"
"github.com/viant/afs/url"
"github.com/viant/datly/cmd/command"
"github.com/viant/datly/cmd/option"
"github.com/viant/datly/cmd/options"
"github.com/viant/datly/config"
"github.com/viant/datly/gateway/runtime/standalone"
codegen "github.com/viant/datly/internal/codegen"
"github.com/viant/datly/router"
"github.com/viant/datly/router/marshal"
"github.com/viant/datly/shared"
Expand Down Expand Up @@ -42,6 +45,7 @@ import (

type (
Builder struct {
Options *options.Options
pluginTypes map[string]bool
constFileContent constFileContent
constIndex ParametersIndex
Expand Down Expand Up @@ -76,10 +80,12 @@ type (
parent *ViewConfig
mainHolder string
viewName string
isVirtual bool
isAuxiliary bool
queryJoin *query.Join
unexpandedTable *Table
outputConfig option.OutputConfig
expandedTable *Table

outputConfig option.OutputConfig

relations []*ViewConfig
relationsIndex map[string]int
Expand All @@ -88,8 +94,8 @@ type (
aKey *relationKey
fileName string
viewType view.Mode
expandedTable *Table
batchEnabled map[string]bool
Spec *codegen.Spec
}

templateMetaConfig struct {
Expand Down Expand Up @@ -132,6 +138,44 @@ type (
}
)

func (c *ViewConfig) ActualHolderName() string {
name := c.expandedTable.HolderName
detectCase, err := format.NewCase(formatter.DetectCase(name))
if err != nil {
return name
}
if detectCase != format.CaseUpperCamel {
name = detectCase.Format(name, format.CaseUpperCamel)
}
return name
}

func (c *ViewConfig) excludedColumns() map[string]bool {
exceptIndex := map[string]bool{}
for _, column := range c.expandedTable.Columns {
for _, except := range column.Except {
exceptIndex[strings.ToLower(except)] = true
}
}
return exceptIndex
}

func (c *ViewConfig) listedColumns() map[string]bool {
includeIndex := map[string]bool{}
for _, column := range c.expandedTable.Inner {
if column.Name == "*" {
return includeIndex
}
}
for _, column := range c.expandedTable.Inner {
if column.Alias != "" {
includeIndex[strings.ToLower(column.Alias)] = true
}
includeIndex[strings.ToLower(column.Name)] = true
}
return includeIndex
}

func (c *constFileContent) MergeFrom(params ...*view.Parameter) {
if len(params) == 0 {
return
Expand Down Expand Up @@ -329,6 +373,57 @@ func (c *ViewConfig) metaConfigByName(holder string) (*templateMetaConfig, bool)
return nil, false
}

func (c *ViewConfig) buildSpec(ctx context.Context, db *sql.DB, pkg string) (err error) {
name := c.ActualHolderName()
if c.Spec, err = codegen.NewSpec(ctx, db, c.TableName(), c.SQL()); err != nil {
return err
}
if len(c.Spec.Columns) == 0 {
return fmt.Errorf("not found table %v(%v) columns", c.TableName(), c.SQL())
}
excludedColumns := c.excludedColumns()
listedColumns := c.listedColumns()
cardinality := view.One
if c.IsToMany() {
cardinality = view.Many
}
if err = c.Spec.BuildType(pkg, name, cardinality, listedColumns, excludedColumns); err != nil {
return err
}
for _, relation := range c.relations {
if err = relation.buildSpec(ctx, db, pkg); err != nil {
return err
}
relation.Spec.Parent = c.Spec
cardinality := view.One
if relation.outputConfig.IsMany() {
cardinality = view.Many
}
c.Spec.AddRelation(relation.ActualHolderName(), relation.queryJoin, relation.Spec, cardinality)
}
return nil
}

func (c *ViewConfig) SQL() string {
SQL := ""
if join := c.queryJoin; join != nil {
SQL = sqlparser.Stringify(c.queryJoin.With)
SQL = strings.Trim(strings.TrimSpace(SQL), "()")
}

return SQL
}

func (s *ViewConfig) TableName() string {
if s.expandedTable != nil {
return s.expandedTable.Name
}
if s.unexpandedTable != nil {
return s.unexpandedTable.Name
}
return ""
}

func (s *Builder) Build(ctx context.Context) error {
if err := s.loadAndInitConfig(ctx); err != nil {
return err
Expand Down Expand Up @@ -400,6 +495,15 @@ func (s *Builder) buildRoute(ctx context.Context, builder *routeBuilder, consts
if err := s.loadSQL(ctx, builder, builder.session.sourceURL); err != nil {
return err
}
statePath := s.options.RelativePath
if statePath != "" {
statePath = path.Join(statePath, s.options.GoModulePkg)
} else {
statePath = s.options.DSQLOutput
}

state, err := codegen.NewState(statePath, builder.option.StateType, config.Config.LookupType)
fmt.Printf("%v %v\n", state, err)

if strings.TrimSpace(builder.sqlStmt) == "" {
return nil
Expand Down Expand Up @@ -756,7 +860,7 @@ func (s *Builder) loadSQL(ctx context.Context, builder *routeBuilder, location s
SQLbytes = []byte(templateContent)
}

SQL, err := s.prepareRuleIfNeeded(SQLbytes)
SQL, err := s.prepareRuleIfNeeded(ctx, SQLbytes)
if err != nil {
return err
}
Expand Down Expand Up @@ -1336,7 +1440,7 @@ func (s *Builder) inheritRouteServiceType(builder *routeBuilder, aView *view.Vie
}
}

func (s *Builder) prepareRuleIfNeeded(SQL []byte) (string, error) {
func (s *Builder) prepareRuleIfNeeded(ctx context.Context, SQL []byte) (string, error) {
if s.options.PrepareRule == "" {
return string(SQL), nil
}
Expand All @@ -1355,19 +1459,40 @@ func (s *Builder) prepareRuleIfNeeded(SQL []byte) (string, error) {
dsqlOutput = path.Join(dsqlOutput, s.options.GoModulePkg)
}

preparedRouteBuilder := s.newRouteBuilder(
routeBuilder := s.newRouteBuilder(
&router.Resource{Resource: view.EmptyResource()},
NewParametersIndex(nil, nil),
newSession(path.Dir(s.options.Location), s.options.Location, s.options.PluginDst, dsqlOutput, dsqlOutput, goFileOutput),
)

cmd := command.New()
template, err := s.buildCodeTemplate(ctx, routeBuilder, SQL, s.options.PrepareRule)
if err != nil {
return "", err
}
if err = cmd.Generate(ctx, s.Options.Generate, template); err != nil {
return "", err
}
SQL, err = s.fs.DownloadWithURL(ctx, s.Options.Generate.DSQLLocation())

return string(SQL), err
//
//
//dsql, err := template.GenerateDSQL()
//fmt.Printf("dsql: %v %v\n", dsql, err)
//state := template.GenerateState("")
//fmt.Printf("state %v\n", state)
//
//entity, err := template.GenerateEntity(ctx, "", nil)
//fmt.Printf("entity %s %v\n", entity, err)

switch strings.ToLower(s.options.PrepareRule) {
case PreparePost:
return s.preparePostRule(context.Background(), preparedRouteBuilder, SQL)
return s.preparePostRule(context.Background(), routeBuilder, SQL)
case PreparePatch:
return s.preparePatchRule(context.Background(), preparedRouteBuilder, SQL)
return s.preparePatchRule(context.Background(), routeBuilder, SQL)
case PreparePut:
return s.preparePutRule(context.Background(), preparedRouteBuilder, SQL)
return s.preparePutRule(context.Background(), routeBuilder, SQL)
default:
return "", fmt.Errorf("unsupported prepare rule type")
}
Expand All @@ -1379,7 +1504,7 @@ func (s *Builder) loadGoType(resource *view.Resource, typeSrc *option.TypeSrcCon
}
s.normalizeURL(typeSrc)

dirTypes, err := xreflect.ParseTypes(typeSrc.URL, xreflect.TypeLookupFn(config.Config.LookupType))
dirTypes, err := xreflect.ParseTypes(typeSrc.URL, xreflect.WithTypeLookupFn(config.Config.LookupType))
if err != nil {
return err
}
Expand Down Expand Up @@ -2026,6 +2151,14 @@ func (s *Builder) detectSinkColumn(ctx context.Context, db *sql.DB, SQL string)
return result, nil
}

func (s *Builder) uploadGoState(tmpl *codegen.Template, builder *routeBuilder, packageNBame string) error {
goURL := builder.session.GoFileURL("state") + ".go"
if _, err := s.upload(builder, goURL, tmpl.GenerateState(packageNBame)); err != nil {
return err
}
return nil
}

func combineURLs(basePath string, segments ...string) string {
basePath = strings.TrimRight(basePath, "/")
var actualSegments []string
Expand Down
18 changes: 12 additions & 6 deletions cmd/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (s *Builder) build() (*standalone.Server, error) {
return nil, err
}
if s.options.OpenApiURL != "" {
//TODO: add opeanpi3.Info to Config
//TODO: add opeanpi3.Spec to Config
openapiSpec, _ := router.GenerateOpenAPI3Spec(openapi3.Info{}, srv.Routes()...)
openApiMarshal, _ := yaml.Marshal(openapiSpec)
_ = os.WriteFile(s.options.OpenApiURL, openApiMarshal, file.DefaultFileOsMode)
Expand All @@ -98,8 +98,13 @@ func normalizeMetaTemplateSQL(SQL string, holderViewName string) string {
return strings.Replace(SQL, "$View."+holderViewName+".SQL", "$View.NonWindowSQL", 1)
}

func NewBuilder(options *Options, logger io.Writer) (*Builder, error) {
func NewBuilder(options *Options, opts *soptions.Options, logger io.Writer) (*Builder, error) {
if opts == nil {
opts = options.BuildOption()
}

builder := &Builder{
Options: opts,
options: options,
tablesMeta: NewTableMetaRegistry(),
logger: logger,
Expand All @@ -116,8 +121,9 @@ func NewBuilder(options *Options, logger io.Writer) (*Builder, error) {
func New(version string, args soptions.Arguments, logger io.Writer) (*standalone.Server, error) {
os.Setenv("AWS_SDK_LOAD_CONFIG", "true")
var options *Options
var opts *soptions.Options
if (args.SubMode() || args.IsHelp()) && !args.IsLegacy() {
opts := soptions.NewOptions(args)
opts = soptions.NewOptions(args)
if _, err := flags.ParseArgs(opts, args); err != nil {
return nil, err
}
Expand Down Expand Up @@ -171,10 +177,10 @@ func New(version string, args soptions.Arguments, logger io.Writer) (*standalone
if isOption("-h", args) {
return nil, nil
}
return runInLegacyMode(options, logger)
return runInLegacyMode(options, opts, logger)
}

func runInLegacyMode(options *Options, logger io.Writer) (*standalone.Server, error) {
func runInLegacyMode(options *Options, opts *soptions.Options, logger io.Writer) (*standalone.Server, error) {
var err error
if options.Package.RuleSourceURL != "" {
return nil, packageConfig(options)
Expand All @@ -188,7 +194,7 @@ func runInLegacyMode(options *Options, logger io.Writer) (*standalone.Server, er
return nil, err
}

builder, err := NewBuilder(options, logger)
builder, err := NewBuilder(options, opts, logger)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/command/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ func (s *Service) syncSourceDependencies(ctx context.Context, pkgLocation string
}

func (s *Service) getGoBinLocation(ctx context.Context) (string, error) {
if s.goLocation != "" {
return s.goLocation, nil
if s.goBinLocation != "" {
return s.goBinLocation, nil
}
goBinLocation, err := s.locateBinary(ctx, "go", "/usr/local/go/bin")
if err != nil {
Expand Down
74 changes: 74 additions & 0 deletions cmd/command/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package command

import (
"context"
"github.com/viant/afs/file"
"github.com/viant/datly/cmd/options"
"github.com/viant/datly/internal/codegen"
"github.com/viant/datly/internal/plugin"
"github.com/viant/datly/utils/formatter"
"github.com/viant/toolbox/format"
"strings"
)

func (s *Service) Generate(ctx context.Context, gen *options.Gen, template *codegen.Template) error {
if err := s.ensureDest(ctx, gen.Dest); err != nil {
return err
}
//TODO adjust if handler option is used
if err := s.generateDSQL(ctx, gen.DSQLLocation(), template); err != nil {
return err
}
info, err := plugin.NewInfo(ctx, gen.GoModuleLocation())
if err != nil {
return err
}
pkg := info.Package(gen.Package)
if err = s.generateState(ctx, pkg, gen, template); err != nil {
return err
}
if err = s.generateEntity(ctx, pkg, gen, info, template); err != nil {
return err
}
return nil
}

func (s *Service) generateEntity(ctx context.Context, pkg string, gen *options.Gen, info *plugin.Info, template *codegen.Template) error {
code, err := template.GenerateEntity(ctx, pkg, info)
if err != nil {
return err
}
entityName := ensureGoFileCaseFormat(template)

return s.fs.Upload(ctx, gen.EntityLocation(entityName), file.DefaultFileOsMode, strings.NewReader(code))
}

func ensureGoFileCaseFormat(template *codegen.Template) string {
entityName := template.Spec.Type.Name
if columnCase, err := format.NewCase(formatter.DetectCase(entityName)); err == nil {
entityName = columnCase.Format(entityName, format.CaseLowerUnderscore)
}
return entityName
}

func (s *Service) generateState(ctx context.Context, pkg string, gen *options.Gen, template *codegen.Template) error {
code := template.GenerateState(pkg)
return s.fs.Upload(ctx, gen.StateLocation(), file.DefaultFileOsMode, strings.NewReader(code))
}

func (s *Service) generateDSQL(ctx context.Context, URL string, template *codegen.Template) error {
DSQL, err := template.GenerateDSQL()
if err != nil {
return err
}
_ = s.fs.Delete(ctx, URL)
err = s.fs.Upload(ctx, URL, file.DefaultFileOsMode, strings.NewReader(DSQL))
return err
}

func (s *Service) ensureDest(ctx context.Context, URL string) error {
if ok, _ := s.fs.Exists(ctx, URL); ok {
return nil
}
return s.fs.Create(ctx, URL, file.DefaultDirOsMode, true)
}
Loading

0 comments on commit 0305678

Please sign in to comment.