Skip to content

Commit

Permalink
Merge pull request #14 from naohito-T/feature/logger
Browse files Browse the repository at this point in the history
Feature/logger
  • Loading branch information
naohito-T authored Sep 22, 2024
2 parents f120aeb + 32487d1 commit 3f31b7a
Show file tree
Hide file tree
Showing 28 changed files with 643 additions and 459 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
"markdownlint.config": {
"MD013": false,
"MD033": false
}
},
"makefile.configureOnOpen": false
}
108 changes: 75 additions & 33 deletions backend/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,6 @@ components:
ErrorModel:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- http://localhost:6500/api/v1/schemas/ErrorModel.json
format: uri
readOnly: true
type: string
detail:
description: A human-readable explanation specific to this occurrence of the problem.
examples:
Expand All @@ -64,7 +57,9 @@ components:
description: Optional list of individual error details
items:
$ref: "#/components/schemas/ErrorDetail"
type: array
type:
- array
- "null"
instance:
description: A URI reference that identifies the specific occurrence of the problem.
examples:
Expand Down Expand Up @@ -114,13 +109,6 @@ components:
HealthCheckResponseBody:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- http://localhost:6500/api/v1/schemas/HealthCheckResponseBody.json
format: uri
readOnly: true
type: string
message:
type: string
required:
Expand All @@ -132,42 +120,71 @@ components:
scheme: bearer
type: http
info:
contact:
email: [email protected]
name: naohito-T
url: https://naohito-t.github.io/
description: This is a simple URL shortener service.
license:
name: MIT
url: https://opensource.org/licenses/MIT
title: TinyURL API
version: 1.0.0
openapi: 3.1.0
paths:
/health:
get:
description: Check the health of the service.
operationId: health
operationId: health-check
parameters:
- description: Optional database check parameter
- description: Optional DynamoDB check parameter
explode: false
in: query
name: q
schema:
description: Optional database check parameter
description: Optional DynamoDB check parameter
type: boolean
responses:
"200":
content:
application/json:
example: "{message: ok}"
schema:
$ref: "#/components/schemas/HealthCheckResponseBody"
description: OK
default:
properties:
message:
type: string
type: object
description: Health check successful
"503":
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ErrorModel"
description: Error
properties:
detail:
type: string
status:
type: integer
title:
type: string
type:
format: uri
type: string
type: object
description: Service unavailable
summary: Health Check
tags:
- Public
/urls:
post:
description: Create a short URL.
operationId: create-tinyurl
parameters:
- description: ID of the short URL
in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
Expand All @@ -181,12 +198,20 @@ paths:
schema:
$ref: "#/components/schemas/CreateTinyURLResponseBody"
description: OK
default:
"201":
description: Created short URL
headers:
Location:
description: Location of the original URL
schema:
format: uri
type: string
"500":
content:
application/problem+json:
text/plain:
schema:
$ref: "#/components/schemas/ErrorModel"
description: Error
type: string
description: Failed to create short URL
summary: Create a short URL
tags:
- Public
Expand Down Expand Up @@ -226,14 +251,26 @@ paths:
schema:
type: string
description: Short URL not found
"500":
content:
text/plain:
schema:
type: string
description: Failed to Get short URL
summary: Redirect to original URL
tags:
- Public
/urls/info/:id:
/urls/:id/info:
get:
description: Get Info tinyurl
operationId: info-tinyurl
parameters:
- description: ID of the short URL
in: path
name: id
required: true
schema:
type: string
- in: path
name: id
required: true
Expand All @@ -246,15 +283,20 @@ paths:
schema:
$ref: "#/components/schemas/GetInfoTinyURLResponseBody"
description: OK
default:
"404":
content:
application/problem+json:
text/plain:
schema:
$ref: "#/components/schemas/ErrorModel"
description: Error
type: string
description: Short URL not found
summary: Get Info tinyurl
tags:
- Public
servers:
- url: http://localhost:6500/api/v1
- description: Local API Server
url: http://localhost:6500/api/v1
- description: Dev API Server
url: http://localhost:6500/api/v1
- description: Prod API Server
url: http://localhost:6500/api/v1

21 changes: 15 additions & 6 deletions backend/cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
"github.com/danielgtaylor/huma/v2/humacli"
"github.com/labstack/echo/v4"
"github.com/naohito-T/tinyurl/backend/configs"
"github.com/naohito-T/tinyurl/backend/internal/infrastructure"
infra "github.com/naohito-T/tinyurl/backend/internal/infrastructure"
"github.com/naohito-T/tinyurl/backend/internal/rest/container"
"github.com/naohito-T/tinyurl/backend/internal/rest/middleware"
"github.com/naohito-T/tinyurl/backend/internal/rest/router"
appSchema "github.com/naohito-T/tinyurl/backend/schema/api"
Expand All @@ -33,16 +34,20 @@ type Options struct {
// private(管理者)

func main() {
var api huma.API
var c configs.AppEnvironment
logger := infrastructure.NewLogger()
var publicAPI huma.API
// var privateAPI huma.API
var c *configs.AppEnvironment

cli := humacli.New(func(hooks humacli.Hooks, opts *Options) {
e := echo.New()
c = configs.NewAppEnvironment()

middleware.CustomMiddleware(e, c)
api = router.NewPublicRouter(humaecho.NewWithGroup(e, e.Group("/api/v1"), appSchema.NewHumaConfig()), logger)

public := e.Group("/v1/public")
// private := e.Group("/v1/private")
publicAPI = router.NewPublicRouter(humaecho.NewWithGroup(e, public, appSchema.NewHumaConfig()), container.PublicContainer, infra.RouterLogger)
// privateAPI = router.NewPublicRouter(humaecho.NewWithGroup(e, private, appSchema.NewHumaConfig()), container.PublicContainer, infra.RouterLogger)
// 未定義のルート用のキャッチオールハンドラ
e.Any("/*", func(c echo.Context) error {
return c.JSON(http.StatusNotFound, map[string]string{"message": "route_not_found"})
Expand All @@ -53,13 +58,17 @@ func main() {
})
})

// Command
cli.Root().AddCommand(&cobra.Command{
Use: "openapi",
Short: "Print the OpenAPI spec",
Run: func(_ *cobra.Command, _ []string) {
b, _ := api.OpenAPI().YAML()
b, _ := publicAPI.OpenAPI().YAML()
// privateAPI.OpenAPI().YAML()
fmt.Println(string(b))
},
})

// Run the CLI
cli.Run()
}
24 changes: 15 additions & 9 deletions backend/configs/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ type AppEnvironment struct {
tinyURLCollection string `default:"offline-tinyurls"`
}

var newOnceLogger = sync.OnceValue(func() AppEnvironment {
var ae AppEnvironment
if err := envconfig.Process("", &ae); err != nil {
// コピーで返す場合
// 構造体のコピーを返すだけなので生成も一度だけでコストは低い。
// ただし、構造体が大きい場合はコピーのコストがかかるので注意。
// 参照で返す場合
// 構造体のコピーを作成せずに参照を返すため、コストは低い。
var newOnceEnvironment = sync.OnceValue(func() *AppEnvironment {
var appEnvironment AppEnvironment
if err := envconfig.Process("", &appEnvironment); err != nil {
panic(fmt.Sprintf("Failed to process environment config: %v", err))
}
return ae
})

// SEE: https://pkg.go.dev/github.com/kelseyhightower/envconfig
func NewAppEnvironment() AppEnvironment {
return newOnceLogger()
}
return &appEnvironment
})

func (a *AppEnvironment) IsTest() bool {
return a.stage == "test"
Expand All @@ -44,3 +45,8 @@ func (a *AppEnvironment) IsProd() bool {
func (a *AppEnvironment) GetTinyURLCollectionName() string {
return a.tinyURLCollection
}

// SEE: https://pkg.go.dev/github.com/kelseyhightower/envconfig
func NewAppEnvironment() *AppEnvironment {
return newOnceEnvironment()
}
12 changes: 6 additions & 6 deletions backend/configs/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ type path = string
const (
// /api/v1/health
Health path = "/health"
// リダイレクト用エンドポイント
GetShortURL path = "/urls/:id"
// すべてのURLを取得するエンドポイント
GetURLs path = "/urls"
// 短縮URLを作成するためのエンドポイント
CreateShortURL path = "/urls"
// すべてのURLをリストするためのエンドポイント
ListShortURLs path = "/urls/list"
// 特定の短縮URLの詳細情報を取得
GetOnlyShortURL path = "/urls/info/:id"
// リダイレクト用エンドポイント
GetShortURL path = "/urls/:id"
// 特定の短縮URLの詳細情報を取得(リダイレクトなし)
GetOnlyShortURL path = "/urls/:id/info"
)
22 changes: 12 additions & 10 deletions backend/internal/infrastructure/dynamo.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ type Client interface {

type Connection struct {
*dynamodb.Client
ILogger
configs.AppEnvironment
ILabelLogger
*configs.AppEnvironment
}

func NewDynamoConnection(logger ILogger, env configs.AppEnvironment) *Connection {
func NewDynamoConnection(logger ILabelLogger, env *configs.AppEnvironment) *Connection {
// https://zenn.dev/y16ra/articles/40ff14e8d2a4db
customResolver := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {

Check failure on line 26 in backend/internal/infrastructure/dynamo.go

View workflow job for this annotation

GitHub Actions / lint-backend

SA1019: aws.EndpointResolverFunc is deprecated: See EndpointResolverWithOptionsFunc (staticcheck)
return aws.Endpoint{
Expand All @@ -35,7 +35,9 @@ func NewDynamoConnection(logger ILogger, env configs.AppEnvironment) *Connection
config.WithEndpointResolver(customResolver),

Check failure on line 35 in backend/internal/infrastructure/dynamo.go

View workflow job for this annotation

GitHub Actions / lint-backend

SA1019: config.WithEndpointResolver is deprecated: See WithEndpointResolverWithOptions (staticcheck)
)
if err != nil {
logger.Error("unable to load SDK config, %v", err)
logger.Error("unable to load SDK config", map[string]interface{}{}, map[string]interface{}{
"error": err,
})
}

return &Connection{
Expand All @@ -46,26 +48,26 @@ func NewDynamoConnection(logger ILogger, env configs.AppEnvironment) *Connection
}

func (c *Connection) Get(ctx context.Context, params *dynamodb.GetItemInput) (*dynamodb.GetItemOutput, error) {
c.Info("GetItemInput: %v", params)
// c.Info("GetItemInput: %v", params)
if result, err := c.GetItem(ctx, &dynamodb.GetItemInput{
TableName: aws.String(c.GetTinyURLCollectionName()),
Key: params.Key,
}); err != nil {
c.Error("Get error: %v", err)
// c.Error("Get error: %v", err)
return nil, err
} else {

Check warning on line 58 in backend/internal/infrastructure/dynamo.go

View workflow job for this annotation

GitHub Actions / lint-backend

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
return result, nil
}
}

func (c *Connection) Put(ctx context.Context, params *dynamodb.PutItemInput, optFns ...func(*dynamodb.Options)) (*dynamodb.PutItemOutput, error) {
c.Info("PutItemInput: %v", params)
// c.Info("PutItemInput: %v", params)
if result, err := c.PutItem(ctx, &dynamodb.PutItemInput{
// rateLimitなどテーブルnameは上から渡したほうがいいかも
TableName: aws.String(c.GetTinyURLCollectionName()),
Item: params.Item,
}, optFns...); err != nil {
c.Error("Put error: %v", err)
// c.Error("Put error: %v", err)
return nil, err
} else {

Check warning on line 72 in backend/internal/infrastructure/dynamo.go

View workflow job for this annotation

GitHub Actions / lint-backend

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
return result, nil
Expand All @@ -83,9 +85,9 @@ func (c *Connection) Put(ctx context.Context, params *dynamodb.PutItemInput, opt
// },
// }
func (c *Connection) Search(ctx context.Context, params *dynamodb.QueryInput, optFns ...func(*dynamodb.Options)) (*dynamodb.QueryOutput, error) {
c.Info("SearchItemInput: %v", params)
// c.Info("SearchItemInput: %v", params)
if result, err := c.Query(ctx, params, optFns...); err != nil {
c.Error("Query error: %v", err)
// c.Error("Query error: %v", err)
return nil, err
} else {

Check warning on line 92 in backend/internal/infrastructure/dynamo.go

View workflow job for this annotation

GitHub Actions / lint-backend

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive)
return result, nil
Expand Down
Loading

0 comments on commit 3f31b7a

Please sign in to comment.