Skip to content

Commit

Permalink
Add: with localstack tinyurl create api
Browse files Browse the repository at this point in the history
  • Loading branch information
naohito-T committed May 5, 2024
1 parent cbdd2e8 commit d26bce6
Show file tree
Hide file tree
Showing 18 changed files with 263 additions and 166 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# tinyurl

<details>
<summary style="font-size: 20px">🔱 CI Actions Status</summary>
[![Test Backend](https://github.com/naohito-T/tinyurl/actions/workflows/test_backend.yml/badge.svg?branch=main)](https://github.com/naohito-T/tinyurl/actions/workflows/test_backend.yml)
</details>
[![Test Backend](https://github.com/naohito-T/tinyurl/actions/workflows/test_backend.yml/badge.svg?branch=main)](https://github.com/naohito-T/tinyurl/actions/workflows/test_backend.yml)

## Overview

Expand Down
4 changes: 1 addition & 3 deletions backend/cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ type Options struct {
// initHuma: humaのconfigを初期化
func initHuma() huma.Config {
config := huma.DefaultConfig(configs.OpenAPITitle, configs.OpenAPIVersion)
// Openapiのserver設定
config.Servers = []*huma.Server{
{URL: "http://localhost:6500/api/v1"},
{URL: configs.OpenAPIDocServerPath},
}

config.Components.SecuritySchemes = map[string]*huma.SecurityScheme{
Expand Down Expand Up @@ -60,7 +59,6 @@ func main() {
configs.NewAppEnvironment()
// ミドルウェアを適用(すべてのリクエストに対して)
middleware.CustomMiddleware(e)

// これgroup化したやつをnewUserRouterに渡す必要かも
api = humaecho.NewWithGroup(e, e.Group("/api/v1"), initHuma())
router.NewPublicRouter(api)
Expand Down
39 changes: 35 additions & 4 deletions backend/configs/constructor.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
package configs

const (
ApplicationPort = ":6500"
OpenAPITitle = "TinyURL API"
OpenAPIVersion = "1.0.0"
OpenAPIServerPath = "http://localhost:6500/api/v1"
// ApplicationPort is the port the application listens on.
ApplicationPort = ":6500"
// OpenAPITitle is the title of the OpenAPI spec.
OpenAPITitle = "TinyURL API"
// OpenAPIVersion is the version of the OpenAPI spec.
OpenAPIVersion = "1.0.0"
// OpenAPIServerPath is the base URL for the OpenAPI spec.
OpenAPIDocServerPath = "http://localhost:6500/api/v1"
)

// OperationID: このAPI操作の一意の識別子。これは、API内で操作を参照する際に使用されます。
// Method: HTTPメソッドを指定します。この例では http.MethodGet が使われており、これはHTTPのGETリクエストを示します。
// Path: エンドポイントのURLパスを指定します。ここでは "/greeting/{name}" となっており、{name} はパスパラメータを表しています。
// Summary: 短い説明文です。APIのドキュメントに表示され、APIの目的を簡潔に説明します。
// Description: APIエンドポイントの詳細な説明です。ここでは操作の詳細や動作についての追加情報を提供します。
// Tags: このAPI操作に関連付けられたタグのリストです。これにより、APIドキュメント内で類似の操作をグループ化することができます。


Check failure on line 21 in backend/configs/constructor.go

View workflow job for this annotation

GitHub Actions / lint-backend

File is not `goimports`-ed (goimports)
// huma.Register(app, huma.Operation{
// OperationID: "health",
// Method: http.MethodGet,
// Path: Router.Health,
// Summary: "Health Check",
// Description: "Check the health of the service.",
// Tags: []string{"Public"},
// }, func(_ context.Context, _ *HealthCheckParams) (*HealthCheckQuery, error) {
// resp := &HealthCheckQuery{
// Body: struct{
// Message string `json:"message,omitempty" example:"Hello, world!" doc:"Greeting message"`
// }{
// Message: "ok",
// },
// }
// fmt.Printf("Health Check: %v\n", resp.Body.Message)
// return resp, nil
// })
9 changes: 6 additions & 3 deletions backend/configs/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ package configs
type path = string

const (
Health path = "/health"
GetShortURL path = "/api/v1/urls/:shortUrl"
CreateShortURL path = "/api/v1/urls"
// /api/v1/health
Health path = "/health"
// /api/v1/urls
GetShortURL path = "/urls/:shortUrl"
// /api/v1/urls
CreateShortURL path = "/urls"
)
2 changes: 0 additions & 2 deletions backend/docker/localstack/init-aws.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ echo "init dynamoDB"
awslocal dynamodb create-table --table-name offline-tinyurls \
--attribute-definitions \
AttributeName=id,AttributeType=S \
AttributeName=originalURL,AttributeType=S \
AttributeName=createdAt,AttributeType=S \
--key-schema \
AttributeName=id,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Expand Down
18 changes: 18 additions & 0 deletions backend/domain/shorturl.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
package domain

import (
"crypto/sha1"

Check failure on line 4 in backend/domain/shorturl.go

View workflow job for this annotation

GitHub Actions / lint-backend

G505: Blocklisted import crypto/sha1: weak cryptographic primitive (gosec)
"encoding/base64"
"strings"
"time"
)

type ShortURL struct {
ID string `json:"id"`
OriginalURL string `json:"original"`
CreatedAt string `json:"created_at"`
}

func GenerateShortURL(originalURL string) *ShortURL {
hasher := sha1.New()

Check failure on line 17 in backend/domain/shorturl.go

View workflow job for this annotation

GitHub Actions / lint-backend

G401: Use of weak cryptographic primitive (gosec)
hasher.Write([]byte(originalURL))
sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
return &ShortURL{
ID: strings.TrimRight(sha, "=")[:7],
OriginalURL: originalURL,
CreatedAt: time.Now().Format(time.RFC3339),
}
}
8 changes: 0 additions & 8 deletions backend/domain/url.go

This file was deleted.

2 changes: 1 addition & 1 deletion backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.27.11
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.13
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.31.1
github.com/danielgtaylor/huma/v2 v2.14.0
github.com/danielgtaylor/huma/v2 v2.15.0
github.com/go-playground/validator/v10 v10.19.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/labstack/echo/v4 v4.11.4
Expand Down
2 changes: 2 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/danielgtaylor/huma/v2 v2.14.0 h1:lRuhQQPZhePvJ4B/m4kfIUYfD8ZPy5BKq/oktLFmB50=
github.com/danielgtaylor/huma/v2 v2.14.0/go.mod h1:OdHC/JliXtOrnvHLQTU5qV7WvYRQXwWY1tkl5rLXmuE=
github.com/danielgtaylor/huma/v2 v2.15.0 h1:26c3hxNT+0xNc8qDLPXNko48qyi31RDFQdhi36gorRI=
github.com/danielgtaylor/huma/v2 v2.15.0/go.mod h1:OdHC/JliXtOrnvHLQTU5qV7WvYRQXwWY1tkl5rLXmuE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
14 changes: 13 additions & 1 deletion backend/internal/infrastructures/dynamo/dynamo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"log/slog"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
)
Expand All @@ -13,7 +14,18 @@ type Connection struct {
}

func NewDynamoConnection() *Connection {
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("ap-northeast-1"))
// https://zenn.dev/y16ra/articles/40ff14e8d2a4db
customResolver := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {

Check failure on line 18 in backend/internal/infrastructures/dynamo/dynamo.go

View workflow job for this annotation

GitHub Actions / lint-backend

SA1019: aws.EndpointResolverFunc is deprecated: See EndpointResolverWithOptionsFunc (staticcheck)
return aws.Endpoint{
PartitionID: "aws",
URL: "http://aws:4566", // LocalStackのDynamoDBエンドポイント
SigningRegion: "ap-northeast-1",
}, nil
})
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion("ap-northeast-1"),
config.WithEndpointResolver(customResolver),

Check failure on line 27 in backend/internal/infrastructures/dynamo/dynamo.go

View workflow job for this annotation

GitHub Actions / lint-backend

SA1019: config.WithEndpointResolver is deprecated: See WithEndpointResolverWithOptions (staticcheck)
)
if err != nil {
slog.Error("unable to load SDK config, %v", err)
}
Expand Down
32 changes: 22 additions & 10 deletions backend/internal/repository/dynamo/shorturl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package dynamo

import (
"context"
"errors"
"log"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/smithy-go"

"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
"github.com/naohito-T/tinyurl/backend/domain"
Expand All @@ -24,7 +26,6 @@ type ShortURLRepository struct {
logger slog.ILogger
}

// NewShortURLRepository アスタリスク * は、ポインタを通じてポインタが指し示すアドレスに格納されている実際の値にアクセスするために使います。また、型宣言でポインタ型を示す際にも用いられます。
func NewShortURLRepository(client *DynamoClient.Connection, logger slog.ILogger) *ShortURLRepository {
// &ShortURLRepository{...} によって ShortURLRepository 型の新しいインスタンスがメモリ上に作成され、そのインスタンスのアドレスが返されます
return &ShortURLRepository{
Expand All @@ -43,11 +44,12 @@ type ItemKey struct {
ID string `dynamodbav:"id"`
}

func (r *ShortURLRepository) GetByShortURL(id string) (domain.ShortURL, error) {
r.logger.Debug("GetItemInput: %v", id)
// 構造体に属することで、構造体が初期されていないと呼び出すことはできないことになる。
func (r *ShortURLRepository) Get(ctx context.Context, hashURL string) (domain.ShortURL, error) {
r.logger.Debug("GetItemInput: %v", hashURL)

itemKey := ItemKey{
ID: id,
ID: hashURL,
}

av, err := attributevalue.MarshalMap(itemKey)
Expand All @@ -56,7 +58,7 @@ func (r *ShortURLRepository) GetByShortURL(id string) (domain.ShortURL, error) {
return domain.ShortURL{}, err
}

result, err := r.Client.Conn.GetItem(context.TODO(), &dynamodb.GetItemInput{
result, err := r.Client.Conn.GetItem(ctx, &dynamodb.GetItemInput{
TableName: aws.String("offline-tinyurls"),
Key: av,
})
Expand All @@ -82,8 +84,8 @@ func (r *ShortURLRepository) GetByShortURL(id string) (domain.ShortURL, error) {
return shortURL, nil
}

func (r *ShortURLRepository) CreateShortURL(params *domain.ShortURL) error {
r.logger.Debug("PutItemInput: %v", params)
func (r *ShortURLRepository) Put(ctx context.Context, params *domain.ShortURL) (domain.ShortURL, error) {
r.logger.Info("PutItemInput: %v", params)

item := TableItem{
ID: params.ID,
Expand All @@ -93,15 +95,25 @@ func (r *ShortURLRepository) CreateShortURL(params *domain.ShortURL) error {
av, err := attributevalue.MarshalMap(item)
if err != nil {
log.Fatal(err)
return domain.ShortURL{}, err // エラー時にゼロ値を返す
}

_, err = r.Client.Conn.PutItem(context.TODO(), &dynamodb.PutItemInput{
_, err = r.Client.Conn.PutItem(ctx, &dynamodb.PutItemInput{
TableName: aws.String("offline-tinyurls"),
Item: av,
})
var oe *smithy.OperationError
if errors.As(err, &oe) {
log.Printf("failed to call service: %s, operation: %s, error: %v", oe.Service(), oe.Operation(), oe.Unwrap())
}
if err != nil {
log.Fatal(err)
return err
return domain.ShortURL{}, err // エラー時にゼロ値を返す
}
return nil
shortURL := domain.ShortURL{
ID: params.ID,
OriginalURL: params.OriginalURL,
CreatedAt: params.CreatedAt,
}
return shortURL, nil
}
4 changes: 2 additions & 2 deletions backend/internal/rest/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ type GuestContainer struct {
URLUsecase *usecase.ShortURLUsecase
}

var newGuestContainer = sync.OnceValue(func() *GuestContainer {
var onceGuestContainer = sync.OnceValue(func() *GuestContainer {
dynamoRepo := repoDynamo.NewShortURLRepository(dynamo.NewDynamoConnection(), slog.NewLogger())
return &GuestContainer{
URLUsecase: usecase.NewURLUsecase(dynamoRepo),
}
})

func NewGuestContainer() *GuestContainer {
return newGuestContainer()
return onceGuestContainer()
}
47 changes: 0 additions & 47 deletions backend/internal/rest/context/humaecho.go

This file was deleted.

7 changes: 0 additions & 7 deletions backend/internal/rest/handler/handler.go

This file was deleted.

27 changes: 23 additions & 4 deletions backend/internal/rest/handler/shorturl.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
package handler

import (
"net/http"
"context"

"github.com/labstack/echo/v4"
"github.com/naohito-T/tinyurl/backend/domain"
"github.com/naohito-T/tinyurl/backend/internal/infrastructures/slog"
"github.com/naohito-T/tinyurl/backend/internal/rest/container"
)

func ShortURLHandler(c echo.Context) error {
return c.String(http.StatusOK, "OK")
type ShortURLHandler struct {
container *container.GuestContainer
}

// IShortURLHandler defines the interface for short URL handler operations.
type IShortURLHandler interface {
CreateShortURLHandler(ctx context.Context, originalURL string) (domain.ShortURL, error)
}

// NewShortURLHandler creates a new handler for short URLs.
func NewShortURLHandler(c *container.GuestContainer) IShortURLHandler {
return &ShortURLHandler{
container: c,
}
}

func (s *ShortURLHandler) CreateShortURLHandler(ctx context.Context, originalURL string) (domain.ShortURL, error) {
slog.NewLogger().Info("CreateShortURLHandler: %v", originalURL)
return s.container.URLUsecase.CreateShortURL(ctx, originalURL)
}
Loading

0 comments on commit d26bce6

Please sign in to comment.