Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# go-users-postgres-api
# go-users-postgres-api

# run tests
- go test ./tests/...

# run App
- go run main.go

## Project structure

42 changes: 42 additions & 0 deletions db/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package db

import (
"log"
"path"
"runtime"

databaseclient "github.com/GolangStudiy/go-users-postgres-rest-api/src/infrastructure"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file"
_ "github.com/lib/pq"
)

func Migrate() {
connection, err := databaseclient.GetDbConnection()

if err != nil {
log.Fatal(err)
}

driver, err := postgres.WithInstance(connection, &postgres.Config{})

if err != nil {
log.Fatal(err)
}

_, b, _, _ := runtime.Caller(0)
projectDir := path.Join(path.Dir(b))

m, err := migrate.NewWithDatabaseInstance(
"file://"+projectDir+"/migrations",
"users", driver)

if err != nil {
log.Fatal(err)
}

if err := m.Up(); err != nil {
log.Fatal(err)
}
}
1 change: 1 addition & 0 deletions db/migrations/000001_create_users_table.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE IF EXISTS users;
7 changes: 7 additions & 0 deletions db/migrations/000001_create_users_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE IF NOT EXISTS users(
id UUID DEFAULT UUID_GENERATE_V4(),
email VARCHAR (50) UNIQUE NOT NULL,
PRIMARY KEY (id)
);
4 changes: 4 additions & 0 deletions diagram.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module github.com/GolangStudiy/go-users-postgres-rest-api

go 1.18

require (
github.com/google/uuid v1.3.0
github.com/lib/pq v1.10.6
)

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Microsoft/hcsshim v0.9.2 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/containerd/containerd v1.6.1 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.16+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/mux v1.8.0
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/moby/sys/mount v0.3.2 // indirect
github.com/moby/sys/mountinfo v0.6.1 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/opencontainers/runc v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
google.golang.org/grpc v1.45.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

require (
github.com/golang-migrate/migrate/v4 v4.15.2
github.com/testcontainers/testcontainers-go v0.13.0
)

require (
github.com/containerd/cgroups v1.0.3 // indirect
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect
)
1,865 changes: 1,865 additions & 0 deletions go.sum

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "github.com/GolangStudiy/go-users-postgres-rest-api/src/server"

func main() {
server.Main()
}
13 changes: 13 additions & 0 deletions src/domain/user/emailvalidator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package domain

import "net/mail"

func ValidateEmail(email string) (string, error) {
address, err := mail.ParseAddress(email)

if err != nil {
return "", err
}

return address.Address, err
}
6 changes: 6 additions & 0 deletions src/domain/user/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package domain

type UserRepository interface {
Post(user User) (string, error)
GetIdByEmail(email string) (string, error)
}
6 changes: 6 additions & 0 deletions src/domain/user/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package domain

type User struct {
ID string
Email string
}
50 changes: 50 additions & 0 deletions src/entrypoint/user/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package entrypoint

import (
"encoding/json"
"io/ioutil"
"net/http"

services "github.com/GolangStudiy/go-users-postgres-rest-api/src/services/user"
"github.com/gorilla/mux"
)

func PostUser(w http.ResponseWriter, request *http.Request) {
reqBody, _ := ioutil.ReadAll(request.Body)
var user RequestUser
json.Unmarshal(reqBody, &user)

responseEmail, err := services.Post(user.Email)

if err != nil {
respondWithError(w, http.StatusBadRequest, err.Error())
} else {
respondWithJSON(w, http.StatusCreated, responseEmail)
}
}

func GetUserIdByEmail(w http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)

email := vars["email"]

id, err := services.GetIdByEmail(email)

if err != nil {
respondWithError(w, http.StatusBadRequest, err.Error())
} else {
respondWithJSON(w, http.StatusOK, id)
}
}

func respondWithError(w http.ResponseWriter, code int, message string) {
respondWithJSON(w, code, map[string]string{"error": message})
}

func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
5 changes: 5 additions & 0 deletions src/entrypoint/user/requestuser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package entrypoint

type RequestUser struct {
Email string
}
58 changes: 58 additions & 0 deletions src/infrastructure/databaseclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package infrastructure

import (
"database/sql"
"fmt"
"os"
"time"

_ "github.com/lib/pq"
)

func GetDbConnection() (*sql.DB, error) {
host := os.Getenv("DB_HOST")
port := os.Getenv("DB_PORT")
user := os.Getenv("DB_USERNAME")
password := os.Getenv("DB_PASSWORD")
dbname := os.Getenv("DB_NAME")
psqlInfo := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
connection, err := sql.Open("postgres", psqlInfo)

if err != nil {
return nil, err
}

err = connection.Ping()

limit := 0
for limit < 10 && err != nil {
time.Sleep(2 * time.Second)
err = connection.Ping()
if err == nil {
limit = 10
}
}

if err != nil {
return nil, err
}

return connection, nil
}

func RunQuery(sql string) (*sql.Rows, error) {
connection, err := GetDbConnection()

if err != nil {
return nil, err
}

data, err := connection.Query(sql)
defer connection.Close()

if err != nil {
return nil, err
}

return data, nil
}
46 changes: 46 additions & 0 deletions src/infrastructure/user/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package infrastructure

import (
"fmt"

domain "github.com/GolangStudiy/go-users-postgres-rest-api/src/domain/user"
databaseclient "github.com/GolangStudiy/go-users-postgres-rest-api/src/infrastructure"
)

type UserRepository struct {
}

func (r *UserRepository) Post(domainUser domain.User) (string, error) {
rows, err := databaseclient.RunQuery(
fmt.Sprintf(
"INSERT INTO users (email) VALUES ('%s') RETURNING email;",
User{Email: domainUser.Email}.Email,
),
)

if err != nil {
return "", err
}

var email string
if rows.Next() {
rows.Scan(&email)
}

return email, nil
}

func (r *UserRepository) GetIdByEmail(email string) (string, error) {
rows, err := databaseclient.RunQuery(fmt.Sprintf("SELECT id FROM users WHERE email = '%s';", email))

if err != nil {
return "", nil
}

var id string
if rows.Next() {
rows.Scan(&id)
}

return id, nil
}
8 changes: 8 additions & 0 deletions src/infrastructure/user/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package infrastructure

import "github.com/google/uuid"

type User struct {
ID uuid.UUID `gorm:"primaryKey;column:id"`
Email string `gorm:"column:email"`
}
14 changes: 14 additions & 0 deletions src/server/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package server

import (
entrypoint "github.com/GolangStudiy/go-users-postgres-rest-api/src/entrypoint/user"
"github.com/gorilla/mux"
)

func Router() *mux.Router {
r := mux.NewRouter().StrictSlash(true)
r.HandleFunc("/users", entrypoint.PostUser).Methods("POST")
r.HandleFunc("/users/get-id-by-email/{email}", entrypoint.GetUserIdByEmail).Methods("GET")

return r
}
Loading