ggcheck

package
v0.0.0-...-8c930d8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: Apache-2.0 Imports: 35 Imported by: 0

Documentation

Overview

Package ggcheck holds the rules gg check holds a business project to. Each Check finds the violations of one rule in the project in the working directory; the gg command running the checks decides which run, in what order, and how their results print.

A rule earns its place by stopping a mistaken use of the framework or by holding the project to a best practice of using it. One that stops no mistake, because the framework settles the matter itself, and only makes an ordinary way of writing the code awkward is not a rule of gg check.

Every check lives in a file of its own, named after the check: the file dsl_design_rules.go declares the DSL design rules Check, its name and rule, the function finding its violations and the helpers no other check uses. Helpers several checks share live in helper.go, which holds no check. Each test file pairs with the file it tests, but for fixtures_test.go, which holds the fixtures the tests share. TestEveryCheckIsDeclaredInAFileNamedAfterIt holds the layout: gathering several checks into one file, or naming a check's file otherwise, fails it.

Index

Constants

View Source
const VersionRequiredTag = "not null;default:1"

VersionRequiredTag is the exact gorm tag payload a model.Version field must carry.

Variables

View Source
var ActionTypeForm = Check{
	Name: "Action type form",
	Rule: "explicit DSL Payload/Result types must be named types declared in the same model package: struct types use the pointer form, slice and map types use the value form, an empty struct type may only pair with an empty peer side, a Payload type is never an interface with methods, and a type defined over another named type (type A B) gives way to an alias (type A = B) or a struct of its own",
	// contains filtered or unexported fields
}

ActionTypeForm holds explicit DSL Payload and Result type arguments to the forms the generated code relies on.

View Source
var ActionTypeNaming = Check{
	Name: "Action type naming",
	Rule: "explicit DSL Payload types must end with Req and Result types with Rsp",
	// contains filtered or unexported fields
}

ActionTypeNaming holds explicit DSL Payload type names to the Req suffix and Result type names to Rsp.

View Source
var ActionTypeUniqueness = Check{
	Name: "Action type uniqueness",
	Rule: "an explicit DSL Payload, Result, StreamingPayload or StreamingResult type name must be declared by one action alone: each action names request and response types of its own, an alias of a shared type when the fields are the same",
	// contains filtered or unexported fields
}

ActionTypeUniqueness holds every explicit DSL action type name to the one action declaring it.

View Source
var ArchitectureDependencies = Check{
	Name: "Architecture dependencies",
	Rule: "service code must not call other service code, dao code must not call service, router, controller or middleware code, and model code must not call service or dao code",
	// contains filtered or unexported fields
}

ArchitectureDependencies keeps each project layer from importing the layers it must not call.

View Source
var ColumnReferenceMinting = Check{
	Name: "Column reference minting",
	Rule: "project code, tests included, must not mint column references through gst.NewColumn, NewNumericColumn, NewTimeColumn or NewJSONStringsColumn; columns are read through the XxxCols variables gg gen writes, which the model schema checks, generated files excepted. Generic code, which has no Cols variable to read, may mint a reference whose model is its own type parameter",
	// contains filtered or unexported fields
}

ColumnReferenceMinting keeps project code from minting column references by hand.

View Source
var DSLDesignRules = Check{
	Name: "DSL design rules",
	Rule: "model files must pass the same validation rules that gate gg gen: the Design() DSL rules, the base types model.Base, model.AutoBase and model.Empty embedded by value, never through a pointer, no two actions registering one path, and no two actions writing one service file, one service type into a directory or two packages into one",
	// contains filtered or unexported fields
}

DSLDesignRules runs the Design() validation that gates gg gen over every model file, and the route and service conflict checks gg gen runs over them all.

View Source
var DatabaseChainTermination = Check{
	Name: "Database chain termination",
	Rule: "a database.Database chain runs one terminal operation: written inline it ends in one, held in a variable it is terminated once, not again and not inside a loop it was bound outside of",
	// contains filtered or unexported fields
}

DatabaseChainTermination keeps every database.Database chain to one terminal operation.

View Source
var DetachedContext = Check{
	Name: "Detached context",
	Rule: "in model, service, dao, cronjob, leader, lock, component and router code, the context passed to a framework database function or to a dao function must not be context.Background() or context.TODO(): the context handed down — the write's for a model hook, a request's, a round's, a tenure's, a lock's, the process's, the start's — carries the identity, the transaction and the lease the work runs under; startup seeding runs in the router package's routes-ready hooks on the context the hook receives",
	// contains filtered or unexported fields
}

DetachedContext keeps context.Background() and context.TODO() out of the framework database and dao calls of project code.

View Source
var DirectoryRestrictions = Check{
	Name: "Directory restrictions",
	Rule: "top-level directories must be ones the framework layout names, such as model, service, router and dao, or hold no Go packages, such as logs and deploy",
	// contains filtered or unexported fields
}

DirectoryRestrictions limits the top-level directories to the ones the framework layout names and the ones holding no Go package.

View Source
var ErrorDiscipline = Check{
	Name: "Error discipline",
	Rule: "errors leaving service methods and model hooks must be built by gst.NewError or gst.NewErrorWithCause",
	// contains filtered or unexported fields
}

ErrorDiscipline requires the errors leaving service methods and model hooks to be built by gst.NewError or gst.NewErrorWithCause.

View Source
var FrameworkColumns = Check{
	Name: "Framework columns",
	Rule: "models must keep the framework's fields under the columns the framework fixes, id, created_by, updated_by, created_at, updated_at and deleted_at for the fields of model.Base and model.AutoBase and tenant_id for tenant.Scope: a model re-declaring one of those fields keeps its column, and embeds tenant.Scope without a prefix",
	// contains filtered or unexported fields
}

FrameworkColumns holds the models to the column names the framework fixes for its fields.

View Source
var GRPCServiceContext = Check{
	Name: "gRPC service context",
	Rule: "the services of models declaring GRPC() must not call the ServiceContext methods only HTTP serves: " + strings.Join(types.HTTPOnlyMethods, ", ") + "; the services of their Import, Export and SSE actions, served over HTTP alone, may",
	// contains filtered or unexported fields
}

GRPCServiceContext keeps the services of the models served over gRPC to what both transports provide.

View Source
var GormTagIndexBan = Check{
	Name: "Gorm tag index ban",
	Rule: "gorm struct tags must not configure indexes (index, uniqueIndex, unique); models declare indexes through the Indexes() []model.Index method",
	// contains filtered or unexported fields
}

GormTagIndexBan keeps index settings out of gorm struct tags.

View Source
var IDAccessors = Check{
	Name: "ID accessors",
	Rule: "a model declaring an ID of its own over the one of model.Base or model.AutoBase, a wider key say, must declare its own GetID, SetID and ClearID: the base's read and write the shadowed field, which no column maps",
	// contains filtered or unexported fields
}

IDAccessors holds the models declaring an ID of their own to the ID accessors that go with it.

View Source
var IndexDeclarations = Check{
	Name: "Index declarations",
	Rule: "on a project configured for MySQL, the Indexes() declarations of every model must be ones MySQL accepts: the fields exist, no column repeats, no declaration duplicates a struct tag index, and every column can be indexed, which a string field without a size cannot, being a longtext column; give it a size (gorm:\"size:191\") or a bounded type",
	// contains filtered or unexported fields
}

IndexDeclarations holds the models' Indexes() declarations to what the configured database accepts.

View Source
var JSONTagNaming = Check{
	Name: "JSON tag naming",
	Rule: "model struct and explicit DSL Payload/Result type fields must carry snake_case json tags; an exported field without a json key would be served under its Go name",
	// contains filtered or unexported fields
}

JSONTagNaming holds every exported field of models and of explicit DSL Payload and Result types to a snake_case json tag.

View Source
var LogFieldBoundedness = Check{
	Name: "Log field boundedness",
	Rule: "project code must not declare MarshalLogObject or MarshalLogArray methods and must not call zap.Namespace: zapcore marshalers and nested namespaces bypass the reflected-value collapsing that keeps log-store field mappings bounded",
	// contains filtered or unexported fields
}

LogFieldBoundedness keeps zapcore marshalers and zap.Namespace out of project code.

View Source
var ModelFileBoundaries = Check{
	Name: "Model file boundaries",
	Rule: "model files must contain at most one model struct",
	// contains filtered or unexported fields
}

ModelFileBoundaries allows at most one model struct per model file.

View Source
var ModelFileNameHyphens = Check{
	Name: "Model file name hyphens",
	Rule: "model file names must not contain hyphens (use underscores instead)",
	// contains filtered or unexported fields
}

ModelFileNameHyphens keeps hyphens out of model file names.

View Source
var ModelPackageNaming = Check{
	Name: "Model package naming",
	Rule: "model package names must match their directory names",
	// contains filtered or unexported fields
}

ModelPackageNaming holds model package names to their directory names.

View Source
var ModelSingularNaming = Check{
	Name: "Model singular naming",
	Rule: "model directories and files must be singular",
	// contains filtered or unexported fields
}

ModelSingularNaming keeps model directory and file names singular.

View Source
var ModelTableNameDeclaration = Check{
	Name: "Model table name declaration",
	Rule: "model structs embedding model.Base or model.AutoBase must declare TableName() string on the struct itself, as a single return of a non-empty string literal",
	// contains filtered or unexported fields
}

ModelTableNameDeclaration requires every database model to declare its table name as a string literal, or as a constant holding one.

View Source
var ModuleAssembly = Check{
	Name: "Module assembly",
	Rule: "the assembly calls a copied framework module declares in its module.json must be made in the project's non-test code, outside the model and service subtrees the module was copied into: the function called, or handed as a value to the installer that runs it; a function of those subtrees comes from the project's copy of the package",
	// contains filtered or unexported fields
}

ModuleAssembly requires the assembly calls a copied framework module declares to be made in project code.

View Source
var ProtobufDefinitions = Check{
	Name: "Protobuf definitions",
	Rule: "models declaring GRPC(), and the types their actions reach, must be describable in protobuf with a valid pb tag on every field, and their definitions must keep the contract committed under pb/: the checks gg gen runs before writing",
	// contains filtered or unexported fields
}

ProtobufDefinitions holds the models declaring GRPC() to what gg gen requires of their protobuf definitions.

View Source
var QueryNames = Check{
	Name: "Query names",
	Rule: "a model's query tags must name each of its URL query parameters once: no two fields declare one name, a business field's name stays outside the \"_\" namespace of the framework's own parameters, and no tag holds \"[\", \"]\" or \".\", which the operator and the path syntax read",
	// contains filtered or unexported fields
}

QueryNames holds the models to query tags naming each URL query parameter once.

View Source
var ServiceFileBoundaries = Check{
	Name: "Service file boundaries",
	Rule: "service files must contain at most one service struct",
	// contains filtered or unexported fields
}

ServiceFileBoundaries allows at most one service struct per service file.

View Source
var ServiceTestCoverage = Check{
	Name: "Service test coverage",
	Rule: "service files generated for DSL Service() actions must have a matching test file (create.go pairs with create_test.go or create_internal_test.go)",
	// contains filtered or unexported fields
}

ServiceTestCoverage requires a test file for every service file generated for a DSL Service() action.

View Source
var ServiceTestOrganization = Check{
	Name: "Service test organization",
	Rule: "test files under the service directory must pair with a source file of their package; main_test.go only declares TestMain, fixtures_test.go only holds shared test fixtures, and test cases without a source file of their own belong in the test file of a related source file",
	// contains filtered or unexported fields
}

ServiceTestOrganization pairs every test file under the service directory with a source file of its package.

View Source
var TenantUniqueIndexes = Check{
	Name: "Tenant unique indexes",
	Rule: "every unique index a model embedding tenant.Scope declares must name TenantID among its fields: the rows of such a model belong to one tenant each, so its uniqueness is declared within the tenant, {Fields: []string{\"TenantID\", \"Code\"}, Unique: true} rather than {Fields: []string{\"Code\"}, Unique: true}",
	// contains filtered or unexported fields
}

TenantUniqueIndexes holds the unique indexes of the models embedding tenant.Scope to the tenant.

View Source
var TestMainImports = Check{
	Name: "TestMain imports",
	Rule: "test files declaring TestMain in an external test package must blank-import the interceptor and pb packages exactly when main.go imports them, which is while they hold Go sources",
	// contains filtered or unexported fields
}

TestMainImports keeps the test files declaring TestMain importing the optional project packages the way main.go does.

View Source
var TransactionClosureContext = Check{
	Name: "Transaction closure context",
	Rule: "database.Database chains and nested database.Transaction calls inside a database.Transaction closure must use the closure's context parameter",
	// contains filtered or unexported fields
}

TransactionClosureContext holds the database calls inside a database.Transaction closure to the context the closure receives.

View Source
var VersionFieldDeclaration = Check{
	Name: "Version field declaration",
	Rule: `model.Version declarations must keep the optimistic-locking shape: on database models a named field with json:",omitempty" and gorm:"not null;default:1", and on DSL Payload/Result types (plus the same-package types reachable from their fields) a json tag of exactly "version,omitempty"`,
	// contains filtered or unexported fields
}

VersionFieldDeclaration holds model.Version declarations to the optimistic- locking shape.

Functions

This section is empty.

Types

type Check

type Check struct {
	// Name is the name the check's result prints under.
	Name string
	// Rule states the rule, as the help of gg check lists it.
	Rule string
	// contains filtered or unexported fields
}

Check is one rule gg check holds a project to.

type Result

type Result struct {
	// Name is the name of the check.
	Name string
	// Violations describes each violation found, one line each; it is empty
	// when the project follows the rule.
	Violations []string
}

Result is what one check found in the project.

func Run

func Run(checks []Check) []Result

Run runs checks over the project in the working directory, in the order given, and returns their results in the same order. Paths the project ignores (see gghelper.ProjectIgnore) are left out of every check.

type TagInsertion

type TagInsertion struct {
	Offset int
	Text   string
}

TagInsertion is one insertion a finding's heal expands to: Text goes in at byte Offset of the file.

type VersionFieldFinding

type VersionFieldFinding struct {
	Path     string
	Line     int
	Struct   string
	Field    string // empty for an embedded declaration
	Embedded bool
	Missing  []string // required gorm settings absent from the tag, bare

	// JSONMissing marks a json tag that does not satisfy the omitempty
	// contract; JSONBlocked marks json:"-", which no tool may heal:
	// un-hiding a field the author silenced is a semantic decision, exactly
	// like reshaping an embedded declaration.
	JSONMissing bool
	JSONBlocked bool
	// contains filtered or unexported fields
}

VersionFieldFinding describes one model.Version declaration that deviates from the required shape, with enough byte geometry for gg gen to rewrite the named-field cases in place.

func VersionFieldFindings

func VersionFieldFindings() ([]VersionFieldFinding, error)

VersionFieldFindings reports every model.Version declaration of a database model under the model directory that deviates from the required shape, the declarations gg gen heals by filling their tags in. Paths the project ignores (see gghelper.ProjectIgnore) are left out.

func (VersionFieldFinding) TagInsertions

func (finding VersionFieldFinding) TagInsertions() []TagInsertion

TagInsertions expands one healable finding into its byte insertions. A field without any json section is given its wire name (see versionJSONName). Applied to the file, the insertions heal

Version model.Version
Revision model.Version `json:"revision" gorm:"not null"`

into

Version model.Version `json:"version,omitempty" gorm:"not null;default:1"`
Revision model.Version `json:"revision,omitempty" gorm:"not null;default:1"`

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址