feat(11.1-05): add the acme.blog walkthrough plugin with its model, migration and posts route
- docs/examples/blog: scaffolder output for acme.blog (make:plugin, make:model) in the root module, with a Post model, a fill allow-list and NewPost, the create migration and GET /api/blog/posts paginated through lagoon - short tests activate the plugin, check the route with surf, the fill allow-list and the migration order - docs/setup/porting-a-plugin.md: registration, model, migrations and routes sections with src= copies of the plugin - TestDocsRequiredPages requires setup/porting-a-plugin
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
// Code generated by summer make. DO NOT EDIT.
|
||||
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CreatePosts returns the 20260101000000_create_acme_blog_posts gormigrate entry.
|
||||
func CreatePosts() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20260101000000_create_acme_blog_posts",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`CREATE TABLE acme_blog_posts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
slug VARCHAR(255) NOT NULL UNIQUE,
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)`).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS acme_blog_posts").Error
|
||||
},
|
||||
}
|
||||
}
|
||||
2
docs/examples/blog/updates/doc.go
Normal file
2
docs/examples/blog/updates/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package updates holds the gormigrate set for the acme.blog plugin.
|
||||
package updates
|
||||
57
docs/examples/blog/updates/updates_test.go
Normal file
57
docs/examples/blog/updates/updates_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package updates_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/summercms/docs/examples/blog"
|
||||
"git.golem15.com/golem15/summercms/docs/examples/blog/updates"
|
||||
)
|
||||
|
||||
var migrationFile = regexp.MustCompile(`^(\d{14})_[a-z0-9_]+\.go$`)
|
||||
|
||||
func TestMigrationIDs(t *testing.T) {
|
||||
if got := updates.CreatePosts().ID; got != "20260101000000_create_acme_blog_posts" {
|
||||
t.Errorf("CreatePosts ID = %q", got)
|
||||
}
|
||||
|
||||
// The plugin returns the set in registry.gen.go's order, which must be
|
||||
// ascending by ID: gormigrate applies it in slice order.
|
||||
var ids []string
|
||||
for _, m := range (&blog.Plugin{}).Migrations() {
|
||||
if m.Migrate == nil || m.Rollback == nil {
|
||||
t.Errorf("migration %s has no Migrate or Rollback", m.ID)
|
||||
}
|
||||
ids = append(ids, m.ID)
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
t.Fatal("the plugin has no migrations")
|
||||
}
|
||||
if !slices.IsSorted(ids) {
|
||||
t.Errorf("migration IDs are not in ascending order: %v", ids)
|
||||
}
|
||||
|
||||
// Every migration file is named after its ID, and every ID has a file.
|
||||
entries, err := os.ReadDir(".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var files []string
|
||||
for _, e := range entries {
|
||||
name := e.Name()
|
||||
if e.IsDir() || name == "doc.go" || strings.HasSuffix(name, "_test.go") || !strings.HasSuffix(name, ".go") {
|
||||
continue
|
||||
}
|
||||
if !migrationFile.MatchString(name) {
|
||||
t.Errorf("%s does not start with a 14-digit timestamp", name)
|
||||
continue
|
||||
}
|
||||
files = append(files, strings.TrimSuffix(name, ".go"))
|
||||
}
|
||||
if !slices.Equal(files, ids) {
|
||||
t.Errorf("migration files %v, want one per ID %v", files, ids)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user