(Feat): Initial Commit
Some checks failed
Build & Test / build-test (push) Has been cancelled
Build & Test / swagger-codegen-cli (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled

This commit is contained in:
2026-07-03 19:41:31 +05:30
commit 7e940c83a7
461 changed files with 45002 additions and 0 deletions

View 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
}

View 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
}

View 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
}

View 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)
}

View 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, &params)
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, &params)
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, &params)
if err != nil {
return err
}
id := params.ID.String()
// TODO: Implement
*/
return c.NoContent(http.StatusNoContent)
}
}
`
)

View 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
}

View 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
}

View 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"
}