Skip to content

Commit

Permalink
Merge pull request #275 from gofr-dev/release/v0.3.0
Browse files Browse the repository at this point in the history
Release/v0.3.0
  • Loading branch information
Umang01-hash authored Feb 19, 2024
2 parents 5af23a5 + ee2a07a commit 80cdfd4
Show file tree
Hide file tree
Showing 40 changed files with 1,013 additions and 96 deletions.
1 change: 1 addition & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ env:
jobs:
dockerize_stage:
runs-on: ubuntu-latest
if: (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main' || github.event_name == 'pull_request' )
outputs:
image: ${{ steps.output-image.outputs.image }}
name: 🐳 Dockerize Stage
Expand Down
109 changes: 109 additions & 0 deletions docs/advanced-guide/handling-data-migrations/page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Handling Data Migrations

Gofr supports data migrations for MySQL and Redis which allows to alter the state of a database, be it adding a new column to existing table or modifying the data type of an existing column or adding constraints to an existing table, setting and removing keys etc.

**How Migrations help?**

Suppose you manually edit fragments of your database, and now it's your responsibility to inform other developers to execute them. Additionally, you need to keep track of which changes should be applied to production machines in the next deployment.

GoFr maintains the table called **gofr_migration** which helps in such case. This table tracks which migrations have already been executed and ensures that only migrations that have never been run are executed. This way, you only need to ensure that your migrations are properly in place. ([Learn more](https://cloud.google.com/architecture/database-migration-concepts-principles-part-1))

## Usage

We will create an employee table using migrations.

### Creating Migration Files

It is recommended to maintain a migrations directory in your project root to enhance readability and maintainability.

**Migration file names**

It is recommended that each migration file should be numbered using the unix timestamp when the migration was created, This helps prevent numbering conflicts when working in a team environment.

Create the following file in migrations directory.

**Filename : 1708322067_create_employee_table.go**
```go
package migrations

import "gofr.dev/pkg/gofr/migration"


const createTable = `CREATE TABLE IF NOT EXISTS employee
(
id int not null
primary key,
name varchar(50) not null,
gender varchar(6) not null,
contact_number varchar(10) not null
);`

func createTableEmployee() migration.Migrate {
return migration.Migrate{
UP: func(d migration.Datasource) error {
_, err := d.DB.Exec(createTable)
if err != nil {
return err
}
return nil
},
}
}
```

`migration.Datasource` have the datasources whose migrations are supported which are Redis and MySQL.
All the migrations run in transactions by default.

For MySQL it is highly recommended to use `IF EXISTS` and `IF NOT EXIST` in DDL commands as MySQL implicitly commits these Commands.

**Create a function which returns all the migrations in a map**

**Filename : all.go**
```go
package migrations

import "gofr.dev/pkg/gofr/migration"

func All() map[int64]migration.Migrate {
return map[int64]migration.Migrate{
1708322067: createTableEmployee(),
}
}
```

Migrations will run in ascending order of keys in this map.

### Initialisation from main.go
```go
package main

import (
"errors"
"fmt"

"gofr.dev/examples/using-migrations/migrations"
"gofr.dev/pkg/gofr"
)

func main() {
// Create a new application
a := gofr.New()

// Add migrations to run
a.Migrate(migrations.All())

// Run the application
a.Run()
}

```

When we run the app we will see the following logs for migrations which ran successfully.

```bash
INFO [16:55:46] Migration 1708322067 ran successfully
```




2 changes: 1 addition & 1 deletion docs/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const navigation = [
{ title: 'Inter-service HTTP Calls', href: '/docs/advanced-guide/interservice-http-call' },
{ title: 'Publishing Custom Metrics', href: '/docs/advanced-guide/publishing-custom-metrics' },
{ title: 'Remote Log Level Change', href: '/docs/advanced-guide/remote-log-level-change' },
// { title: 'Handling Data Migrations', href: '/docs/advanced-guide/data-migrations' },
{ title: 'Handling Data Migrations', href: '/docs/advanced-guide/handling-data-migrations' },
// { title: 'Dealing with Remote Files', href: '/docs/advanced-guide/remote-files' },
// { title: 'Dynamic Configurations', href: '/docs/advanced-guide/dynamic-configs' },
// { title: 'Supporting OAuth', href: '/docs/advanced-guide/oauth' },
Expand Down
1 change: 1 addition & 0 deletions examples/sample-cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestCMDRunWithParams(t *testing.T) {
"command params -name=Vikash",
"command params -name=Vikash",
"command -name=Vikash params",
"command params -name=Vikash -",
}

for i, command := range commands {
Expand Down
14 changes: 14 additions & 0 deletions examples/using-migrations/configs/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
APP_NAME=using-migrations
HTTP_PORT=9000

REDIS_HOST=localhost
REDIS_PORT=2002

DB_HOST=localhost
DB_USER=root
DB_PASSWORD=password
DB_NAME=gofr
DB_PORT=2001

TRACER_HOST=localhost
TRACER_PORT=2005
77 changes: 77 additions & 0 deletions examples/using-migrations/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package main

import (
"errors"
"fmt"

"gofr.dev/examples/using-migrations/migrations"
"gofr.dev/pkg/gofr"
)

const (
queryGetEmployee = "SELECT id,name,gender,contact_number,dob from employee where name = ?"
queryInsertEmployee = "INSERT INTO employee (id, name, gender, contact_number,dob) values (?, ?, ?, ?, ?)"
)

func main() {
// Create a new application
a := gofr.New()

// Add migrations to run
a.Migrate(migrations.All())

// Add all the routes
a.GET("/employee", GetHandler)
a.POST("/employee", PostHandler)

// Run the application
a.Run()
}

type Employee struct {
ID int `json:"id"`
Name string `json:"name"`
Gender string `json:"gender"`
Phone int `json:"contact_number"`
DOB string `json:"dob"`
}

// GetHandler handles GET requests for retrieving employee information
func GetHandler(c *gofr.Context) (interface{}, error) {
name := c.Param("name")
if name == "" {
return nil, errors.New("name can't be empty")
}

row := c.DB.QueryRowContext(c, queryGetEmployee, name)
if row.Err() != nil {
return nil, errors.New(fmt.Sprintf("DB Error : %v", row.Err()))
}

var emp Employee

err := row.Scan(&emp.ID, &emp.Name, &emp.Gender, &emp.Phone, &emp.DOB)
if err != nil {
return nil, errors.New(fmt.Sprintf("DB Error : %v", err))
}

return emp, nil
}

// PostHandler handles POST requests for creating new employees
func PostHandler(c *gofr.Context) (interface{}, error) {
var emp Employee
if err := c.Bind(&emp); err != nil {
c.Logger.Errorf("error in binding: %v", err)
return nil, errors.New("invalid body")
}

// Execute the INSERT query
_, err := c.DB.ExecContext(c, queryInsertEmployee, emp.ID, emp.Name, emp.Gender, emp.Phone, emp.DOB)

if err != nil {
return Employee{}, errors.New(fmt.Sprintf("DB Error : %v", err))
}

return fmt.Sprintf("succesfully posted entity : %v", emp.Name), nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package migrations

import (
"gofr.dev/pkg/gofr/migration"
)

const createTable = `CREATE TABLE IF NOT EXISTS employee
(
id int not null
primary key,
name varchar(50) not null,
gender varchar(6) not null,
contact_number varchar(10) not null
);`

const employee_date = `INSERT INTO employee (id, name, gender, contact_number) VALUES (1, 'Umang', "M", "0987654321");`

func createTableEmployee() migration.Migrate {
return migration.Migrate{
UP: func(d migration.Datasource) error {
_, err := d.DB.Exec(createTable)
if err != nil {
return err
}

_, err = d.DB.Exec(employee_date)
if err != nil {
return err
}

_, err = d.DB.Exec("alter table employee add dob varchar(11) null;")
if err != nil {
return err
}

return nil
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package migrations

import (
"context"
"gofr.dev/pkg/gofr/migration"
)

func addEmployeeInRedis() migration.Migrate {
return migration.Migrate{
UP: func(d migration.Datasource) error {
err := d.Redis.Set(context.Background(), "Umang", "0987654321", 0).Err()
if err != nil {
return err
}

return nil

},
}
}
12 changes: 12 additions & 0 deletions examples/using-migrations/migrations/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package migrations

import (
"gofr.dev/pkg/gofr/migration"
)

func All() map[int64]migration.Migrate {
return map[int64]migration.Migrate{
1708322067: createTableEmployee(),
1708322089: addEmployeeInRedis(),
}
}
15 changes: 8 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/alicebob/miniredis/v2 v2.31.1
github.com/go-sql-driver/mysql v1.7.1
github.com/gogo/protobuf v1.3.2
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/joho/godotenv v1.5.1
Expand All @@ -15,13 +16,13 @@ require (
github.com/stretchr/testify v1.8.4
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.47.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0
go.opentelemetry.io/otel v1.22.0
go.opentelemetry.io/otel/exporters/prometheus v0.45.0
go.opentelemetry.io/otel/exporters/zipkin v1.22.0
go.opentelemetry.io/otel/metric v1.22.0
go.opentelemetry.io/otel/sdk v1.22.0
go.opentelemetry.io/otel/sdk/metric v1.22.0
go.opentelemetry.io/otel/trace v1.22.0
go.opentelemetry.io/otel v1.23.1
go.opentelemetry.io/otel/exporters/prometheus v0.45.2
go.opentelemetry.io/otel/exporters/zipkin v1.23.1
go.opentelemetry.io/otel/metric v1.23.1
go.opentelemetry.io/otel/sdk v1.23.1
go.opentelemetry.io/otel/sdk/metric v1.23.1
go.opentelemetry.io/otel/trace v1.23.1
go.uber.org/mock v0.4.0
golang.org/x/term v0.16.0
google.golang.org/grpc v1.61.0
Expand Down
29 changes: 15 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -119,20 +120,20 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.47.0/go.mod h1:2NonlJyJNVbDK/hCwiLsu5gsD2bVtmIzQ/tGzWq58us=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
go.opentelemetry.io/otel/exporters/prometheus v0.45.0 h1:BeIK2KGho0oCWa7LxEGSqfDZbs7Fpv/Viz+FS4P8CXE=
go.opentelemetry.io/otel/exporters/prometheus v0.45.0/go.mod h1:UVJZPLnfDSvHj+eJuZE+E1GjIBD267mEMfAAHJdghWg=
go.opentelemetry.io/otel/exporters/zipkin v1.22.0 h1:18n1VrUfs6uUYg+WgyC4Nl9bsb06gh+swvCVVhfwi7I=
go.opentelemetry.io/otel/exporters/zipkin v1.22.0/go.mod h1:/iI0r/ApELDJC7e+RDbBCxJBPvZ5hV2tVEBfXfgsCRY=
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
go.opentelemetry.io/otel/sdk/metric v1.22.0 h1:ARrRetm1HCVxq0cbnaZQlfwODYJHo3gFL8Z3tSmHBcI=
go.opentelemetry.io/otel/sdk/metric v1.22.0/go.mod h1:KjQGeMIDlBNEOo6HvjhxIec1p/69/kULDcp4gr0oLQQ=
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY=
go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA=
go.opentelemetry.io/otel/exporters/prometheus v0.45.2 h1:pe2Jqk1K18As0RCw7J08QhgXNqr+6npx0a5W4IgAFA8=
go.opentelemetry.io/otel/exporters/prometheus v0.45.2/go.mod h1:B38pscHKI6bhFS44FDw0eFU3iqG3ASNIvY+fZgR5sAc=
go.opentelemetry.io/otel/exporters/zipkin v1.23.1 h1:goka4KdsPPpHHQnzp1/XE1wVpk2oQO9RXCOH4MZWSyg=
go.opentelemetry.io/otel/exporters/zipkin v1.23.1/go.mod h1:KXTI1fJdTqRrQlIYgdmF4MnyAbHFWg1z320eOpL53qA=
go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo=
go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI=
go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E=
go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk=
go.opentelemetry.io/otel/sdk/metric v1.23.1 h1:T9/8WsYg+ZqIpMWwdISVVrlGb/N0Jr1OHjR/alpKwzg=
go.opentelemetry.io/otel/sdk/metric v1.23.1/go.mod h1:8WX6WnNtHCgUruJ4TJ+UssQjMtpxkpX0zveQC8JG/E0=
go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8=
go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
Expand Down
5 changes: 5 additions & 0 deletions pkg/gofr/cmd/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func NewRequest(args []string) *Request {
continue
}

if len(arg) == 1 {
continue
}

a := ""
if arg[1] == '-' {
a = arg[2:]
Expand Down Expand Up @@ -75,6 +79,7 @@ func (r *Request) PathParam(key string) string {
func (r *Request) Context() context.Context {
return context.Background()
}

func (r *Request) HostName() string {
h, err := os.Hostname()
if err != nil {
Expand Down
Loading

0 comments on commit 80cdfd4

Please sign in to comment.