Skip to content

Commit

Permalink
lesson 61: Design DB for Email verification
Browse files Browse the repository at this point in the history
  • Loading branch information
blessedmadukoma committed Oct 2, 2023
1 parent 2f8666c commit a9ad1b2
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 10 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ migratedown:
migratedown1:
migrate -path db/migration -database "postgresql://postgres:postgres@localhost:5432/simplebank?sslmode=disable" -verbose down 1

new_migration:
migrate create -ext sql -dir db/migration -seq $(name)

sqlc:
sqlc generate

Expand Down Expand Up @@ -82,4 +85,4 @@ redis:
redisping:
docker exec -it redis redis-cli ping

.PHONY: postgres createdb dropdb psql createmigration migrateup migratedown migrateup1 migratedown1 sqlc test db_docs db_schema server mock proto evans redis redisping
.PHONY: postgres createdb dropdb psql createmigration migrateup migratedown migrateup1 migratedown1 sqlc test db_docs db_schema server mock proto evans redis redisping new_migration
3 changes: 3 additions & 0 deletions db/migration/000004_add_verify_emails.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DROP TABLE IF EXISTS "verify_emails" CASCADE;

ALTER TABLE "users" DROP COLUMN "is_email_verified";
13 changes: 13 additions & 0 deletions db/migration/000004_add_verify_emails.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CREATE TABLE "verify_emails" (
"id" bigserial PRIMARY KEY,
"username" varchar NOT NULL,
"email" varchar NOT NULL,
"secret_code" varchar NOT NULL,
"is_used" bool NOT NULL DEFAULT false,
"created_at" timestamptz NOT NULL DEFAULT (now()),
"expired_at" timestamptz NOT NULL DEFAULT (now() + interval '15 minutes')
);

ALTER TABLE "verify_emails" ADD FOREIGN KEY ("username") REFERENCES "users" ("username");

ALTER TABLE "users" ADD COLUMN "is_email_verified" bool NOT NULL DEFAULT false;
15 changes: 15 additions & 0 deletions db/mock/store.go

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

8 changes: 8 additions & 0 deletions db/query/verify_email.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- name: CreateVerifyEmail :one
INSERT INTO verify_emails (
username,
email,
secret_code
) VALUES (
$1, $2, $3
) RETURNING *;
11 changes: 11 additions & 0 deletions db/sqlc/models.go

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

1 change: 1 addition & 0 deletions db/sqlc/querier.go

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

9 changes: 6 additions & 3 deletions db/sqlc/user.sql.go

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

41 changes: 41 additions & 0 deletions db/sqlc/verify_email.sql.go

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

10 changes: 9 additions & 1 deletion details.md
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,12 @@
60. Skip tests
1. Skip tests that take time to run such as the E-mail test
2. added a check to see if testing that takes a long time i.e. `testing.Short()` is true in `sender_test.go`, then skip the test.
3. Updated the Makefile to include skipping the tests
3. Updated the Makefile to include skipping the tests

61. Design DB for Email verification
1. updated `db.dbml` docs to include email verification table. Ran `make db_schema` to generate a `schema.sql` file
2. added new `Makefile` command to migrate new schema i.e. `new_migration` and ran `make new_migration name=add_verify_emails` to generate a new migration file for `verify_emails`
3. ran `make migrateup` to commit the migration into the database
4. created `db/query/verify_email.sql` to generate code. Ran `make sqlc` and `make mock`.
5. updated `task_send_verify_email.go` by adding `CreateVerifyEmail` and imnplementing the mail sender. Updated `processor.go` to include the mailer as a field in the `RedisTaskProcessor` struct.
6. updated `main.go` to include the mailer.
22 changes: 21 additions & 1 deletion docs/db.dbml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,29 @@ Project simple_bank {
'''
}

Table users as U {
username varchar [pk]
hashed_password varchar [not null]
full_name varchar [not null]
email varchar [not null]
is_email_verified bool [not null, default: false]
password_changed_at timestamptz [not null, default: `0001-01-01`]
created_at timestamptz [not null, default: `now()`]
}

Table verify_emails {
id bigserial [pk]
username varchar [ref: > U.username, not null]
email varchar [not null]
secret_code varchar [not null]
is_used bool [not null, default: false]
created_at timestamptz [not null, default: `now()`]
expired_at timestamptz [not null, default: `now() + interval '15 minutes'`]
}

Table "accounts" {
"id" bigserial [pk, increment]
"owner" varchar [not null]
"owner" varchar [ref: > U.username, not null]
"balance" numeric [not null]
"currency" varchar [not null]
"created_at" timestamptz [not null, default: `now()`]
Expand Down
71 changes: 71 additions & 0 deletions docs/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
-- SQL dump generated using DBML (dbml-lang.org)
-- Database: PostgreSQL
-- Generated at: 2023-10-02T11:26:20.038Z

CREATE TABLE "users" (
"username" varchar PRIMARY KEY,
"hashed_password" varchar NOT NULL,
"full_name" varchar NOT NULL,
"email" varchar NOT NULL,
"is_email_verified" bool NOT NULL DEFAULT false,
"password_changed_at" timestamptz NOT NULL DEFAULT (0001-01-01),
"created_at" timestamptz NOT NULL DEFAULT (now())
);

CREATE TABLE "verify_emails" (
"id" bigserial PRIMARY KEY,
"username" varchar NOT NULL,
"email" varchar NOT NULL,
"secret_code" varchar NOT NULL,
"is_used" bool NOT NULL DEFAULT false,
"created_at" timestamptz NOT NULL DEFAULT (now()),
"expired_at" timestamptz NOT NULL DEFAULT (now() + interval '15 minutes')
);

CREATE TABLE "accounts" (
"id" BIGSERIAL PRIMARY KEY,
"owner" varchar NOT NULL,
"balance" numeric NOT NULL,
"currency" varchar NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT (now()),
"updated_at" timestamptz
);

CREATE TABLE "entries" (
"id" BIGSERIAL PRIMARY KEY,
"account_id" bigint NOT NULL,
"amount" numeric NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT (now())
);

CREATE TABLE "transfers" (
"id" BIGSERIAL PRIMARY KEY,
"from_account_id" bigint NOT NULL,
"to_account_id" bigint NOT NULL,
"amount" numeric NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT (now())
);

CREATE INDEX ON "accounts" ("owner");

CREATE INDEX ON "entries" ("account_id");

CREATE INDEX ON "transfers" ("from_account_id");

CREATE INDEX ON "transfers" ("to_account_id");

CREATE INDEX ON "transfers" ("from_account_id", "to_account_id");

COMMENT ON COLUMN "entries"."amount" IS 'can be poitive or negative';

COMMENT ON COLUMN "transfers"."amount" IS 'must be positive';

ALTER TABLE "verify_emails" ADD FOREIGN KEY ("username") REFERENCES "users" ("username");

ALTER TABLE "accounts" ADD FOREIGN KEY ("owner") REFERENCES "users" ("username");

ALTER TABLE "entries" ADD FOREIGN KEY ("account_id") REFERENCES "accounts" ("id");

ALTER TABLE "transfers" ADD FOREIGN KEY ("from_account_id") REFERENCES "accounts" ("id");

ALTER TABLE "transfers" ADD FOREIGN KEY ("to_account_id") REFERENCES "accounts" ("id");
9 changes: 6 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/blessedmadukoma/go-simple-bank/api"
db "github.com/blessedmadukoma/go-simple-bank/db/sqlc"
"github.com/blessedmadukoma/go-simple-bank/gapi"
"github.com/blessedmadukoma/go-simple-bank/mail"
"github.com/blessedmadukoma/go-simple-bank/pb"
"github.com/blessedmadukoma/go-simple-bank/util"
"github.com/blessedmadukoma/go-simple-bank/worker"
Expand Down Expand Up @@ -58,7 +59,7 @@ func main() {

taskDistributor := worker.NewRedisTaskDistributor(redisOpt)

go runTaskProcessor(redisOpt, store)
go runTaskProcessor(config, redisOpt, store)
// runGinServer(config, store)
go runGatewayServer(config, store, taskDistributor) // run in a separate goroutine
runGrpcServer(config, store, taskDistributor)
Expand All @@ -84,8 +85,10 @@ func runDBMigration(migrationURL string, dbSource string) {
log.Info().Msg("migration successful")
}

func runTaskProcessor(redisOpt asynq.RedisClientOpt, store db.Store) {
taskProcessor := worker.NewRedisTaskProcessor(redisOpt, store)
func runTaskProcessor(config util.Config, redisOpt asynq.RedisClientOpt, store db.Store) {
mailer := mail.NewGmailSender(config.EmailSenderName, config.EmailSenderAddress, config.EmailSenderPassword)

taskProcessor := worker.NewRedisTaskProcessor(redisOpt, store, mailer)

log.Info().Msg("starting task processor")
err := taskProcessor.Start()
Expand Down
5 changes: 4 additions & 1 deletion worker/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

db "github.com/blessedmadukoma/go-simple-bank/db/sqlc"
"github.com/blessedmadukoma/go-simple-bank/mail"
"github.com/go-redis/redis/v8"
"github.com/hibiken/asynq"
"github.com/rs/zerolog/log"
Expand All @@ -22,10 +23,11 @@ type TaskProcessor interface {
type RedisTaskProcessor struct {
server *asynq.Server
store db.Store
mailer mail.EmailSender
}

// NewRedisTaskProcessor creates a new RedisTaskProcessor
func NewRedisTaskProcessor(redisOpt asynq.RedisClientOpt, store db.Store) TaskProcessor {
func NewRedisTaskProcessor(redisOpt asynq.RedisClientOpt, store db.Store, mailer mail.EmailSender) TaskProcessor {

logger := NewLogger()
redis.SetLogger(logger)
Expand All @@ -44,6 +46,7 @@ func NewRedisTaskProcessor(redisOpt asynq.RedisClientOpt, store db.Store) TaskPr
return &RedisTaskProcessor{
server: server,
store: store,
mailer: mailer,
}
}

Expand Down
Loading

0 comments on commit a9ad1b2

Please sign in to comment.