Skip to content

Commit

Permalink
feat: first draft end to end Create API (#23)
Browse files Browse the repository at this point in the history
* feat: Add a NoopLogger to help in tests

* feat: Implement unmarshal in ItemsCreate

* feat: Connect handler with repository

* chore: Add docker compose file for local testing

* feat: Add migrate command

* refactor: Add metadata to Item, remove path and add back DisplayName

* chore: Delete the docker compose, for now focus on sqlite
  • Loading branch information
MaikelVeen committed Aug 18, 2024
1 parent 1e20100 commit e529102
Show file tree
Hide file tree
Showing 23 changed files with 406 additions and 192 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,7 @@ docs/how_to_guides/*
# Notes
TODO.md

profile.cov
profile.cov

# Sqlite
*.sqlite
5 changes: 3 additions & 2 deletions api/v1/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 10 additions & 8 deletions api/v1/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ func (i *Item) MapToDomain() *item.Item {
if i == nil {
return nil
}

return &item.Item{
UID: i.Id,
Name: i.Name,
Path: i.Path,
Content: i.Content,
Hash: parser.HashContent([]byte(i.Content)),
CreateTime: i.CreateTime,
UpdateTime: i.UpdateTime,
Properties: i.Properties,
UID: i.Uid,
Name: i.Name,
DisplayName: i.DisplayName,
Content: i.Content,
Hash: parser.HashContent([]byte(i.Content)),
CreateTime: i.CreateTime,
UpdateTime: i.UpdateTime,
Properties: i.Properties,
Metadata: i.Metadata,
}
}
55 changes: 25 additions & 30 deletions api/v1/item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ func TestItem_MapToDomain(t *testing.T) {
DisplayName string
ID string
Name string
Path string
Properties map[string]interface{}
UpdateTime time.Time
}
Expand All @@ -37,19 +36,18 @@ func TestItem_MapToDomain(t *testing.T) {
DisplayName: "Test Display Name",
ID: "1234",
Name: "Test Name",
Path: "/test/path",
Properties: map[string]interface{}{"key1": "value1", "key2": "value2"},
UpdateTime: time.Now(),
},
want: &item.Item{
UID: "1234",
Name: "Test Name",
Path: "/test/path",
Content: "Test Content",
Hash: parser.HashContent([]byte("Test Content")),
CreateTime: time.Now().Add(-24 * time.Hour),
UpdateTime: time.Now(),
Properties: map[string]any{"key1": "value1", "key2": "value2"},
UID: "1234",
Name: "Test Name",
DisplayName: "Test Display Name",
Content: "Test Content",
Hash: parser.HashContent([]byte("Test Content")),
CreateTime: time.Now().Add(-24 * time.Hour),
UpdateTime: time.Now(),
Properties: map[string]any{"key1": "value1", "key2": "value2"},
},
},
{
Expand All @@ -60,19 +58,18 @@ func TestItem_MapToDomain(t *testing.T) {
DisplayName: "",
ID: "",
Name: "",
Path: "",
Properties: nil,
UpdateTime: time.Time{},
},
want: &item.Item{
UID: "",
Name: "",
Path: "",
Content: "",
Hash: parser.HashContent([]byte("")),
CreateTime: time.Time{},
UpdateTime: time.Time{},
Properties: nil,
UID: "",
Name: "",
DisplayName: "",
Content: "",
Hash: parser.HashContent([]byte("")),
CreateTime: time.Time{},
UpdateTime: time.Time{},
Properties: nil,
},
},
{
Expand All @@ -83,19 +80,18 @@ func TestItem_MapToDomain(t *testing.T) {
DisplayName: "Test Display Name",
ID: "5678",
Name: "Test Name 2",
Path: "/test/path2",
Properties: nil,
UpdateTime: time.Now(),
},
want: &item.Item{
UID: "5678",
Name: "Test Name 2",
Path: "/test/path2",
Content: "Test Content",
Hash: parser.HashContent([]byte("Test Content")),
CreateTime: time.Now().Add(-24 * time.Hour),
UpdateTime: time.Now(),
Properties: nil,
UID: "5678",
Name: "Test Name 2",
Content: "Test Content",
DisplayName: "Test Display Name",
Hash: parser.HashContent([]byte("Test Content")),
CreateTime: time.Now().Add(-24 * time.Hour),
UpdateTime: time.Now(),
Properties: nil,
},
},
}
Expand All @@ -108,9 +104,8 @@ func TestItem_MapToDomain(t *testing.T) {
Content: tt.fields.Content,
CreateTime: tt.fields.CreateTime,
DisplayName: tt.fields.DisplayName,
Id: tt.fields.ID,
Uid: tt.fields.ID,
Name: tt.fields.Name,
Path: tt.fields.Path,
Properties: tt.fields.Properties,
UpdateTime: tt.fields.UpdateTime,
}
Expand Down
68 changes: 68 additions & 0 deletions cmd/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmd

import (
"log/slog"
"os"

"github.com/glass-cms/glasscms/database"
"github.com/lmittmann/tint"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

type MigrateCommand struct {
Command *cobra.Command
logger *slog.Logger

databaseConfig database.Config
}

func NewMigrateCommand() *MigrateCommand {
mc := &MigrateCommand{
logger: slog.New(
tint.NewHandler(os.Stdout, &tint.Options{
Level: slog.LevelDebug,
}),
),
}

mc.Command = &cobra.Command{
Use: "migrate",
Short: "Migrate the database schema",
Hidden: true,
RunE: mc.Execute,
Args: cobra.NoArgs,
}

flagset := mc.Command.Flags()

flagset.StringVar(
&mc.databaseConfig.Driver,
database.ArgDriver,
"",
"The name of the database driver",
)
_ = viper.BindPFlag(database.ArgDriver, flagset.Lookup(database.ArgDriver))

flagset.StringVar(
&mc.databaseConfig.DSN,
database.ArgDSN,
"",
"The data source name (DSN) for the database",
)
_ = viper.BindPFlag(database.ArgDSN, flagset.Lookup(database.ArgDSN))

return mc
}

func (mc *MigrateCommand) Execute(_ *cobra.Command, _ []string) error {
mc.logger.Info("Migrating the database schema")

db, err := database.NewConnection(mc.databaseConfig)
if err != nil {
mc.logger.Error("Failed to create a new database connection")
return err
}

return database.MigrateDatabase(db, mc.databaseConfig)
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func init() {
rootCmd.AddCommand(NewConvertCommand().Command)
rootCmd.AddCommand(NewDocsCommand().Command)
rootCmd.AddCommand(server.NewCommand().Command)
rootCmd.AddCommand(NewMigrateCommand().Command)

// Register flags.
pflags := rootCmd.PersistentFlags()
Expand Down
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
output: './out'
database:
dsn: 'file:test.db?cache=shared&mode=memory'
dsn: 'file:test.sqlite?cache=shared'
driver: 'sqlite3'

2 changes: 2 additions & 0 deletions database/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"errors"
"fmt"

// Import the PostgreSQL driver.
_ "github.com/lib/pq"
// Import the SQLite3 driver.
_ "github.com/mattn/go-sqlite3"
)
Expand Down
11 changes: 8 additions & 3 deletions database/migrations/1_create_items.sql
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
-- +goose Up
CREATE TABLE items (
uid TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
create_time TIMESTAMP NOT NULL,
update_time TIMESTAMP NOT NULL,
delete_time TIMESTAMP,
hash TEXT,
name TEXT NOT NULL,
path TEXT NOT NULL,
content TEXT,
properties JSON
properties JSON,
metadata JSON
);

CREATE INDEX items_name ON items(name);
CREATE INDEX items_delete_time ON items(delete_time);

-- +goose Down
DROP TABLE items;
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.22.0

require (
github.com/djherbis/times v1.6.0
github.com/lib/pq v1.10.9
github.com/lmittmann/tint v1.0.4
github.com/mattn/go-sqlite3 v1.14.22
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc=
github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
Expand Down
25 changes: 12 additions & 13 deletions item/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ package item

import "time"

const (
PropertyTitle = "title"
)

// Item is the core data structure for the content management system.
// An item represent a single piece of content. It is the structured version of a markdown file.
type Item struct {
UID string `json:"uid" yaml:"uid"`
// Name is the full resource name of the item.
Name string `json:"name" yaml:"name"`
Path string `json:"path" yaml:"path"`
Content string `json:"content" yaml:"content"`
Hash string `json:"hash" yaml:"hash"`
CreateTime time.Time `json:"create_time" yaml:"create_time"`
UpdateTime time.Time `json:"update_time" yaml:"update_time"`
Properties map[string]any `json:"properties" yaml:"properties"`
UID string `mapstructure:"uid"`
Name string `mapstructure:"name"`
DisplayName string `mapstructure:"display_name"`
Content string `mapstructure:"content"`
Hash string `mapstructure:"hash"`
CreateTime time.Time `mapstructure:"create_time"`
UpdateTime time.Time `mapstructure:"update_time"`
DeleteTime *time.Time `mapstructure:"delete_time"`
Properties map[string]any `mapstructure:"properties"`
Metadata map[string]any `mapstructure:"metadata"`
}
Loading

0 comments on commit e529102

Please sign in to comment.