(Feat): Initial Commit
This commit is contained in:
120
scripts/internal/handlers/check.go
Normal file
120
scripts/internal/handlers/check.go
Normal file
@@ -0,0 +1,120 @@
|
||||
//go:build scripts
|
||||
|
||||
// This program checks /internal/api/handlers.go and /internal/types/spec_handlers.go
|
||||
// It can be invoked by running go run -tags scripts scripts/handlers/check_handlers.go
|
||||
|
||||
// Supported args:
|
||||
// --print-all
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"allaboutapps.dev/aw/go-starter/internal/api"
|
||||
"allaboutapps.dev/aw/go-starter/internal/api/router"
|
||||
"allaboutapps.dev/aw/go-starter/internal/config"
|
||||
"allaboutapps.dev/aw/go-starter/internal/types"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func CheckHandlers(printAll bool) error {
|
||||
// we initialize a minimal echo server without any other deps
|
||||
// so we can attach the current defined routes and read them
|
||||
log.Logger = log.Output(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
||||
w.TimeFormat = "15:04:05"
|
||||
}))
|
||||
|
||||
defaultConfig := config.DefaultServiceConfigFromEnv()
|
||||
defaultConfig.Echo.ListenAddress = ":0"
|
||||
|
||||
s := api.NewServer(defaultConfig)
|
||||
err := router.Init(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize router: %w", err)
|
||||
}
|
||||
|
||||
// swaggerspec vs routes
|
||||
routes := s.Router.Routes
|
||||
swaggerSpec := types.NewSwaggerSpec()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, route := range routes {
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
// replace named echo ":param" to swagger path params "{param}" (curly braces) to properly match paths
|
||||
fragments := strings.Split(route.Path, "/")
|
||||
|
||||
for i, fragment := range fragments {
|
||||
if strings.HasPrefix(fragment, ":") {
|
||||
fragments[i] = "{" + strings.TrimLeft(fragment, ":") + "}"
|
||||
}
|
||||
}
|
||||
|
||||
swaggerPath := strings.Join(fragments, "/")
|
||||
|
||||
ok := swaggerSpec.Handlers[route.Method][swaggerPath]
|
||||
|
||||
if !ok {
|
||||
log.Warn().Msgf("%s %s\n WARNING: Missing swagger spec in api/swagger.yml!", route.Method, route.Path)
|
||||
} else if printAll {
|
||||
log.Info().Msgf("%s %s", route.Method, route.Path)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for method, v := range swaggerSpec.Handlers {
|
||||
for path := range v {
|
||||
// NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
|
||||
ttMethod := method
|
||||
ttPath := path
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
// replace named swagger path params "{param}" to echo ":param" (dotted) to properly match paths
|
||||
fragments := strings.Split(ttPath, "/")
|
||||
|
||||
for i, fragment := range fragments {
|
||||
if strings.HasPrefix(fragment, "{") && strings.HasSuffix(fragment, "}") {
|
||||
fragments[i] = ":" + strings.TrimLeft(strings.TrimRight(fragment, "}"), "{")
|
||||
}
|
||||
}
|
||||
|
||||
echoPath := strings.Join(fragments, "/")
|
||||
|
||||
hasMatch := false
|
||||
|
||||
for _, route := range routes {
|
||||
if route.Method == ttMethod && route.Path == echoPath {
|
||||
hasMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasMatch {
|
||||
log.Warn().Msgf("%s %s\n WARNING: Missing route implementation in internal/api/handlers/*!", ttMethod, echoPath)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// we have our routes, the server is no longer needed.
|
||||
if errs := s.Shutdown(context.Background()); len(errs) > 0 {
|
||||
log.Error().Errs("shutdownErrors", errs).Msg("Failed to stop introspection server")
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
183
scripts/internal/handlers/gen.go
Normal file
183
scripts/internal/handlers/gen.go
Normal file
@@ -0,0 +1,183 @@
|
||||
//go:build scripts
|
||||
|
||||
// This program generates /internal/api/handlers.go.
|
||||
// It can be invoked by running go run -tags scripts scripts/handlers/gen_handlers.go
|
||||
|
||||
// Supported args:
|
||||
// -print-only
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"allaboutapps.dev/aw/go-starter/scripts/internal/util"
|
||||
)
|
||||
|
||||
// https://blog.carlmjohnson.net/post/2016-11-27-how-to-use-go-generate/
|
||||
|
||||
var (
|
||||
handlersPackage = "/internal/api/handlers"
|
||||
|
||||
pathProjectRoot = util.GetProjectRootDir()
|
||||
pathHandlersRoot = pathProjectRoot + handlersPackage
|
||||
pathModFile = pathProjectRoot + "/go.mod"
|
||||
pathHandlersFile = pathHandlersRoot + "/handlers.go"
|
||||
|
||||
// methodPrefixes defines all keywords that we search for in the handlers sub packages
|
||||
// <methodPrefixes>*<methodSuffix> must be the naming of the route (Capitalized)
|
||||
methodPrefixes = []string{
|
||||
// https://swagger.io/specification/v2/ fixed fields: GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH
|
||||
"Get", "Put", "Post", "Delete", "Options", "Head", "Patch",
|
||||
}
|
||||
// Match suffixes like 'Route', 'RouteV1', 'RouteV123'
|
||||
methodSuffixPattern = regexp.MustCompile(`Route(V[0-9]*)?$`)
|
||||
|
||||
packageTemplate = template.Must(template.New("").Parse(`// Code generated by go run -tags scripts scripts/handlers/gen_handlers.go; DO NOT EDIT.
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"allaboutapps.dev/aw/go-starter/internal/api"
|
||||
{{- range .SubPkgs }}
|
||||
"{{ . }}"
|
||||
{{- end }}
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func AttachAllRoutes(s *api.Server) {
|
||||
// attach our routes
|
||||
s.Router.Routes = []*echo.Route{
|
||||
{{- range .Funcs }}
|
||||
{{ .PackageName }}.{{ .FunctionName }}(s),
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
`))
|
||||
)
|
||||
|
||||
type ResolvedFunction struct {
|
||||
FunctionName string
|
||||
PackageName string
|
||||
PackageNameFQDN string
|
||||
}
|
||||
|
||||
type TempateData struct {
|
||||
SubPkgs []string
|
||||
Funcs []ResolvedFunction
|
||||
}
|
||||
|
||||
// get all functions in above handler packages
|
||||
// that match <methodPrefixes>*<methodSuffix>
|
||||
func GenHandlers(printOnly bool) error {
|
||||
funcs := []ResolvedFunction{}
|
||||
|
||||
baseModuleName, err := util.GetModuleName(pathModFile)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to get module name")
|
||||
}
|
||||
|
||||
set := token.NewFileSet()
|
||||
|
||||
err = filepath.Walk(pathHandlersRoot, func(path string, fileInfo os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to access path %q: %w", path, err)
|
||||
}
|
||||
|
||||
// ignore handler file to be generated
|
||||
// ignore directories
|
||||
// ignore non go files
|
||||
// ignore test go files
|
||||
if path == pathHandlersFile || fileInfo.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "test.go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
gofile, err := parser.ParseFile(set, path, nil, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse package: %q: %w", path, err)
|
||||
}
|
||||
|
||||
fileDir := filepath.Dir(path)
|
||||
packageNameFQDN := strings.Replace(fileDir, pathProjectRoot, baseModuleName, 1)
|
||||
|
||||
for _, d := range gofile.Decls {
|
||||
if fn, isFn := d.(*ast.FuncDecl); isFn {
|
||||
fnName := fn.Name.String()
|
||||
|
||||
for _, prefix := range methodPrefixes {
|
||||
if strings.HasPrefix(fnName, prefix) && methodSuffixPattern.MatchString(fnName) {
|
||||
funcs = append(funcs, ResolvedFunction{
|
||||
FunctionName: fnName,
|
||||
PackageName: gofile.Name.Name,
|
||||
PackageNameFQDN: packageNameFQDN,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fmt.Println(packageNameFQDN, path)
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to walk handlers root: %w", err)
|
||||
}
|
||||
|
||||
// stable sort the functions
|
||||
// first PackageName then FunctionName
|
||||
sort.Slice(funcs, func(i, j int) bool {
|
||||
return funcs[i].PackageNameFQDN+funcs[i].FunctionName < funcs[j].PackageNameFQDN+funcs[j].FunctionName
|
||||
})
|
||||
|
||||
// only add subPkg if there was actually a route within it found
|
||||
subPkgs := []string{}
|
||||
for _, fun := range funcs {
|
||||
mustAppend := true
|
||||
|
||||
for _, a := range subPkgs {
|
||||
if a == fun.PackageNameFQDN {
|
||||
mustAppend = false
|
||||
}
|
||||
}
|
||||
|
||||
if mustAppend {
|
||||
subPkgs = append(subPkgs, fun.PackageNameFQDN)
|
||||
}
|
||||
}
|
||||
|
||||
if printOnly {
|
||||
for _, function := range funcs {
|
||||
log.Info().Msgf("%s %s", function.PackageNameFQDN, function.FunctionName)
|
||||
}
|
||||
|
||||
// bailout
|
||||
return nil
|
||||
}
|
||||
|
||||
handlersFile, err := os.Create(pathHandlersFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create handlers file: %w", err)
|
||||
}
|
||||
|
||||
defer handlersFile.Close()
|
||||
|
||||
if err = packageTemplate.Execute(handlersFile, TempateData{
|
||||
SubPkgs: subPkgs,
|
||||
Funcs: funcs,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to execute package template: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
367
scripts/internal/scaffold/scaffold.go
Normal file
367
scripts/internal/scaffold/scaffold.go
Normal file
@@ -0,0 +1,367 @@
|
||||
//go:build scripts
|
||||
|
||||
package scaffold
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// Scaffolding tool to auto-generate basic CRUD handlers for a given database model.
|
||||
|
||||
type FieldType struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Type FieldType
|
||||
}
|
||||
|
||||
type StorageResource struct {
|
||||
Name string
|
||||
Fields []Field
|
||||
}
|
||||
|
||||
func ParseModel(path string) (*StorageResource, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read model file: %w", err)
|
||||
}
|
||||
|
||||
regex, err := regexp.Compile(`type ([^\s]+) (struct {[^}]+})`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compile model expression regex: %w", err)
|
||||
}
|
||||
|
||||
matches := regex.FindStringSubmatch(string(content))
|
||||
resourceName := matches[1]
|
||||
expression := matches[2]
|
||||
|
||||
node, err := parser.ParseExpr(expression)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse model expression: %w", err)
|
||||
}
|
||||
|
||||
structType, ok := node.(*ast.StructType)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s is not a struct definition", expression)
|
||||
}
|
||||
|
||||
fields := []Field{}
|
||||
for _, field := range structType.Fields.List {
|
||||
name := field.Names[0].Name
|
||||
fieldType := expression[field.Type.Pos()-1 : field.Type.End()-1]
|
||||
field := Field{
|
||||
Name: name,
|
||||
Type: FieldType{
|
||||
Name: fieldType,
|
||||
},
|
||||
}
|
||||
|
||||
if field.Name == "R" || field.Name == "L" {
|
||||
break
|
||||
}
|
||||
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
resource := StorageResource{
|
||||
Name: resourceName,
|
||||
Fields: fields,
|
||||
}
|
||||
|
||||
return &resource, nil
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Name string
|
||||
Type string
|
||||
Required bool
|
||||
Format *string
|
||||
}
|
||||
|
||||
type SwaggerResource struct {
|
||||
Name string
|
||||
URLName string
|
||||
Properties []Property
|
||||
PayloadProperties []Property
|
||||
}
|
||||
|
||||
func GenerateSwagger(resource *StorageResource, outputPath string, force bool) error {
|
||||
definitionsPath := filepath.Join(outputPath, "definitions")
|
||||
pathsPath := filepath.Join(outputPath, "paths")
|
||||
if err := createDirIfAbsent(definitionsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := createDirIfAbsent(pathsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
swaggerResource := toSwaggerResource(resource)
|
||||
definitionsSpecPath := filepath.Join(definitionsPath, swaggerResource.URLName+".yml")
|
||||
pathsSpecPath := filepath.Join(pathsPath, swaggerResource.URLName+".yml")
|
||||
|
||||
if err := executeTemplate(swaggerDefinitionsTemplate, definitionsSpecPath, swaggerResource, force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return executeTemplate(swaggerPathsTemplate, pathsSpecPath, swaggerResource, force)
|
||||
}
|
||||
|
||||
var payloadExcluded = []string{"ID", "CreatedAt", "UpdatedAt"}
|
||||
|
||||
func toSwaggerResource(resource *StorageResource) *SwaggerResource {
|
||||
properties := make([]Property, 0, len(resource.Fields))
|
||||
payloadProperties := make([]Property, 0, len(resource.Fields))
|
||||
for _, field := range resource.Fields {
|
||||
property := fieldToProperty(field)
|
||||
properties = append(properties, property)
|
||||
|
||||
if !slices.Contains(payloadExcluded, field.Name) {
|
||||
payloadProperties = append(payloadProperties, property)
|
||||
}
|
||||
}
|
||||
|
||||
swaggerResource := SwaggerResource{
|
||||
Name: resource.Name,
|
||||
URLName: strings.ToLower(resource.Name), // TODO: Use dash separator
|
||||
Properties: properties,
|
||||
PayloadProperties: payloadProperties,
|
||||
}
|
||||
|
||||
return &swaggerResource
|
||||
}
|
||||
|
||||
const (
|
||||
propertyTypeString = "string"
|
||||
propertyTypeInteger = "integer"
|
||||
propertyTypeBoolean = "boolean"
|
||||
propertyTypeNumber = "number"
|
||||
propertyTypeDateTime = "date-time"
|
||||
propertyTypeUUID4 = "uuid4"
|
||||
)
|
||||
|
||||
func fieldToProperty(field Field) Property {
|
||||
propertyType := propertyTypeString // Fallback type
|
||||
required := true
|
||||
var format *string
|
||||
|
||||
switch field.Type.Name {
|
||||
case "int":
|
||||
propertyType = propertyTypeInteger
|
||||
case "null.Int":
|
||||
propertyType = propertyTypeInteger
|
||||
required = false
|
||||
case "bool":
|
||||
propertyType = propertyTypeBoolean
|
||||
case "null.Bool":
|
||||
propertyType = propertyTypeBoolean
|
||||
required = false
|
||||
case "null.String":
|
||||
propertyType = propertyTypeString
|
||||
required = false
|
||||
case "types.Decimal":
|
||||
propertyType = propertyTypeNumber
|
||||
case "types.NullDecimal":
|
||||
propertyType = propertyTypeNumber
|
||||
required = false
|
||||
case "time.Time":
|
||||
format = swag.String(propertyTypeDateTime)
|
||||
case "null.Time":
|
||||
format = swag.String(propertyTypeDateTime)
|
||||
required = false
|
||||
}
|
||||
|
||||
if strings.Contains(field.Name, "ID") {
|
||||
format = swag.String(propertyTypeUUID4)
|
||||
}
|
||||
|
||||
return Property{
|
||||
Name: goToJSNaming(field.Name),
|
||||
Type: propertyType,
|
||||
Required: required,
|
||||
Format: format,
|
||||
}
|
||||
}
|
||||
|
||||
type HandlerField struct {
|
||||
Name string
|
||||
Value string
|
||||
PlaceholderValue string
|
||||
}
|
||||
|
||||
type HandlerResource struct {
|
||||
Name string
|
||||
Fields []HandlerField
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
Module string
|
||||
Package string
|
||||
Resource *HandlerResource
|
||||
}
|
||||
|
||||
func toHandlerResource(storageResource *StorageResource, swaggerResource *SwaggerResource) *HandlerResource {
|
||||
fields := make([]HandlerField, len(swaggerResource.Properties))
|
||||
for i, property := range swaggerResource.Properties {
|
||||
fields[i] = propertyToHandlerField(property)
|
||||
|
||||
// Hack to get the proper field name.
|
||||
fields[i].Name = storageResource.Fields[i].Name
|
||||
}
|
||||
|
||||
return &HandlerResource{
|
||||
Name: swaggerResource.Name,
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
func propertyToHandlerField(property Property) HandlerField {
|
||||
placeholderValue := `swag.String("` + property.Name + `")` // Fallback placeholder
|
||||
|
||||
switch property.Type {
|
||||
case "integer":
|
||||
placeholderValue = `swag.Int64(100)`
|
||||
case "boolean":
|
||||
placeholderValue = `swag.Bool(true)`
|
||||
case "number":
|
||||
placeholderValue = `swag.Float64(10.0)`
|
||||
}
|
||||
|
||||
if property.Format != nil {
|
||||
switch *property.Format {
|
||||
case "date-time":
|
||||
placeholderValue = `conv.DateTime(strfmt.DateTime(time.Now()))`
|
||||
|
||||
case "uuid4":
|
||||
placeholderValue = `conv.UUID4(strfmt.UUID4("1606388b-1f88-4f56-bd97-c27fbc3fe080"))`
|
||||
}
|
||||
}
|
||||
|
||||
return HandlerField{
|
||||
Name: property.Name,
|
||||
PlaceholderValue: placeholderValue,
|
||||
}
|
||||
}
|
||||
|
||||
type handlerConfig struct {
|
||||
filePrefix string
|
||||
fileSuffix string
|
||||
template string
|
||||
}
|
||||
|
||||
var configuredHandlers = map[string]handlerConfig{
|
||||
"get-all": {"get_", "_list.go", getListHandlerTemplate},
|
||||
"get": {"get_", ".go", getHandlerTemplate},
|
||||
"post": {"post_", ".go", postHandlerTemplate},
|
||||
"put": {"put_", ".go", putHandlerTemplate},
|
||||
"delete": {"delete_", ".go", deleteHandlerTemplate},
|
||||
}
|
||||
|
||||
func GenerateHandlers(resource *StorageResource, handlerBaseDir, modulePath string, methods []string, force bool) error {
|
||||
packageName := strings.ToLower(resource.Name)
|
||||
resourceBaseDir := filepath.Join(handlerBaseDir, packageName)
|
||||
|
||||
if _, err := os.Stat(resourceBaseDir); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(resourceBaseDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create resource base directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
module, err := getModuleName(modulePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
handler := Handler{
|
||||
Module: module,
|
||||
Package: packageName,
|
||||
Resource: toHandlerResource(resource, toSwaggerResource(resource)),
|
||||
}
|
||||
|
||||
for _, method := range methods {
|
||||
handlerConfig, ok := configuredHandlers[method]
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported method: %s", method)
|
||||
}
|
||||
|
||||
outputPath := filepath.Join(resourceBaseDir, handlerConfig.filePrefix+packageName+handlerConfig.fileSuffix)
|
||||
if err := executeTemplate(handlerConfig.template, outputPath, handler, force); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func createDirIfAbsent(path string) error {
|
||||
if !fileExists(path) {
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory '%s': %w", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func executeTemplate(templateStr, outputPath string, data interface{}, force bool) error {
|
||||
templ := template.Template{}
|
||||
if _, err := templ.Parse(templateStr); err != nil {
|
||||
return fmt.Errorf("failed to parse template: %w", err)
|
||||
}
|
||||
|
||||
if !force && fileExists(outputPath) {
|
||||
return fmt.Errorf("file '%s' already exists; call with --force to overwrite", outputPath)
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(outputPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file '%s': %w", outputPath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := templ.Execute(file, data); err != nil {
|
||||
return fmt.Errorf("failed to execute template: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func goToJSNaming(name string) string {
|
||||
// Hack to properly handle ID.
|
||||
switch name {
|
||||
case "ID":
|
||||
return "id"
|
||||
default:
|
||||
first := strings.ToLower(name[0:1])
|
||||
return first + name[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func getModuleName(absolutePathToGoMod string) (string, error) {
|
||||
dat, err := os.ReadFile(absolutePathToGoMod)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read go.mod: %w", err)
|
||||
}
|
||||
|
||||
mod := modfile.ModulePath(dat)
|
||||
|
||||
return mod, nil
|
||||
}
|
||||
86
scripts/internal/scaffold/scaffold_test.go
Normal file
86
scripts/internal/scaffold/scaffold_test.go
Normal file
@@ -0,0 +1,86 @@
|
||||
//go:build scripts
|
||||
|
||||
package scaffold_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"allaboutapps.dev/aw/go-starter/internal/test"
|
||||
"allaboutapps.dev/aw/go-starter/internal/util"
|
||||
"allaboutapps.dev/aw/go-starter/scripts/internal/scaffold"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
resourcePath = "testdata/test_resource.txt"
|
||||
definitionsPath = "testdata/definitions"
|
||||
pathsPath = "testdata/paths"
|
||||
handlerPath = "testdata/testresource"
|
||||
modulePath = filepath.Join(util.GetProjectRootDir(), "go.mod")
|
||||
|
||||
defaultMethods = []string{"get-all", "get", "post", "put", "delete"}
|
||||
)
|
||||
|
||||
func TestParseModel_Success(t *testing.T) {
|
||||
// Execute
|
||||
resource, err := scaffold.ParseModel(resourcePath)
|
||||
|
||||
// Assert
|
||||
require.NoError(t, err)
|
||||
test.Snapshoter.Save(t, resource)
|
||||
}
|
||||
|
||||
func TestGenerateSwagger_Success(t *testing.T) {
|
||||
// Setup
|
||||
resource, err := scaffold.ParseModel(resourcePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = scaffold.GenerateSwagger(resource, "testdata", true)
|
||||
|
||||
// Assert
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.DirExists(t, definitionsPath, "Should create the definitions directory")
|
||||
assert.DirExists(t, pathsPath, "Should create the paths directory")
|
||||
assert.FileExists(t, filepath.Join(definitionsPath, "testresource.yml"), "Should generate the definition spec")
|
||||
assert.FileExists(t, filepath.Join(pathsPath, "testresource.yml"), "Should generate the paths spec")
|
||||
|
||||
// Cleanup
|
||||
err = os.RemoveAll(definitionsPath)
|
||||
require.NoError(t, err)
|
||||
err = os.RemoveAll(pathsPath)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGenerateHandlers_Success(t *testing.T) {
|
||||
// Setup
|
||||
resource, err := scaffold.ParseModel(resourcePath)
|
||||
require.NoError(t, err)
|
||||
err = scaffold.GenerateSwagger(resource, "testdata", true)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Execute
|
||||
err = scaffold.GenerateHandlers(resource, "testdata", modulePath, defaultMethods, true)
|
||||
|
||||
// Assert
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.DirExists(t, handlerPath, "Should create the handler directory")
|
||||
assert.FileExists(t, filepath.Join(handlerPath, "get_testresource_list.go"), "Should create the GET list handler")
|
||||
assert.FileExists(t, filepath.Join(handlerPath, "get_testresource.go"), "Should create the GET handler")
|
||||
assert.FileExists(t, filepath.Join(handlerPath, "post_testresource.go"), "Should create the POST handler")
|
||||
assert.FileExists(t, filepath.Join(handlerPath, "put_testresource.go"), "Should create the PUT handler")
|
||||
assert.FileExists(t, filepath.Join(handlerPath, "delete_testresource.go"), "Should create the DELETE handler")
|
||||
|
||||
// Cleanup
|
||||
err = os.RemoveAll(definitionsPath)
|
||||
require.NoError(t, err)
|
||||
err = os.RemoveAll(pathsPath)
|
||||
require.NoError(t, err)
|
||||
err = os.RemoveAll(handlerPath)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
366
scripts/internal/scaffold/templates.go
Normal file
366
scripts/internal/scaffold/templates.go
Normal file
@@ -0,0 +1,366 @@
|
||||
//go:build scripts
|
||||
|
||||
package scaffold
|
||||
|
||||
const (
|
||||
swaggerDefinitionsTemplate = `swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 0.1.0
|
||||
paths: {}
|
||||
definitions:
|
||||
{{ .Name }}:
|
||||
type: object
|
||||
required:
|
||||
{{- range .Properties}}
|
||||
{{- if .Required }}
|
||||
- {{ .Name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
properties:
|
||||
{{- range .Properties}}
|
||||
{{ .Name }}:
|
||||
type: {{ .Type }}
|
||||
{{- if not .Required }}
|
||||
x-nullable: true
|
||||
{{- end }}
|
||||
{{- if .Format }}
|
||||
format: {{ .Format }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ .Name }}Payload:
|
||||
type: object
|
||||
required:
|
||||
{{- range .PayloadProperties}}
|
||||
{{- if .Required }}
|
||||
- {{ .Name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
properties:
|
||||
{{- range .PayloadProperties}}
|
||||
{{ .Name }}:
|
||||
type: {{ .Type }}
|
||||
{{- if not .Required }}
|
||||
x-nullable: true
|
||||
{{- end }}
|
||||
{{- if .Format }}
|
||||
format: {{ .Format }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ .Name }}List:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/{{ .Name }}"
|
||||
`
|
||||
|
||||
swaggerPathsTemplate = `swagger: "2.0"
|
||||
info:
|
||||
title: ""
|
||||
version: 0.1.0
|
||||
parameters:
|
||||
{{ .Name }}IdParam:
|
||||
type: string
|
||||
format: uuid4
|
||||
name: id
|
||||
description: ID of {{ .Name }}
|
||||
in: path
|
||||
required: true
|
||||
|
||||
paths:
|
||||
/api/v1/{{ .URLName }}s:
|
||||
get:
|
||||
security:
|
||||
- Bearer: []
|
||||
description: "Return a list of {{ .Name }}"
|
||||
tags:
|
||||
- {{ .Name }}
|
||||
summary: "Return a list of {{ .Name }}"
|
||||
operationId: Get{{ .Name }}ListRoute
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
schema:
|
||||
$ref: "../definitions/{{ .URLName }}.yml#/definitions/{{ .Name }}List"
|
||||
|
||||
post:
|
||||
security:
|
||||
- Bearer: []
|
||||
description: "Update the given {{ .Name }}"
|
||||
tags:
|
||||
- {{ .Name }}
|
||||
summary: "Update the given {{ .Name }}"
|
||||
operationId: Post{{ .Name }}Route
|
||||
parameters:
|
||||
- name: Payload
|
||||
in: body
|
||||
schema:
|
||||
$ref: "../definitions/{{ .URLName }}.yml#/definitions/{{ .Name }}Payload"
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
schema:
|
||||
$ref: "../definitions/{{ .URLName }}.yml#/definitions/{{ .Name }}"
|
||||
|
||||
/api/v1/{{ .URLName }}s/{id}:
|
||||
get:
|
||||
security:
|
||||
- Bearer: []
|
||||
description: "Return {{ .Name }} with ID"
|
||||
tags:
|
||||
- {{ .Name }}
|
||||
summary: "Return {{ .Name }} with ID"
|
||||
operationId: Get{{ .Name }}Route
|
||||
parameters:
|
||||
- $ref: "#/parameters/{{ .Name }}IdParam"
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
schema:
|
||||
$ref: "../definitions/{{ .URLName }}.yml#/definitions/{{ .Name }}"
|
||||
|
||||
put:
|
||||
security:
|
||||
- Bearer: []
|
||||
description: "Update the given {{ .Name }}"
|
||||
tags:
|
||||
- {{ .Name }}
|
||||
summary: "Update the given {{ .Name }}"
|
||||
operationId: Put{{ .Name }}Route
|
||||
parameters:
|
||||
- $ref: "#/parameters/{{ .Name }}IdParam"
|
||||
- name: Payload
|
||||
in: body
|
||||
schema:
|
||||
$ref: "../definitions/{{ .URLName }}.yml#/definitions/{{ .Name }}Payload"
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
schema:
|
||||
$ref: "../definitions/{{ .URLName }}.yml#/definitions/{{ .Name }}"
|
||||
|
||||
delete:
|
||||
security:
|
||||
- Bearer: []
|
||||
description: "Delete {{ .Name }} with ID"
|
||||
tags:
|
||||
- {{ .Name }}
|
||||
summary: "Delete {{ .Name }} with ID"
|
||||
operationId: Delete{{ .Name }}Route
|
||||
parameters:
|
||||
- $ref: "#/parameters/{{ .Name }}IdParam"
|
||||
responses:
|
||||
"204":
|
||||
description: Success
|
||||
`
|
||||
|
||||
getHandlerTemplate = `package {{ .Package }}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"{{ .Module }}/internal/api"
|
||||
"{{ .Module }}/internal/types"
|
||||
"{{ .Module }}/internal/util"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/strfmt/conv"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func Get{{ .Resource.Name }}Route(s *api.Server) *echo.Route {
|
||||
return s.Router.APIV1{{ .Resource.Name }}.GET("/:id", get{{ .Resource.Name }}Handler(s))
|
||||
}
|
||||
|
||||
func get{{ .Resource.Name }}Handler(s *api.Server) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
/* Uncomment for real implementation
|
||||
ctx := c.Request().Context()
|
||||
|
||||
params := {{ .Package }}.NewGet{{ .Resource.Name }}RouteParams()
|
||||
err := util.BindAndValidatePathParams(c, ¶ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := params.ID.String()
|
||||
|
||||
// TODO: Implement
|
||||
*/
|
||||
|
||||
response := types.{{ .Resource.Name }}{
|
||||
{{- range .Resource.Fields }}
|
||||
{{ .Name }}: {{ .PlaceholderValue }},
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
return util.ValidateAndReturn(c, http.StatusOK, &response)
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
getListHandlerTemplate = `package {{ .Package }}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"{{ .Module }}/internal/api"
|
||||
"{{ .Module }}/internal/types"
|
||||
"{{ .Module }}/internal/util"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/strfmt/conv"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func Get{{ .Resource.Name }}ListRoute(s *api.Server) *echo.Route {
|
||||
return s.Router.APIV1{{ .Resource.Name }}.GET("", get{{ .Resource.Name }}ListHandler(s))
|
||||
}
|
||||
|
||||
func get{{ .Resource.Name }}ListHandler(s *api.Server) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
/* Uncomment for real implementation
|
||||
ctx := c.Request().Context()
|
||||
|
||||
// TODO: Implement
|
||||
*/
|
||||
|
||||
item := types.{{ .Resource.Name }}{
|
||||
{{- range .Resource.Fields }}
|
||||
{{ .Name }}: {{ .PlaceholderValue }},
|
||||
{{- end }}
|
||||
}
|
||||
response := types.{{ .Resource.Name }}List{&item}
|
||||
|
||||
return util.ValidateAndReturn(c, http.StatusOK, &response)
|
||||
}
|
||||
}
|
||||
`
|
||||
postHandlerTemplate = `package {{ .Package }}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"{{ .Module }}/internal/api"
|
||||
"{{ .Module }}/internal/types"
|
||||
"{{ .Module }}/internal/util"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/strfmt/conv"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func Post{{ .Resource.Name }}Route(s *api.Server) *echo.Route {
|
||||
return s.Router.APIV1{{ .Resource.Name }}.POST("", post{{ .Resource.Name }}Handler(s))
|
||||
}
|
||||
|
||||
func post{{ .Resource.Name }}Handler(s *api.Server) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
/* Uncomment for real implementation
|
||||
ctx := c.Request().Context()
|
||||
|
||||
var body types.{{ .Resource.Name }}Payload
|
||||
err := util.BindAndValidateBody(c, &body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
*/
|
||||
|
||||
response := types.{{ .Resource.Name }}{
|
||||
{{- range .Resource.Fields }}
|
||||
{{ .Name }}: {{ .PlaceholderValue }},
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
return util.ValidateAndReturn(c, http.StatusOK, &response)
|
||||
}
|
||||
}
|
||||
`
|
||||
putHandlerTemplate = `package {{ .Package }}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"{{ .Module }}/internal/api"
|
||||
"{{ .Module }}/internal/types"
|
||||
"{{ .Module }}/internal/util"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/strfmt/conv"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func Put{{ .Resource.Name }}Route(s *api.Server) *echo.Route {
|
||||
return s.Router.APIV1{{ .Resource.Name }}.PUT("/:id", put{{ .Resource.Name }}Handler(s))
|
||||
}
|
||||
|
||||
func put{{ .Resource.Name }}Handler(s *api.Server) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
/* Uncomment for real implementation
|
||||
ctx := c.Request().Context()
|
||||
|
||||
params := {{ .Package }}.NewGet{{ .Resource.Name }}RouteParams()
|
||||
err := util.BindAndValidatePathParams(c, ¶ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := params.ID.String()
|
||||
|
||||
var body types.{{ .Resource.Name }}Payload
|
||||
err = util.BindAndValidateBody(c, &body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
*/
|
||||
|
||||
response := types.{{ .Resource.Name }}{
|
||||
{{- range .Resource.Fields }}
|
||||
{{ .Name }}: {{ .PlaceholderValue }},
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
return util.ValidateAndReturn(c, http.StatusOK, &response)
|
||||
}
|
||||
}
|
||||
`
|
||||
deleteHandlerTemplate = `package {{ .Package }}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"{{ .Module }}/internal/api"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func Delete{{ .Resource.Name }}Route(s *api.Server) *echo.Route {
|
||||
return s.Router.APIV1{{ .Resource.Name }}.DELETE("/:id", delete{{ .Resource.Name }}Handler(s))
|
||||
}
|
||||
|
||||
func delete{{ .Resource.Name }}Handler(s *api.Server) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
/* Uncomment for real implementation
|
||||
ctx := c.Request().Context()
|
||||
|
||||
params := {{ .Package }}.NewGet{{ .Resource.Name }}RouteParams()
|
||||
err := util.BindAndValidatePathParams(c, ¶ms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := params.ID.String()
|
||||
|
||||
// TODO: Implement
|
||||
*/
|
||||
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
758
scripts/internal/scaffold/testdata/test_resource.txt
vendored
Normal file
758
scripts/internal/scaffold/testdata/test_resource.txt
vendored
Normal file
@@ -0,0 +1,758 @@
|
||||
// Code generated by SQLBoiler 4.5.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT.
|
||||
// This file is meant to be re-generated in place and/or deleted at any time.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/friendsofgo/errors"
|
||||
"github.com/volatiletech/null/v8"
|
||||
"github.com/volatiletech/sqlboiler/v4/boil"
|
||||
"github.com/volatiletech/sqlboiler/v4/queries"
|
||||
"github.com/volatiletech/sqlboiler/v4/queries/qm"
|
||||
"github.com/volatiletech/sqlboiler/v4/queries/qmhelper"
|
||||
"github.com/volatiletech/sqlboiler/v4/types"
|
||||
"github.com/volatiletech/strmangle"
|
||||
)
|
||||
|
||||
// TestResource is an object representing the database table.
|
||||
type TestResource struct {
|
||||
ID string `boil:"id" json:"id" toml:"id" yaml:"id"`
|
||||
NumericField types.Decimal `boil:"numeric_field" json:"numeric_field" toml:"numeric_field" yaml:"numeric_field"`
|
||||
NumericNullField types.NullDecimal `boil:"numeric_null_field" json:"numeric_null_field,omitempty" toml:"numeric_null_field" yaml:"numeric_null_field,omitempty"`
|
||||
IntegerField int `boil:"integer_field" json:"integer_field" toml:"integer_field" yaml:"integer_field"`
|
||||
IntegerNullField null.Int `boil:"integer_null_field" json:"integer_null_field,omitempty" toml:"integer_null_field" yaml:"integer_null_field,omitempty"`
|
||||
BoolField bool `boil:"bool_field" json:"bool_field" toml:"bool_field" yaml:"bool_field"`
|
||||
BoolNullField null.Bool `boil:"bool_null_field" json:"bool_null_field,omitempty" toml:"bool_null_field" yaml:"bool_null_field,omitempty"`
|
||||
DecimalField types.Decimal `boil:"decimal_field" json:"decimal_field" toml:"decimal_field" yaml:"decimal_field"`
|
||||
DecimalNullField types.NullDecimal `boil:"decimal_null_field" json:"decimal_null_field,omitempty" toml:"decimal_null_field" yaml:"decimal_null_field,omitempty"`
|
||||
TextField string `boil:"text_field" json:"text_field" toml:"text_field" yaml:"text_field"`
|
||||
TextNullField null.String `boil:"text_null_field" json:"text_null_field,omitempty" toml:"text_null_field" yaml:"text_null_field,omitempty"`
|
||||
TimtestamptzNullField null.Time `boil:"timtestamptz_null_field" json:"timtestamptz_null_field,omitempty" toml:"timtestamptz_null_field" yaml:"timtestamptz_null_field,omitempty"`
|
||||
CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
|
||||
UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
|
||||
|
||||
R *testResourceR `boil:"-" json:"-" toml:"-" yaml:"-"`
|
||||
L testResourceL `boil:"-" json:"-" toml:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
var TestResourceColumns = struct {
|
||||
ID string
|
||||
NumericField string
|
||||
NumericNullField string
|
||||
IntegerField string
|
||||
IntegerNullField string
|
||||
BoolField string
|
||||
BoolNullField string
|
||||
DecimalField string
|
||||
DecimalNullField string
|
||||
TextField string
|
||||
TextNullField string
|
||||
TimtestamptzNullField string
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
}{
|
||||
ID: "id",
|
||||
NumericField: "numeric_field",
|
||||
NumericNullField: "numeric_null_field",
|
||||
IntegerField: "integer_field",
|
||||
IntegerNullField: "integer_null_field",
|
||||
BoolField: "bool_field",
|
||||
BoolNullField: "bool_null_field",
|
||||
DecimalField: "decimal_field",
|
||||
DecimalNullField: "decimal_null_field",
|
||||
TextField: "text_field",
|
||||
TextNullField: "text_null_field",
|
||||
TimtestamptzNullField: "timtestamptz_null_field",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
}
|
||||
|
||||
// Generated where
|
||||
|
||||
type whereHelpernull_Bool struct{ field string }
|
||||
|
||||
func (w whereHelpernull_Bool) EQ(x null.Bool) qm.QueryMod {
|
||||
return qmhelper.WhereNullEQ(w.field, false, x)
|
||||
}
|
||||
func (w whereHelpernull_Bool) NEQ(x null.Bool) qm.QueryMod {
|
||||
return qmhelper.WhereNullEQ(w.field, true, x)
|
||||
}
|
||||
func (w whereHelpernull_Bool) IsNull() qm.QueryMod { return qmhelper.WhereIsNull(w.field) }
|
||||
func (w whereHelpernull_Bool) IsNotNull() qm.QueryMod { return qmhelper.WhereIsNotNull(w.field) }
|
||||
func (w whereHelpernull_Bool) LT(x null.Bool) qm.QueryMod {
|
||||
return qmhelper.Where(w.field, qmhelper.LT, x)
|
||||
}
|
||||
func (w whereHelpernull_Bool) LTE(x null.Bool) qm.QueryMod {
|
||||
return qmhelper.Where(w.field, qmhelper.LTE, x)
|
||||
}
|
||||
func (w whereHelpernull_Bool) GT(x null.Bool) qm.QueryMod {
|
||||
return qmhelper.Where(w.field, qmhelper.GT, x)
|
||||
}
|
||||
func (w whereHelpernull_Bool) GTE(x null.Bool) qm.QueryMod {
|
||||
return qmhelper.Where(w.field, qmhelper.GTE, x)
|
||||
}
|
||||
|
||||
var TestResourceWhere = struct {
|
||||
ID whereHelperstring
|
||||
NumericField whereHelpertypes_Decimal
|
||||
NumericNullField whereHelpertypes_NullDecimal
|
||||
IntegerField whereHelperint
|
||||
IntegerNullField whereHelpernull_Int
|
||||
BoolField whereHelperbool
|
||||
BoolNullField whereHelpernull_Bool
|
||||
DecimalField whereHelpertypes_Decimal
|
||||
DecimalNullField whereHelpertypes_NullDecimal
|
||||
TextField whereHelperstring
|
||||
TextNullField whereHelpernull_String
|
||||
TimtestamptzNullField whereHelpernull_Time
|
||||
CreatedAt whereHelpertime_Time
|
||||
UpdatedAt whereHelpertime_Time
|
||||
}{
|
||||
ID: whereHelperstring{field: "\"test_resource\".\"id\""},
|
||||
NumericField: whereHelpertypes_Decimal{field: "\"test_resource\".\"numeric_field\""},
|
||||
NumericNullField: whereHelpertypes_NullDecimal{field: "\"test_resource\".\"numeric_null_field\""},
|
||||
IntegerField: whereHelperint{field: "\"test_resource\".\"integer_field\""},
|
||||
IntegerNullField: whereHelpernull_Int{field: "\"test_resource\".\"integer_null_field\""},
|
||||
BoolField: whereHelperbool{field: "\"test_resource\".\"bool_field\""},
|
||||
BoolNullField: whereHelpernull_Bool{field: "\"test_resource\".\"bool_null_field\""},
|
||||
DecimalField: whereHelpertypes_Decimal{field: "\"test_resource\".\"decimal_field\""},
|
||||
DecimalNullField: whereHelpertypes_NullDecimal{field: "\"test_resource\".\"decimal_null_field\""},
|
||||
TextField: whereHelperstring{field: "\"test_resource\".\"text_field\""},
|
||||
TextNullField: whereHelpernull_String{field: "\"test_resource\".\"text_null_field\""},
|
||||
TimtestamptzNullField: whereHelpernull_Time{field: "\"test_resource\".\"timtestamptz_null_field\""},
|
||||
CreatedAt: whereHelpertime_Time{field: "\"test_resource\".\"created_at\""},
|
||||
UpdatedAt: whereHelpertime_Time{field: "\"test_resource\".\"updated_at\""},
|
||||
}
|
||||
|
||||
// TestResourceRels is where relationship names are stored.
|
||||
var TestResourceRels = struct {
|
||||
}{}
|
||||
|
||||
// testResourceR is where relationships are stored.
|
||||
type testResourceR struct {
|
||||
}
|
||||
|
||||
// NewStruct creates a new relationship struct
|
||||
func (*testResourceR) NewStruct() *testResourceR {
|
||||
return &testResourceR{}
|
||||
}
|
||||
|
||||
// testResourceL is where Load methods for each relationship are stored.
|
||||
type testResourceL struct{}
|
||||
|
||||
var (
|
||||
testResourceAllColumns = []string{"id", "numeric_field", "numeric_null_field", "integer_field", "integer_null_field", "bool_field", "bool_null_field", "decimal_field", "decimal_null_field", "text_field", "text_null_field", "timtestamptz_null_field", "created_at", "updated_at"}
|
||||
testResourceColumnsWithoutDefault = []string{"id", "numeric_field", "numeric_null_field", "integer_field", "integer_null_field", "bool_field", "bool_null_field", "decimal_field", "decimal_null_field", "text_field", "text_null_field", "timtestamptz_null_field", "created_at", "updated_at"}
|
||||
testResourceColumnsWithDefault = []string{}
|
||||
testResourcePrimaryKeyColumns = []string{"id"}
|
||||
)
|
||||
|
||||
type (
|
||||
// TestResourceSlice is an alias for a slice of pointers to TestResource.
|
||||
// This should generally be used opposed to []TestResource.
|
||||
TestResourceSlice []*TestResource
|
||||
|
||||
testResourceQuery struct {
|
||||
*queries.Query
|
||||
}
|
||||
)
|
||||
|
||||
// Cache for insert, update and upsert
|
||||
var (
|
||||
testResourceType = reflect.TypeOf(&TestResource{})
|
||||
testResourceMapping = queries.MakeStructMapping(testResourceType)
|
||||
testResourcePrimaryKeyMapping, _ = queries.BindMapping(testResourceType, testResourceMapping, testResourcePrimaryKeyColumns)
|
||||
testResourceInsertCacheMut sync.RWMutex
|
||||
testResourceInsertCache = make(map[string]insertCache)
|
||||
testResourceUpdateCacheMut sync.RWMutex
|
||||
testResourceUpdateCache = make(map[string]updateCache)
|
||||
testResourceUpsertCacheMut sync.RWMutex
|
||||
testResourceUpsertCache = make(map[string]insertCache)
|
||||
)
|
||||
|
||||
var (
|
||||
// Force time package dependency for automated UpdatedAt/CreatedAt.
|
||||
_ = time.Second
|
||||
// Force qmhelper dependency for where clause generation (which doesn't
|
||||
// always happen)
|
||||
_ = qmhelper.Where
|
||||
)
|
||||
|
||||
// One returns a single testResource record from the query.
|
||||
func (q testResourceQuery) One(ctx context.Context, exec boil.ContextExecutor) (*TestResource, error) {
|
||||
o := &TestResource{}
|
||||
|
||||
queries.SetLimit(q.Query, 1)
|
||||
|
||||
err := q.Bind(ctx, exec, o)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
return nil, errors.Wrap(err, "models: failed to execute a one query for test_resource")
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// All returns all TestResource records from the query.
|
||||
func (q testResourceQuery) All(ctx context.Context, exec boil.ContextExecutor) (TestResourceSlice, error) {
|
||||
var o []*TestResource
|
||||
|
||||
err := q.Bind(ctx, exec, &o)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "models: failed to assign all query results to TestResource slice")
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// Count returns the count of all TestResource records in the query.
|
||||
func (q testResourceQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
|
||||
var count int64
|
||||
|
||||
queries.SetSelect(q.Query, nil)
|
||||
queries.SetCount(q.Query)
|
||||
|
||||
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: failed to count test_resource rows")
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// Exists checks if the row exists in the table.
|
||||
func (q testResourceQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) {
|
||||
var count int64
|
||||
|
||||
queries.SetSelect(q.Query, nil)
|
||||
queries.SetCount(q.Query)
|
||||
queries.SetLimit(q.Query, 1)
|
||||
|
||||
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "models: failed to check if test_resource exists")
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// TestResources retrieves all the records using an executor.
|
||||
func TestResources(mods ...qm.QueryMod) testResourceQuery {
|
||||
mods = append(mods, qm.From("\"test_resource\""))
|
||||
return testResourceQuery{NewQuery(mods...)}
|
||||
}
|
||||
|
||||
// FindTestResource retrieves a single record by ID with an executor.
|
||||
// If selectCols is empty Find will return all columns.
|
||||
func FindTestResource(ctx context.Context, exec boil.ContextExecutor, iD string, selectCols ...string) (*TestResource, error) {
|
||||
testResourceObj := &TestResource{}
|
||||
|
||||
sel := "*"
|
||||
if len(selectCols) > 0 {
|
||||
sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",")
|
||||
}
|
||||
query := fmt.Sprintf(
|
||||
"select %s from \"test_resource\" where \"id\"=$1", sel,
|
||||
)
|
||||
|
||||
q := queries.Raw(query, iD)
|
||||
|
||||
err := q.Bind(ctx, exec, testResourceObj)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
return nil, errors.Wrap(err, "models: unable to select from test_resource")
|
||||
}
|
||||
|
||||
return testResourceObj, nil
|
||||
}
|
||||
|
||||
// Insert a single record using an executor.
|
||||
// See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts.
|
||||
func (o *TestResource) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error {
|
||||
if o == nil {
|
||||
return errors.New("models: no test_resource provided for insertion")
|
||||
}
|
||||
|
||||
var err error
|
||||
if !boil.TimestampsAreSkipped(ctx) {
|
||||
currTime := time.Now().In(boil.GetLocation())
|
||||
|
||||
if o.CreatedAt.IsZero() {
|
||||
o.CreatedAt = currTime
|
||||
}
|
||||
if o.UpdatedAt.IsZero() {
|
||||
o.UpdatedAt = currTime
|
||||
}
|
||||
}
|
||||
|
||||
nzDefaults := queries.NonZeroDefaultSet(testResourceColumnsWithDefault, o)
|
||||
|
||||
key := makeCacheKey(columns, nzDefaults)
|
||||
testResourceInsertCacheMut.RLock()
|
||||
cache, cached := testResourceInsertCache[key]
|
||||
testResourceInsertCacheMut.RUnlock()
|
||||
|
||||
if !cached {
|
||||
wl, returnColumns := columns.InsertColumnSet(
|
||||
testResourceAllColumns,
|
||||
testResourceColumnsWithDefault,
|
||||
testResourceColumnsWithoutDefault,
|
||||
nzDefaults,
|
||||
)
|
||||
|
||||
cache.valueMapping, err = queries.BindMapping(testResourceType, testResourceMapping, wl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.retMapping, err = queries.BindMapping(testResourceType, testResourceMapping, returnColumns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(wl) != 0 {
|
||||
cache.query = fmt.Sprintf("INSERT INTO \"test_resource\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1))
|
||||
} else {
|
||||
cache.query = "INSERT INTO \"test_resource\" %sDEFAULT VALUES%s"
|
||||
}
|
||||
|
||||
var queryOutput, queryReturning string
|
||||
|
||||
if len(cache.retMapping) != 0 {
|
||||
queryReturning = fmt.Sprintf(" RETURNING \"%s\"", strings.Join(returnColumns, "\",\""))
|
||||
}
|
||||
|
||||
cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning)
|
||||
}
|
||||
|
||||
value := reflect.Indirect(reflect.ValueOf(o))
|
||||
vals := queries.ValuesFromMapping(value, cache.valueMapping)
|
||||
|
||||
if boil.IsDebug(ctx) {
|
||||
writer := boil.DebugWriterFrom(ctx)
|
||||
fmt.Fprintln(writer, cache.query)
|
||||
fmt.Fprintln(writer, vals)
|
||||
}
|
||||
|
||||
if len(cache.retMapping) != 0 {
|
||||
err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...)
|
||||
} else {
|
||||
_, err = exec.ExecContext(ctx, cache.query, vals...)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "models: unable to insert into test_resource")
|
||||
}
|
||||
|
||||
if !cached {
|
||||
testResourceInsertCacheMut.Lock()
|
||||
testResourceInsertCache[key] = cache
|
||||
testResourceInsertCacheMut.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update uses an executor to update the TestResource.
|
||||
// See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates.
|
||||
// Update does not automatically update the record in case of default values. Use .Reload() to refresh the records.
|
||||
func (o *TestResource) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) {
|
||||
if !boil.TimestampsAreSkipped(ctx) {
|
||||
currTime := time.Now().In(boil.GetLocation())
|
||||
|
||||
o.UpdatedAt = currTime
|
||||
}
|
||||
|
||||
var err error
|
||||
key := makeCacheKey(columns, nil)
|
||||
testResourceUpdateCacheMut.RLock()
|
||||
cache, cached := testResourceUpdateCache[key]
|
||||
testResourceUpdateCacheMut.RUnlock()
|
||||
|
||||
if !cached {
|
||||
wl := columns.UpdateColumnSet(
|
||||
testResourceAllColumns,
|
||||
testResourcePrimaryKeyColumns,
|
||||
)
|
||||
|
||||
if !columns.IsWhitelist() {
|
||||
wl = strmangle.SetComplement(wl, []string{"created_at"})
|
||||
}
|
||||
if len(wl) == 0 {
|
||||
return 0, errors.New("models: unable to update test_resource, could not build whitelist")
|
||||
}
|
||||
|
||||
cache.query = fmt.Sprintf("UPDATE \"test_resource\" SET %s WHERE %s",
|
||||
strmangle.SetParamNames("\"", "\"", 1, wl),
|
||||
strmangle.WhereClause("\"", "\"", len(wl)+1, testResourcePrimaryKeyColumns),
|
||||
)
|
||||
cache.valueMapping, err = queries.BindMapping(testResourceType, testResourceMapping, append(wl, testResourcePrimaryKeyColumns...))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping)
|
||||
|
||||
if boil.IsDebug(ctx) {
|
||||
writer := boil.DebugWriterFrom(ctx)
|
||||
fmt.Fprintln(writer, cache.query)
|
||||
fmt.Fprintln(writer, values)
|
||||
}
|
||||
var result sql.Result
|
||||
result, err = exec.ExecContext(ctx, cache.query, values...)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to update test_resource row")
|
||||
}
|
||||
|
||||
rowsAff, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: failed to get rows affected by update for test_resource")
|
||||
}
|
||||
|
||||
if !cached {
|
||||
testResourceUpdateCacheMut.Lock()
|
||||
testResourceUpdateCache[key] = cache
|
||||
testResourceUpdateCacheMut.Unlock()
|
||||
}
|
||||
|
||||
return rowsAff, nil
|
||||
}
|
||||
|
||||
// UpdateAll updates all rows with the specified column values.
|
||||
func (q testResourceQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
|
||||
queries.SetUpdate(q.Query, cols)
|
||||
|
||||
result, err := q.Query.ExecContext(ctx, exec)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to update all for test_resource")
|
||||
}
|
||||
|
||||
rowsAff, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to retrieve rows affected for test_resource")
|
||||
}
|
||||
|
||||
return rowsAff, nil
|
||||
}
|
||||
|
||||
// UpdateAll updates all rows with the specified column values, using an executor.
|
||||
func (o TestResourceSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
|
||||
ln := int64(len(o))
|
||||
if ln == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if len(cols) == 0 {
|
||||
return 0, errors.New("models: update all requires at least one column argument")
|
||||
}
|
||||
|
||||
colNames := make([]string, len(cols))
|
||||
args := make([]interface{}, len(cols))
|
||||
|
||||
i := 0
|
||||
for name, value := range cols {
|
||||
colNames[i] = name
|
||||
args[i] = value
|
||||
i++
|
||||
}
|
||||
|
||||
// Append all of the primary key values for each column
|
||||
for _, obj := range o {
|
||||
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), testResourcePrimaryKeyMapping)
|
||||
args = append(args, pkeyArgs...)
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf("UPDATE \"test_resource\" SET %s WHERE %s",
|
||||
strmangle.SetParamNames("\"", "\"", 1, colNames),
|
||||
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), len(colNames)+1, testResourcePrimaryKeyColumns, len(o)))
|
||||
|
||||
if boil.IsDebug(ctx) {
|
||||
writer := boil.DebugWriterFrom(ctx)
|
||||
fmt.Fprintln(writer, sql)
|
||||
fmt.Fprintln(writer, args...)
|
||||
}
|
||||
result, err := exec.ExecContext(ctx, sql, args...)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to update all in testResource slice")
|
||||
}
|
||||
|
||||
rowsAff, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to retrieve rows affected all in update all testResource")
|
||||
}
|
||||
return rowsAff, nil
|
||||
}
|
||||
|
||||
// Upsert attempts an insert using an executor, and does an update or ignore on conflict.
|
||||
// See boil.Columns documentation for how to properly use updateColumns and insertColumns.
|
||||
func (o *TestResource) Upsert(ctx context.Context, exec boil.ContextExecutor, updateOnConflict bool, conflictColumns []string, updateColumns, insertColumns boil.Columns) error {
|
||||
if o == nil {
|
||||
return errors.New("models: no test_resource provided for upsert")
|
||||
}
|
||||
if !boil.TimestampsAreSkipped(ctx) {
|
||||
currTime := time.Now().In(boil.GetLocation())
|
||||
|
||||
if o.CreatedAt.IsZero() {
|
||||
o.CreatedAt = currTime
|
||||
}
|
||||
o.UpdatedAt = currTime
|
||||
}
|
||||
|
||||
nzDefaults := queries.NonZeroDefaultSet(testResourceColumnsWithDefault, o)
|
||||
|
||||
// Build cache key in-line uglily - mysql vs psql problems
|
||||
buf := strmangle.GetBuffer()
|
||||
if updateOnConflict {
|
||||
buf.WriteByte('t')
|
||||
} else {
|
||||
buf.WriteByte('f')
|
||||
}
|
||||
buf.WriteByte('.')
|
||||
for _, c := range conflictColumns {
|
||||
buf.WriteString(c)
|
||||
}
|
||||
buf.WriteByte('.')
|
||||
buf.WriteString(strconv.Itoa(updateColumns.Kind))
|
||||
for _, c := range updateColumns.Cols {
|
||||
buf.WriteString(c)
|
||||
}
|
||||
buf.WriteByte('.')
|
||||
buf.WriteString(strconv.Itoa(insertColumns.Kind))
|
||||
for _, c := range insertColumns.Cols {
|
||||
buf.WriteString(c)
|
||||
}
|
||||
buf.WriteByte('.')
|
||||
for _, c := range nzDefaults {
|
||||
buf.WriteString(c)
|
||||
}
|
||||
key := buf.String()
|
||||
strmangle.PutBuffer(buf)
|
||||
|
||||
testResourceUpsertCacheMut.RLock()
|
||||
cache, cached := testResourceUpsertCache[key]
|
||||
testResourceUpsertCacheMut.RUnlock()
|
||||
|
||||
var err error
|
||||
|
||||
if !cached {
|
||||
insert, ret := insertColumns.InsertColumnSet(
|
||||
testResourceAllColumns,
|
||||
testResourceColumnsWithDefault,
|
||||
testResourceColumnsWithoutDefault,
|
||||
nzDefaults,
|
||||
)
|
||||
update := updateColumns.UpdateColumnSet(
|
||||
testResourceAllColumns,
|
||||
testResourcePrimaryKeyColumns,
|
||||
)
|
||||
|
||||
if updateOnConflict && len(update) == 0 {
|
||||
return errors.New("models: unable to upsert test_resource, could not build update column list")
|
||||
}
|
||||
|
||||
conflict := conflictColumns
|
||||
if len(conflict) == 0 {
|
||||
conflict = make([]string, len(testResourcePrimaryKeyColumns))
|
||||
copy(conflict, testResourcePrimaryKeyColumns)
|
||||
}
|
||||
cache.query = buildUpsertQueryPostgres(dialect, "\"test_resource\"", updateOnConflict, ret, update, conflict, insert)
|
||||
|
||||
cache.valueMapping, err = queries.BindMapping(testResourceType, testResourceMapping, insert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ret) != 0 {
|
||||
cache.retMapping, err = queries.BindMapping(testResourceType, testResourceMapping, ret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value := reflect.Indirect(reflect.ValueOf(o))
|
||||
vals := queries.ValuesFromMapping(value, cache.valueMapping)
|
||||
var returns []interface{}
|
||||
if len(cache.retMapping) != 0 {
|
||||
returns = queries.PtrsFromMapping(value, cache.retMapping)
|
||||
}
|
||||
|
||||
if boil.IsDebug(ctx) {
|
||||
writer := boil.DebugWriterFrom(ctx)
|
||||
fmt.Fprintln(writer, cache.query)
|
||||
fmt.Fprintln(writer, vals)
|
||||
}
|
||||
if len(cache.retMapping) != 0 {
|
||||
err = exec.QueryRowContext(ctx, cache.query, vals...).Scan(returns...)
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil // Postgres doesn't return anything when there's no update
|
||||
}
|
||||
} else {
|
||||
_, err = exec.ExecContext(ctx, cache.query, vals...)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "models: unable to upsert test_resource")
|
||||
}
|
||||
|
||||
if !cached {
|
||||
testResourceUpsertCacheMut.Lock()
|
||||
testResourceUpsertCache[key] = cache
|
||||
testResourceUpsertCacheMut.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes a single TestResource record with an executor.
|
||||
// Delete will match against the primary key column to find the record to delete.
|
||||
func (o *TestResource) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
|
||||
if o == nil {
|
||||
return 0, errors.New("models: no TestResource provided for delete")
|
||||
}
|
||||
|
||||
args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), testResourcePrimaryKeyMapping)
|
||||
sql := "DELETE FROM \"test_resource\" WHERE \"id\"=$1"
|
||||
|
||||
if boil.IsDebug(ctx) {
|
||||
writer := boil.DebugWriterFrom(ctx)
|
||||
fmt.Fprintln(writer, sql)
|
||||
fmt.Fprintln(writer, args...)
|
||||
}
|
||||
result, err := exec.ExecContext(ctx, sql, args...)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to delete from test_resource")
|
||||
}
|
||||
|
||||
rowsAff, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: failed to get rows affected by delete for test_resource")
|
||||
}
|
||||
|
||||
return rowsAff, nil
|
||||
}
|
||||
|
||||
// DeleteAll deletes all matching rows.
|
||||
func (q testResourceQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
|
||||
if q.Query == nil {
|
||||
return 0, errors.New("models: no testResourceQuery provided for delete all")
|
||||
}
|
||||
|
||||
queries.SetDelete(q.Query)
|
||||
|
||||
result, err := q.Query.ExecContext(ctx, exec)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to delete all from test_resource")
|
||||
}
|
||||
|
||||
rowsAff, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for test_resource")
|
||||
}
|
||||
|
||||
return rowsAff, nil
|
||||
}
|
||||
|
||||
// DeleteAll deletes all rows in the slice, using an executor.
|
||||
func (o TestResourceSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
|
||||
if len(o) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var args []interface{}
|
||||
for _, obj := range o {
|
||||
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), testResourcePrimaryKeyMapping)
|
||||
args = append(args, pkeyArgs...)
|
||||
}
|
||||
|
||||
sql := "DELETE FROM \"test_resource\" WHERE " +
|
||||
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, testResourcePrimaryKeyColumns, len(o))
|
||||
|
||||
if boil.IsDebug(ctx) {
|
||||
writer := boil.DebugWriterFrom(ctx)
|
||||
fmt.Fprintln(writer, sql)
|
||||
fmt.Fprintln(writer, args)
|
||||
}
|
||||
result, err := exec.ExecContext(ctx, sql, args...)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: unable to delete all from testResource slice")
|
||||
}
|
||||
|
||||
rowsAff, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for test_resource")
|
||||
}
|
||||
|
||||
return rowsAff, nil
|
||||
}
|
||||
|
||||
// Reload refetches the object from the database
|
||||
// using the primary keys with an executor.
|
||||
func (o *TestResource) Reload(ctx context.Context, exec boil.ContextExecutor) error {
|
||||
ret, err := FindTestResource(ctx, exec, o.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*o = *ret
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReloadAll refetches every row with matching primary key column values
|
||||
// and overwrites the original object slice with the newly updated slice.
|
||||
func (o *TestResourceSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error {
|
||||
if o == nil || len(*o) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
slice := TestResourceSlice{}
|
||||
var args []interface{}
|
||||
for _, obj := range *o {
|
||||
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), testResourcePrimaryKeyMapping)
|
||||
args = append(args, pkeyArgs...)
|
||||
}
|
||||
|
||||
sql := "SELECT \"test_resource\".* FROM \"test_resource\" WHERE " +
|
||||
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, testResourcePrimaryKeyColumns, len(*o))
|
||||
|
||||
q := queries.Raw(sql, args...)
|
||||
|
||||
err := q.Bind(ctx, exec, &slice)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "models: unable to reload all in TestResourceSlice")
|
||||
}
|
||||
|
||||
*o = slice
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestResourceExists checks if the TestResource row exists.
|
||||
func TestResourceExists(ctx context.Context, exec boil.ContextExecutor, iD string) (bool, error) {
|
||||
var exists bool
|
||||
sql := "select exists(select 1 from \"test_resource\" where \"id\"=$1 limit 1)"
|
||||
|
||||
if boil.IsDebug(ctx) {
|
||||
writer := boil.DebugWriterFrom(ctx)
|
||||
fmt.Fprintln(writer, sql)
|
||||
fmt.Fprintln(writer, iD)
|
||||
}
|
||||
row := exec.QueryRowContext(ctx, sql, iD)
|
||||
|
||||
err := row.Scan(&exists)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "models: unable to check if test_resource exists")
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
||||
28
scripts/internal/util/get_module_name.go
Normal file
28
scripts/internal/util/get_module_name.go
Normal file
@@ -0,0 +1,28 @@
|
||||
//go:build scripts
|
||||
|
||||
// nolint:revive
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
// GetModuleName returns the current go module's name as defined in the go.mod file.
|
||||
// https://stackoverflow.com/questions/53183356/api-to-get-the-module-name
|
||||
// https://github.com/rogpeppe/go-internal
|
||||
func GetModuleName(absolutePathToGoMod string) (string, error) {
|
||||
dat, err := os.ReadFile(absolutePathToGoMod)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return "", fmt.Errorf("failed to read go.mod file: %w", err)
|
||||
}
|
||||
|
||||
mod := modfile.ModulePath(dat)
|
||||
|
||||
return mod, nil
|
||||
}
|
||||
14
scripts/internal/util/get_project_root_dir.go
Normal file
14
scripts/internal/util/get_project_root_dir.go
Normal file
@@ -0,0 +1,14 @@
|
||||
//go:build scripts
|
||||
|
||||
// nolint:revive
|
||||
package util
|
||||
|
||||
import "os"
|
||||
|
||||
func GetProjectRootDir() string {
|
||||
if val, ok := os.LookupEnv("PROJECT_ROOT_DIR"); ok {
|
||||
return val
|
||||
}
|
||||
|
||||
return "/app"
|
||||
}
|
||||
Reference in New Issue
Block a user