From a591bafa7cefe3088f87cd6cb8f2ee03eae51728 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Wed, 25 Jun 2025 17:03:12 +0800 Subject: [PATCH 01/17] start working on cache --- .env.example | 2 +- Makefile | 2 +- boost/router.go | 2 +- handler/profile.go | 28 +++++++++++++++++++++++++--- handler/response/profile.go | 14 ++++++++++++++ handler/version/version.go | 28 ++++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 handler/response/profile.go create mode 100644 handler/version/version.go diff --git a/.env.example b/.env.example index 375dc374..860ca254 100644 --- a/.env.example +++ b/.env.example @@ -27,5 +27,5 @@ EN_DB_BIN_DIR="" # --- This flag is only needed/used for docker purposes. # The full db url value is only used for docker purposes given the application works in the -GORM DSN- # Example: postgresql://gocanto-user:gocanto-password@postgres:5432/oullin_db?sslmode=require -ENV_DB_URL="" +ENV_DB_URL="postgresql://gocanto-user:gocanto-password@postgres:5432/oullin_db?sslmode=require" diff --git a/Makefile b/Makefile index 044be022..a4a23824 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ YELLOW := \033[1;33m # -------------------------------------------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------------------------------------------- # -ROOT_NETWORK := gocanto +ROOT_NETWORK := "oullin_net" DATABASE := postgres SOURCE := go_bindata ROOT_PATH := $(shell pwd) diff --git a/boost/router.go b/boost/router.go index b280b9d4..f0f0d6bf 100644 --- a/boost/router.go +++ b/boost/router.go @@ -29,7 +29,7 @@ func (r *Router) PipelineFor(apiHandler http.ApiHandler) baseHttp.HandlerFunc { } func (r *Router) Profile() { - abstract := handler.MakeProfileHandler() + abstract := handler.MakeProfileHandler("./storage/fixture/profile.json") resolver := r.PipelineFor( abstract.Handle, diff --git a/handler/profile.go b/handler/profile.go index c7613c39..b101797e 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -1,7 +1,10 @@ package handler import ( + "encoding/json" + "github.com/oullin/handler/response" "github.com/oullin/pkg/http" + "log" "log/slog" baseHttp "net/http" "os" @@ -11,9 +14,9 @@ type ProfileHandler struct { content string } -func MakeProfileHandler() ProfileHandler { +func MakeProfileHandler(fixture string) ProfileHandler { return ProfileHandler{ - content: "./storage/fixture/profile.json", + content: fixture, } } @@ -26,9 +29,28 @@ func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) * return http.InternalError("could not read profile data") } - if err := writeJSON(fixture, w); err != nil { + var data response.ProfileResponse + if err := json.Unmarshal(fixture, &data); err != nil { return http.InternalError(err.Error()) } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(baseHttp.StatusOK) + + if err := json.NewEncoder(w).Encode(data); err != nil { + // This error could happen if the struct has unmarshallable types (e.g., channels). + log.Printf("Error marshalling JSON for response: %v", err) + // The header might already be sent, so we can't send a new http.Error. + // We just log the error. + return nil + } + + // Marshal and send the JSON data + //json.NewEncoder(w).Encode(responseData) + + //if err := writeJSON(fixture, w); err != nil { + // return http.InternalError(err.Error()) + //} + return nil // A nil return indicates success. } diff --git a/handler/response/profile.go b/handler/response/profile.go new file mode 100644 index 00000000..0f18e6ab --- /dev/null +++ b/handler/response/profile.go @@ -0,0 +1,14 @@ +package response + +type ProfileResponse struct { + Version string `json:"version"` + Data ProfileData `json:"data"` +} + +type ProfileData struct { + Nickname string `json:"nickname"` + Handle string `json:"handle"` + Name string `json:"name"` + Email string `json:"email"` + Profession string `json:"profession"` +} diff --git a/handler/version/version.go b/handler/version/version.go new file mode 100644 index 00000000..560df8af --- /dev/null +++ b/handler/version/version.go @@ -0,0 +1,28 @@ +package version + +// Section represents a specific section of a user's profile or portfolio. +type Section string + +const ( + ProfileSection Section = "profile" + ExperienceSection Section = "experience" + ProjectsSection Section = "projects" + SocialSection Section = "social" + TalksSection Section = "talks" +) + +func getSectionVersion(section Section) string { + versionMap := map[Section]string{ + ProfileSection: "v1.2.0", + ExperienceSection: "v1.1.0", + ProjectsSection: "v2.0.1", + SocialSection: "v1.0.0", + TalksSection: "v1.3.2", + } + + version, ok := versionMap[section] + if !ok { + return "unknown" + } + return version +} From 40baa0da83c7aa88030b4875ac45a33e89a5d642 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Wed, 25 Jun 2025 17:05:00 +0800 Subject: [PATCH 02/17] remove this --- .env.example | 2 +- handler/version/version.go | 28 ---------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 handler/version/version.go diff --git a/.env.example b/.env.example index 860ca254..375dc374 100644 --- a/.env.example +++ b/.env.example @@ -27,5 +27,5 @@ EN_DB_BIN_DIR="" # --- This flag is only needed/used for docker purposes. # The full db url value is only used for docker purposes given the application works in the -GORM DSN- # Example: postgresql://gocanto-user:gocanto-password@postgres:5432/oullin_db?sslmode=require -ENV_DB_URL="postgresql://gocanto-user:gocanto-password@postgres:5432/oullin_db?sslmode=require" +ENV_DB_URL="" diff --git a/handler/version/version.go b/handler/version/version.go deleted file mode 100644 index 560df8af..00000000 --- a/handler/version/version.go +++ /dev/null @@ -1,28 +0,0 @@ -package version - -// Section represents a specific section of a user's profile or portfolio. -type Section string - -const ( - ProfileSection Section = "profile" - ExperienceSection Section = "experience" - ProjectsSection Section = "projects" - SocialSection Section = "social" - TalksSection Section = "talks" -) - -func getSectionVersion(section Section) string { - versionMap := map[Section]string{ - ProfileSection: "v1.2.0", - ExperienceSection: "v1.1.0", - ProjectsSection: "v2.0.1", - SocialSection: "v1.0.0", - TalksSection: "v1.3.2", - } - - version, ok := versionMap[section] - if !ok { - return "unknown" - } - return version -} From c4ec9e4a96708f896f8bf5ec2bbacaf00fce1697 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Wed, 25 Jun 2025 17:09:52 +0800 Subject: [PATCH 03/17] add etag header --- handler/profile.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/handler/profile.go b/handler/profile.go index b101797e..d7ce189b 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -2,6 +2,7 @@ package handler import ( "encoding/json" + "fmt" "github.com/oullin/handler/response" "github.com/oullin/pkg/http" "log" @@ -34,7 +35,22 @@ func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) * return http.InternalError(err.Error()) } + version := data.Version + etag := fmt.Sprintf(`"%s"`, version) + + if match := r.Header.Get("If-None-Match"); match != "" { + if match == etag { + // If the ETags match, the client's version is fresh. + // Send back a 304 Not Modified status and an empty body. + w.WriteHeader(baseHttp.StatusNotModified) + + return nil + } + } + + w.Header().Set("Cache-Control", "public, max-age=3600") w.Header().Set("Content-Type", "application/json") + w.Header().Set("ETag", etag) w.WriteHeader(baseHttp.StatusOK) if err := json.NewEncoder(w).Encode(data); err != nil { From e9535a5003f45ab51e09ded822b8be3096ccdd43 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Wed, 25 Jun 2025 17:35:49 +0800 Subject: [PATCH 04/17] wip --- handler/profile.go | 1 + pkg/handler.go | 41 ----------------- pkg/response/response.go | 99 +++++++++++++--------------------------- 3 files changed, 33 insertions(+), 108 deletions(-) delete mode 100644 pkg/handler.go diff --git a/handler/profile.go b/handler/profile.go index d7ce189b..0c1c53e4 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -49,6 +49,7 @@ func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) * } w.Header().Set("Cache-Control", "public, max-age=3600") + w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("Content-Type", "application/json") w.Header().Set("ETag", etag) w.WriteHeader(baseHttp.StatusOK) diff --git a/pkg/handler.go b/pkg/handler.go deleted file mode 100644 index dca7eac5..00000000 --- a/pkg/handler.go +++ /dev/null @@ -1,41 +0,0 @@ -package pkg - -import ( - "encoding/json" - "github.com/oullin/pkg/response" - "log/slog" - "net/http" -) - -type BaseHandler func(w http.ResponseWriter, r *http.Request) *response.Response - -func CreateHandle(callback BaseHandler) http.HandlerFunc { - return func(writer http.ResponseWriter, request *http.Request) { - if err := callback(writer, request); err != nil { - err.Respond(writer) - return // Stop processing after error response. - } - - // If the callback returns nil, it means success and the handler. - // The caller itself is responsible for writing the success response. - } -} - -func SendJSON(writer http.ResponseWriter, statusCode int, data any) *response.Response { - writer.Header().Set("Content-Type", "application/json; charset=utf-8") - writer.WriteHeader(statusCode) - - if data == nil { - // Handle cases where no data needs to be sent (e.g., 204 No Content) - // Although usually, 204 doesn't have a body or Content-Type. - // This check prevents json.NewEncoder from writing "null". - return nil - } - - if err := json.NewEncoder(writer).Encode(data); err != nil { - slog.Error("Error encoding success response", "error", err) - return response.InternalServerError("Failed to encode response", err) - } - - return nil // Signal success -} diff --git a/pkg/response/response.go b/pkg/response/response.go index 7b75bb79..dccf61d1 100644 --- a/pkg/response/response.go +++ b/pkg/response/response.go @@ -3,90 +3,55 @@ package response import ( "encoding/json" "fmt" - "log/slog" - "net/http" + baseHttp "net/http" + "strings" ) type Response struct { - Code int - Message string - Err error - ValidationErrors map[string]any + version string + etag string + writer baseHttp.ResponseWriter + request *baseHttp.Request } -func MakeResponse(code int, message string, err error) *Response { - return &Response{ - Code: code, - Message: message, - Err: err, - ValidationErrors: make(map[string]any), - } -} +func MakeFrom(version string, w baseHttp.ResponseWriter, r *baseHttp.Request) Response { + v := strings.TrimSpace(version) -func BadRequest(message string, err error) *Response { - return MakeResponse(http.StatusBadRequest, message, err) + return Response{ + version: v, + writer: w, + request: r, + etag: fmt.Sprintf(`"%s"`, v), + } } -func InternalServerError(message string, err error) *Response { - return MakeResponse(http.StatusInternalServerError, message, err) +func (r *Response) Encode(payload any) error { + return json.NewEncoder(r.writer).Encode(payload) } -func Forbidden(message string, validationErrors map[string]any, err error) *Response { - return &Response{ - Code: http.StatusForbidden, - Message: message, - Err: err, - ValidationErrors: validationErrors, - } -} +func (r *Response) HasCache() bool { + request := r.request -func Unauthorized(message string, err error) *Response { - return &Response{ - Code: http.StatusUnauthorized, - Message: message, - Err: err, - ValidationErrors: make(map[string]any), - } -} + match := strings.TrimSpace( + request.Header.Get("If-None-Match"), + ) -func Unprocessable(message string, err error) *Response { - return &Response{ - Code: http.StatusUnprocessableEntity, - Message: message, - Err: err, - ValidationErrors: make(map[string]any), - } + return match == r.etag } -func (e *Response) Error() string { - if e.Err != nil { - return fmt.Sprintf("%s: %v", e.Message, e.Err) - } +func (r *Response) SetHeaders() { + w := r.writer - return e.Message + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Content-Type", "application/json") + w.Header().Set("ETag", r.etag) } -func (e *Response) Unwrap() error { - return e.Err +func (r *Response) RespondWithNotModified() { + r.writer.WriteHeader(baseHttp.StatusNotModified) } -func (e *Response) Respond(w http.ResponseWriter) { - slog.Error("HTTP Error", "status", e.Code, "message", e.Message, "error", e.Err, "validation_errors", e.ValidationErrors) - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Header().Set("X-Content-Type-Options", "nosniff") // Basic security header - w.WriteHeader(e.Code) - - payload := map[string]any{ - "message": e.Message, - } - - if len(e.ValidationErrors) > 0 { - payload["errors"] = e.ValidationErrors - } - - if err := json.NewEncoder(w).Encode(payload); err != nil { - slog.Error("Error encoding error response", "encode_error", err, "original_error", e) - _, _ = fmt.Fprintf(w, `{"message":"Error generating error response"}`) - } +func (r *Response) RespondOk() { + r.writer.WriteHeader(baseHttp.StatusOK) } From f5bf37589893294a33092a64e5c3e605d03084ff Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 10:27:21 +0800 Subject: [PATCH 05/17] extract response type --- handler/{response.go => json.go} | 0 handler/{response => payload}/profile.go | 2 +- handler/profile.go | 46 ++++----------- pkg/http/message.go | 13 ---- pkg/http/response.go | 73 +++++++++++++++++++++++ pkg/request/request.go | 75 ------------------------ pkg/response/response.go | 57 ------------------ 7 files changed, 86 insertions(+), 180 deletions(-) rename handler/{response.go => json.go} (100%) rename handler/{response => payload}/profile.go (94%) delete mode 100644 pkg/http/message.go create mode 100644 pkg/http/response.go delete mode 100644 pkg/request/request.go delete mode 100644 pkg/response/response.go diff --git a/handler/response.go b/handler/json.go similarity index 100% rename from handler/response.go rename to handler/json.go diff --git a/handler/response/profile.go b/handler/payload/profile.go similarity index 94% rename from handler/response/profile.go rename to handler/payload/profile.go index 0f18e6ab..e1eb263d 100644 --- a/handler/response/profile.go +++ b/handler/payload/profile.go @@ -1,4 +1,4 @@ -package response +package payload type ProfileResponse struct { Version string `json:"version"` diff --git a/handler/profile.go b/handler/profile.go index 0c1c53e4..32d30772 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -2,27 +2,25 @@ package handler import ( "encoding/json" - "fmt" - "github.com/oullin/handler/response" + "github.com/oullin/handler/payload" "github.com/oullin/pkg/http" - "log" "log/slog" baseHttp "net/http" "os" ) type ProfileHandler struct { - content string + fixture string } -func MakeProfileHandler(fixture string) ProfileHandler { +func MakeProfileHandler(file string) ProfileHandler { return ProfileHandler{ - content: fixture, + fixture: file, } } func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - fixture, err := os.ReadFile(h.content) + fixture, err := os.ReadFile(h.fixture) if err != nil { slog.Error("Error reading projects file", "error", err) @@ -30,44 +28,24 @@ func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) * return http.InternalError("could not read profile data") } - var data response.ProfileResponse + var data payload.ProfileResponse if err := json.Unmarshal(fixture, &data); err != nil { return http.InternalError(err.Error()) } - version := data.Version - etag := fmt.Sprintf(`"%s"`, version) + resp := http.MakeResponseFrom(data.Version, w, r) - if match := r.Header.Get("If-None-Match"); match != "" { - if match == etag { - // If the ETags match, the client's version is fresh. - // Send back a 304 Not Modified status and an empty body. - w.WriteHeader(baseHttp.StatusNotModified) + if resp.HasCache() { + resp.RespondWithNotModified() - return nil - } + return nil } - w.Header().Set("Cache-Control", "public, max-age=3600") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.Header().Set("Content-Type", "application/json") - w.Header().Set("ETag", etag) - w.WriteHeader(baseHttp.StatusOK) + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for response", "error", err) - if err := json.NewEncoder(w).Encode(data); err != nil { - // This error could happen if the struct has unmarshallable types (e.g., channels). - log.Printf("Error marshalling JSON for response: %v", err) - // The header might already be sent, so we can't send a new http.Error. - // We just log the error. return nil } - // Marshal and send the JSON data - //json.NewEncoder(w).Encode(responseData) - - //if err := writeJSON(fixture, w); err != nil { - // return http.InternalError(err.Error()) - //} - return nil // A nil return indicates success. } diff --git a/pkg/http/message.go b/pkg/http/message.go deleted file mode 100644 index 85280ab8..00000000 --- a/pkg/http/message.go +++ /dev/null @@ -1,13 +0,0 @@ -package http - -import ( - "fmt" - baseHttp "net/http" -) - -func InternalError(msg string) *ApiError { - return &ApiError{ - Message: fmt.Sprintf("Internal Server Error: %s", msg), - Status: baseHttp.StatusInternalServerError, - } -} diff --git a/pkg/http/response.go b/pkg/http/response.go new file mode 100644 index 00000000..0eb8129d --- /dev/null +++ b/pkg/http/response.go @@ -0,0 +1,73 @@ +package http + +import ( + "encoding/json" + "fmt" + baseHttp "net/http" + "strings" +) + +type Response struct { + etag string + cacheControl string + writer baseHttp.ResponseWriter + request *baseHttp.Request + headers func(w baseHttp.ResponseWriter) +} + +func MakeResponseFrom(salt string, writer baseHttp.ResponseWriter, request *baseHttp.Request) *Response { + etag := fmt.Sprintf( + `"%s"`, + strings.TrimSpace(salt), + ) + + cacheControl := "public, max-age=3600" + + return &Response{ + writer: writer, + request: request, + etag: strings.TrimSpace(etag), + cacheControl: cacheControl, + headers: func(w baseHttp.ResponseWriter) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Cache-Control", cacheControl) + w.Header().Set("ETag", etag) + }, + } +} + +func (r *Response) WithHeaders(callback func(w baseHttp.ResponseWriter)) { + callback(r.writer) +} + +func (r *Response) RespondOk(payload any) error { + w := r.writer + headers := r.headers + + headers(w) + w.WriteHeader(baseHttp.StatusOK) + + return json.NewEncoder(r.writer).Encode(payload) +} + +func (r *Response) HasCache() bool { + request := r.request + + match := strings.TrimSpace( + request.Header.Get("If-None-Match"), + ) + + return match == r.etag +} + +func (r *Response) RespondWithNotModified() { + r.writer.WriteHeader(baseHttp.StatusNotModified) +} + +func InternalError(msg string) *ApiError { + return &ApiError{ + Message: fmt.Sprintf("Internal Server Error: %s", msg), + Status: baseHttp.StatusInternalServerError, + } +} diff --git a/pkg/request/request.go b/pkg/request/request.go deleted file mode 100644 index 28c20fb3..00000000 --- a/pkg/request/request.go +++ /dev/null @@ -1,75 +0,0 @@ -package request - -import ( - "errors" - "fmt" - "github.com/oullin/pkg/media" - "io" - "log/slog" - "mime/multipart" - "net/http" -) - -type Request struct { - baseRequest *http.Request - isMultipart bool - multipartReader *multipart.Reader - multiPartRawData media.MultipartFormInterface -} - -func MakeMultipartRequest[T media.MultipartFormInterface](r *http.Request, rawData T) (*Request, error) { - reader, err := r.MultipartReader() - - if err != nil { - return nil, errors.New("the isMultipart form reader is invalid") - } - - return &Request{ - baseRequest: r, - isMultipart: true, - multiPartRawData: rawData, - multipartReader: reader, - }, nil -} - -func (req *Request) Close(message *string) { - m := "Issue closing the request body" - - if message == nil { - message = &m - } - - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - slog.Error(m, err) - } - }(req.baseRequest.Body) -} - -func (req *Request) ParseRawData(callback func(reader *multipart.Reader, data media.MultipartFormInterface) error) error { - fmt.Println(fmt.Sprintf("dd: %+v", req)) - if req.multipartReader == nil { - return errors.New("1) invalid isMultipart form") - } - - if req.multiPartRawData == nil { - return errors.New("2) invalid isMultipart form request") - } - - result := callback(req.multipartReader, req.multiPartRawData) - - if result != nil { - return errors.New("3) invalid isMultipart form parsing: " + result.Error()) - } - - return nil -} - -func (req *Request) GetFile() []byte { - return req.multiPartRawData.GetFile() -} - -func (req *Request) GetHeaderName() string { - return req.multiPartRawData.GetHeaderName() -} diff --git a/pkg/response/response.go b/pkg/response/response.go deleted file mode 100644 index dccf61d1..00000000 --- a/pkg/response/response.go +++ /dev/null @@ -1,57 +0,0 @@ -package response - -import ( - "encoding/json" - "fmt" - baseHttp "net/http" - "strings" -) - -type Response struct { - version string - etag string - writer baseHttp.ResponseWriter - request *baseHttp.Request -} - -func MakeFrom(version string, w baseHttp.ResponseWriter, r *baseHttp.Request) Response { - v := strings.TrimSpace(version) - - return Response{ - version: v, - writer: w, - request: r, - etag: fmt.Sprintf(`"%s"`, v), - } -} - -func (r *Response) Encode(payload any) error { - return json.NewEncoder(r.writer).Encode(payload) -} - -func (r *Response) HasCache() bool { - request := r.request - - match := strings.TrimSpace( - request.Header.Get("If-None-Match"), - ) - - return match == r.etag -} - -func (r *Response) SetHeaders() { - w := r.writer - - w.Header().Set("Cache-Control", "public, max-age=3600") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.Header().Set("Content-Type", "application/json") - w.Header().Set("ETag", r.etag) -} - -func (r *Response) RespondWithNotModified() { - r.writer.WriteHeader(baseHttp.StatusNotModified) -} - -func (r *Response) RespondOk() { - r.writer.WriteHeader(baseHttp.StatusOK) -} From 6648ad24f8e3a4b1e25e7903bfdf16b5277db19f Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 11:24:11 +0800 Subject: [PATCH 06/17] add json parser --- handler/profile.go | 10 ++-------- pkg/parser.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 pkg/parser.go diff --git a/handler/profile.go b/handler/profile.go index 32d30772..f6b10fb5 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -1,12 +1,11 @@ package handler import ( - "encoding/json" "github.com/oullin/handler/payload" + "github.com/oullin/pkg" "github.com/oullin/pkg/http" "log/slog" baseHttp "net/http" - "os" ) type ProfileHandler struct { @@ -20,7 +19,7 @@ func MakeProfileHandler(file string) ProfileHandler { } func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - fixture, err := os.ReadFile(h.fixture) + data, err := pkg.ParseJsonFile[payload.ProfileResponse](h.fixture) if err != nil { slog.Error("Error reading projects file", "error", err) @@ -28,11 +27,6 @@ func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) * return http.InternalError("could not read profile data") } - var data payload.ProfileResponse - if err := json.Unmarshal(fixture, &data); err != nil { - return http.InternalError(err.Error()) - } - resp := http.MakeResponseFrom(data.Version, w, r) if resp.HasCache() { diff --git a/pkg/parser.go b/pkg/parser.go new file mode 100644 index 00000000..0c8a58bd --- /dev/null +++ b/pkg/parser.go @@ -0,0 +1,29 @@ +package pkg + +import ( + "encoding/json" + "fmt" + "os" +) + +func ParseJsonFile[T any](filePath string) (T, error) { + // We must declare a variable of type T to hold the result. + // This will also be the zero value of T if an error occurs. + var result T + + // Read the entire file into a byte slice. + content, err := os.ReadFile(filePath) + if err != nil { + // Wrap the error with context for clearer debugging. + return result, fmt.Errorf("could not read file %s: %w", filePath, err) + } + + // Unmarshal the JSON data into the 'result' variable. + // We pass a pointer to 'result' so json.Unmarshal can populate it. + if err := json.Unmarshal(content, &result); err != nil { + return result, fmt.Errorf("could not unmarshal json from %s: %w", filePath, err) + } + + // If successful, return the populated struct and a nil error. + return result, nil +} From 2fd58dd0bfc5e03f214fb95f236b8d9f616aee45 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 11:30:24 +0800 Subject: [PATCH 07/17] doc --- handler/profile.go | 9 +++++---- pkg/parser.go | 38 +++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/handler/profile.go b/handler/profile.go index f6b10fb5..316065f6 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -9,17 +9,18 @@ import ( ) type ProfileHandler struct { - fixture string + //The file containing the given handler endpoint information. + filePah string } -func MakeProfileHandler(file string) ProfileHandler { +func MakeProfileHandler(filePah string) ProfileHandler { return ProfileHandler{ - fixture: file, + filePah: filePah, } } func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ProfileResponse](h.fixture) + data, err := pkg.ParseJsonFile[payload.ProfileResponse](h.filePah) if err != nil { slog.Error("Error reading projects file", "error", err) diff --git a/pkg/parser.go b/pkg/parser.go index 0c8a58bd..ddfed34b 100644 --- a/pkg/parser.go +++ b/pkg/parser.go @@ -1,29 +1,29 @@ package pkg import ( - "encoding/json" - "fmt" - "os" + "encoding/json" + "fmt" + "os" ) func ParseJsonFile[T any](filePath string) (T, error) { - // We must declare a variable of type T to hold the result. - // This will also be the zero value of T if an error occurs. - var result T + // We must declare a variable of type T to hold the result. + // This will also be the zero value of T if an error occurs. + var result T - // Read the entire file into a byte slice. - content, err := os.ReadFile(filePath) - if err != nil { - // Wrap the error with context for clearer debugging. - return result, fmt.Errorf("could not read file %s: %w", filePath, err) - } + // Read the entire file into a byte slice. + content, err := os.ReadFile(filePath) + if err != nil { + // Wrap the error with context for clearer debugging. + return result, fmt.Errorf("could not read file %s: %w", filePath, err) + } - // Unmarshal the JSON data into the 'result' variable. - // We pass a pointer to 'result' so json.Unmarshal can populate it. - if err := json.Unmarshal(content, &result); err != nil { - return result, fmt.Errorf("could not unmarshal json from %s: %w", filePath, err) - } + // Unmarshal the JSON data into the 'result' variable. + // We pass a pointer to 'result' so json.Unmarshal can populate it. + if err := json.Unmarshal(content, &result); err != nil { + return result, fmt.Errorf("could not unmarshal json from %s: %w", filePath, err) + } - // If successful, return the populated struct and a nil error. - return result, nil + // If successful, return the populated struct and a nil error. + return result, nil } From aec2d7ad9342a7f10900fc5a452fdbc5512e87c2 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 11:42:55 +0800 Subject: [PATCH 08/17] move experience to the new abstraction --- boost/router.go | 2 +- handler/experience.go | 27 +++++++++++++++++++-------- handler/payload/experience.go | 20 ++++++++++++++++++++ storage/fixture/experience.json | 2 +- 4 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 handler/payload/experience.go diff --git a/boost/router.go b/boost/router.go index f0f0d6bf..f5a02e8a 100644 --- a/boost/router.go +++ b/boost/router.go @@ -39,7 +39,7 @@ func (r *Router) Profile() { } func (r *Router) Experience() { - abstract := handler.MakeExperienceHandler() + abstract := handler.MakeExperienceHandler("./storage/fixture/experience.json") resolver := r.PipelineFor( abstract.Handle, diff --git a/handler/experience.go b/handler/experience.go index 028f893d..0663c0fb 100644 --- a/handler/experience.go +++ b/handler/experience.go @@ -1,33 +1,44 @@ package handler import ( + "github.com/oullin/handler/payload" + "github.com/oullin/pkg" "github.com/oullin/pkg/http" "log/slog" baseHttp "net/http" - "os" ) type ExperienceHandler struct { - content string + filePah string } -func MakeExperienceHandler() ExperienceHandler { +func MakeExperienceHandler(filePah string) ExperienceHandler { return ExperienceHandler{ - content: "./storage/fixture/experience.json", + filePah: filePah, } } func (h ExperienceHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - fixture, err := os.ReadFile(h.content) + data, err := pkg.ParseJsonFile[payload.ExperienceResponse](h.filePah) if err != nil { slog.Error("Error reading projects file", "error", err) - return http.InternalError("could not read experience data") + return http.InternalError("could not read profile data") } - if err := writeJSON(fixture, w); err != nil { - return http.InternalError(err.Error()) + resp := http.MakeResponseFrom(data.Version, w, r) + + if resp.HasCache() { + resp.RespondWithNotModified() + + return nil + } + + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for response", "error", err) + + return nil } return nil // A nil return indicates success. diff --git a/handler/payload/experience.go b/handler/payload/experience.go new file mode 100644 index 00000000..87bffd4f --- /dev/null +++ b/handler/payload/experience.go @@ -0,0 +1,20 @@ +package payload + +type ExperienceResponse struct { + Version string `json:"version"` + Data []ExperienceData `json:"data"` +} + +type ExperienceData struct { + UUID string `json:"uuid"` + Company string `json:"company"` + EmploymentType string `json:"employment_type"` + LocationType string `json:"location_type"` + Position string `json:"position"` + StartDate string `json:"start_date"` + EndDate string `json:"end_date"` + Summary string `json:"summary"` + Country string `json:"country"` + City string `json:"city"` + Skills string `json:"skills"` +} diff --git a/storage/fixture/experience.json b/storage/fixture/experience.json index a4cfb8ff..050a2643 100644 --- a/storage/fixture/experience.json +++ b/storage/fixture/experience.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "date": [ + "data": [ { "uuid": "c17a68bc-8832-4d44-b2ed-f9587cf14cd1", "company": "Perx Technologies", From 2eefc5360ddb591e4b34b63cac29e2a5f2e80090 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 11:50:38 +0800 Subject: [PATCH 09/17] move projects to the new abstraction --- boost/router.go | 2 +- handler/payload/project.go | 16 +++++++++++++ handler/profile.go | 1 - handler/projects.go | 47 +++++++++++++++++++++++--------------- 4 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 handler/payload/project.go diff --git a/boost/router.go b/boost/router.go index f5a02e8a..770e81d8 100644 --- a/boost/router.go +++ b/boost/router.go @@ -49,7 +49,7 @@ func (r *Router) Experience() { } func (r *Router) Projects() { - abstract := handler.MakeProjectsHandler() + abstract := handler.MakeProjectsHandler("./storage/fixture/projects.json") resolver := r.PipelineFor( abstract.Handle, diff --git a/handler/payload/project.go b/handler/payload/project.go new file mode 100644 index 00000000..6f4b57b6 --- /dev/null +++ b/handler/payload/project.go @@ -0,0 +1,16 @@ +package payload + +type ProjectResponse struct { + Version string `json:"version"` + Data []ProjectData `json:"data"` +} + +type ProjectData struct { + UUID string `json:"uuid"` + Language string `json:"language"` + Title string `json:"title"` + Excerpt string `json:"excerpt"` + URL string `json:"url"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} diff --git a/handler/profile.go b/handler/profile.go index 316065f6..47b3305a 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -9,7 +9,6 @@ import ( ) type ProfileHandler struct { - //The file containing the given handler endpoint information. filePah string } diff --git a/handler/projects.go b/handler/projects.go index 2268d86c..939a0b85 100644 --- a/handler/projects.go +++ b/handler/projects.go @@ -1,34 +1,45 @@ package handler import ( - "github.com/oullin/pkg/http" - "log/slog" - baseHttp "net/http" - "os" + "github.com/oullin/handler/payload" + "github.com/oullin/pkg" + "github.com/oullin/pkg/http" + "log/slog" + baseHttp "net/http" ) type ProjectsHandler struct { - content string + filePah string } -func MakeProjectsHandler() ProjectsHandler { - return ProjectsHandler{ - content: "./storage/fixture/projects.json", - } +func MakeProjectsHandler(filePah string) ProjectsHandler { + return ProjectsHandler{ + filePah: filePah, + } } func (h ProjectsHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - fixture, err := os.ReadFile(h.content) + data, err := pkg.ParseJsonFile[payload.ProjectResponse](h.filePah) - if err != nil { - slog.Error("Error reading projects file", "error", err) + if err != nil { + slog.Error("Error reading projects file", "error", err) - return http.InternalError("could not read projects data") - } + return http.InternalError("could not read profile data") + } - if err := writeJSON(fixture, w); err != nil { - return http.InternalError(err.Error()) - } + resp := http.MakeResponseFrom(data.Version, w, r) - return nil // A nil return indicates success. + if resp.HasCache() { + resp.RespondWithNotModified() + + return nil + } + + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for response", "error", err) + + return nil + } + + return nil // A nil return indicates success. } From bab2f8afc5a4a2e4dac357c2652e6090da233221 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 11:54:35 +0800 Subject: [PATCH 10/17] move social to the new abstraction --- boost/router.go | 2 +- handler/payload/social.go | 14 ++++++++++++ handler/projects.go | 48 +++++++++++++++++++-------------------- handler/social.go | 27 +++++++++++++++------- 4 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 handler/payload/social.go diff --git a/boost/router.go b/boost/router.go index 770e81d8..1823ad8e 100644 --- a/boost/router.go +++ b/boost/router.go @@ -59,7 +59,7 @@ func (r *Router) Projects() { } func (r *Router) Social() { - abstract := handler.MakeSocialHandler() + abstract := handler.MakeSocialHandler("./storage/fixture/social.json") resolver := r.PipelineFor( abstract.Handle, diff --git a/handler/payload/social.go b/handler/payload/social.go new file mode 100644 index 00000000..8623a9fe --- /dev/null +++ b/handler/payload/social.go @@ -0,0 +1,14 @@ +package payload + +type SocialResponse struct { + Version string `json:"version"` + Data []SocialData `json:"data"` +} + +type SocialData struct { + UUID string `json:"uuid"` + Handle string `json:"handle"` + URL string `json:"url"` + Description string `json:"description"` + Name string `json:"name"` +} diff --git a/handler/projects.go b/handler/projects.go index 939a0b85..e78aaeba 100644 --- a/handler/projects.go +++ b/handler/projects.go @@ -1,45 +1,45 @@ package handler import ( - "github.com/oullin/handler/payload" - "github.com/oullin/pkg" - "github.com/oullin/pkg/http" - "log/slog" - baseHttp "net/http" + "github.com/oullin/handler/payload" + "github.com/oullin/pkg" + "github.com/oullin/pkg/http" + "log/slog" + baseHttp "net/http" ) type ProjectsHandler struct { - filePah string + filePah string } func MakeProjectsHandler(filePah string) ProjectsHandler { - return ProjectsHandler{ - filePah: filePah, - } + return ProjectsHandler{ + filePah: filePah, + } } func (h ProjectsHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ProjectResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.ProjectResponse](h.filePah) - if err != nil { - slog.Error("Error reading projects file", "error", err) + if err != nil { + slog.Error("Error reading projects file", "error", err) - return http.InternalError("could not read profile data") - } + return http.InternalError("could not read profile data") + } - resp := http.MakeResponseFrom(data.Version, w, r) + resp := http.MakeResponseFrom(data.Version, w, r) - if resp.HasCache() { - resp.RespondWithNotModified() + if resp.HasCache() { + resp.RespondWithNotModified() - return nil - } + return nil + } - if err := resp.RespondOk(data); err != nil { - slog.Error("Error marshaling JSON for response", "error", err) + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for response", "error", err) - return nil - } + return nil + } - return nil // A nil return indicates success. + return nil // A nil return indicates success. } diff --git a/handler/social.go b/handler/social.go index 89131176..dfe43b08 100644 --- a/handler/social.go +++ b/handler/social.go @@ -1,33 +1,44 @@ package handler import ( + "github.com/oullin/handler/payload" + "github.com/oullin/pkg" "github.com/oullin/pkg/http" "log/slog" baseHttp "net/http" - "os" ) type SocialHandler struct { - content string + filePah string } -func MakeSocialHandler() SocialHandler { +func MakeSocialHandler(filePah string) SocialHandler { return SocialHandler{ - content: "./storage/fixture/social.json", + filePah: filePah, } } func (h SocialHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - fixture, err := os.ReadFile(h.content) + data, err := pkg.ParseJsonFile[payload.SocialResponse](h.filePah) if err != nil { slog.Error("Error reading projects file", "error", err) - return http.InternalError("could not read social data") + return http.InternalError("could not read profile data") } - if err := writeJSON(fixture, w); err != nil { - return http.InternalError(err.Error()) + resp := http.MakeResponseFrom(data.Version, w, r) + + if resp.HasCache() { + resp.RespondWithNotModified() + + return nil + } + + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for response", "error", err) + + return nil } return nil // A nil return indicates success. From 5d40fc0d2d6e203b6a581ad99e9d7636e1dae766 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 11:58:12 +0800 Subject: [PATCH 11/17] move talks to the new abstraction --- boost/router.go | 2 +- handler/payload/talk.go | 17 +++++++++++++++++ handler/talks.go | 27 +++++++++++++++++++-------- 3 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 handler/payload/talk.go diff --git a/boost/router.go b/boost/router.go index 1823ad8e..9dd4c178 100644 --- a/boost/router.go +++ b/boost/router.go @@ -69,7 +69,7 @@ func (r *Router) Social() { } func (r *Router) Talks() { - abstract := handler.MakeTalks() + abstract := handler.MakeTalks("./storage/fixture/talks.json") resolver := r.PipelineFor( abstract.Handle, diff --git a/handler/payload/talk.go b/handler/payload/talk.go new file mode 100644 index 00000000..655340a5 --- /dev/null +++ b/handler/payload/talk.go @@ -0,0 +1,17 @@ +package payload + +type TalksResponse struct { + Version string `json:"version"` + Data []TalkData `json:"data"` +} + +type TalkData struct { + UUID string `json:"uuid"` + Title string `json:"title"` + Subject string `json:"subject"` + Location string `json:"location"` + URL string `json:"url"` + Photo string `json:"photo"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} diff --git a/handler/talks.go b/handler/talks.go index b71dd0b6..62474e4d 100644 --- a/handler/talks.go +++ b/handler/talks.go @@ -1,33 +1,44 @@ package handler import ( + "github.com/oullin/handler/payload" + "github.com/oullin/pkg" "github.com/oullin/pkg/http" "log/slog" baseHttp "net/http" - "os" ) type Talks struct { - content string + filePah string } -func MakeTalks() Talks { +func MakeTalks(filePah string) Talks { return Talks{ - content: "./storage/fixture/talks.json", + filePah: filePah, } } func (h Talks) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - fixture, err := os.ReadFile(h.content) + data, err := pkg.ParseJsonFile[payload.TalksResponse](h.filePah) if err != nil { slog.Error("Error reading projects file", "error", err) - return http.InternalError("could not read talks data") + return http.InternalError("could not read profile data") } - if err := writeJSON(fixture, w); err != nil { - return http.InternalError(err.Error()) + resp := http.MakeResponseFrom(data.Version, w, r) + + if resp.HasCache() { + resp.RespondWithNotModified() + + return nil + } + + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for response", "error", err) + + return nil } return nil // A nil return indicates success. From 35929f5e878f95a606687fc9f3f3c453ca853cfb Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 12:02:27 +0800 Subject: [PATCH 12/17] format --- handler/json.go | 17 ----------------- handler/payload/{project.go => projects.go} | 8 ++++---- handler/payload/{talk.go => talks.go} | 6 +++--- handler/projects.go | 2 +- handler/talks.go | 8 ++++---- 5 files changed, 12 insertions(+), 29 deletions(-) delete mode 100644 handler/json.go rename handler/payload/{project.go => projects.go} (65%) rename handler/payload/{talk.go => talks.go} (77%) diff --git a/handler/json.go b/handler/json.go deleted file mode 100644 index 55f7860e..00000000 --- a/handler/json.go +++ /dev/null @@ -1,17 +0,0 @@ -package handler - -import ( - "fmt" - baseHttp "net/http" -) - -func writeJSON(content []byte, w baseHttp.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(baseHttp.StatusOK) - - if _, err := w.Write(content); err != nil { - return fmt.Errorf("error writing response: %v", err) - } - - return nil -} diff --git a/handler/payload/project.go b/handler/payload/projects.go similarity index 65% rename from handler/payload/project.go rename to handler/payload/projects.go index 6f4b57b6..6b4ec530 100644 --- a/handler/payload/project.go +++ b/handler/payload/projects.go @@ -1,11 +1,11 @@ package payload -type ProjectResponse struct { - Version string `json:"version"` - Data []ProjectData `json:"data"` +type ProjectsResponse struct { + Version string `json:"version"` + Data []ProjectsData `json:"data"` } -type ProjectData struct { +type ProjectsData struct { UUID string `json:"uuid"` Language string `json:"language"` Title string `json:"title"` diff --git a/handler/payload/talk.go b/handler/payload/talks.go similarity index 77% rename from handler/payload/talk.go rename to handler/payload/talks.go index 655340a5..eeca08b9 100644 --- a/handler/payload/talk.go +++ b/handler/payload/talks.go @@ -1,11 +1,11 @@ package payload type TalksResponse struct { - Version string `json:"version"` - Data []TalkData `json:"data"` + Version string `json:"version"` + Data []TalksData `json:"data"` } -type TalkData struct { +type TalksData struct { UUID string `json:"uuid"` Title string `json:"title"` Subject string `json:"subject"` diff --git a/handler/projects.go b/handler/projects.go index e78aaeba..ccf826d6 100644 --- a/handler/projects.go +++ b/handler/projects.go @@ -19,7 +19,7 @@ func MakeProjectsHandler(filePah string) ProjectsHandler { } func (h ProjectsHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ProjectResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.ProjectsResponse](h.filePah) if err != nil { slog.Error("Error reading projects file", "error", err) diff --git a/handler/talks.go b/handler/talks.go index 62474e4d..1122150d 100644 --- a/handler/talks.go +++ b/handler/talks.go @@ -8,17 +8,17 @@ import ( baseHttp "net/http" ) -type Talks struct { +type TalksHandler struct { filePah string } -func MakeTalks(filePah string) Talks { - return Talks{ +func MakeTalks(filePah string) TalksHandler { + return TalksHandler{ filePah: filePah, } } -func (h Talks) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { +func (h TalksHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { data, err := pkg.ParseJsonFile[payload.TalksResponse](h.filePah) if err != nil { From 6ef1e0a3c95f523711ed171f538470003498c79a Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 12:04:22 +0800 Subject: [PATCH 13/17] format --- boost/router.go | 2 +- handler/talks.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/boost/router.go b/boost/router.go index 9dd4c178..7d695df9 100644 --- a/boost/router.go +++ b/boost/router.go @@ -69,7 +69,7 @@ func (r *Router) Social() { } func (r *Router) Talks() { - abstract := handler.MakeTalks("./storage/fixture/talks.json") + abstract := handler.MakeTalksHandler("./storage/fixture/talks.json") resolver := r.PipelineFor( abstract.Handle, diff --git a/handler/talks.go b/handler/talks.go index 1122150d..1ca36ec6 100644 --- a/handler/talks.go +++ b/handler/talks.go @@ -12,7 +12,7 @@ type TalksHandler struct { filePah string } -func MakeTalks(filePah string) TalksHandler { +func MakeTalksHandler(filePah string) TalksHandler { return TalksHandler{ filePah: filePah, } From 36439dd476405603bb889318cba250e0dad8c368 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 12:07:05 +0800 Subject: [PATCH 14/17] wording --- handler/experience.go | 6 +++--- handler/profile.go | 4 ++-- handler/projects.go | 48 +++++++++++++++++++++---------------------- handler/social.go | 6 +++--- handler/talks.go | 6 +++--- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/handler/experience.go b/handler/experience.go index 0663c0fb..858c0233 100644 --- a/handler/experience.go +++ b/handler/experience.go @@ -22,9 +22,9 @@ func (h ExperienceHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request data, err := pkg.ParseJsonFile[payload.ExperienceResponse](h.filePah) if err != nil { - slog.Error("Error reading projects file", "error", err) + slog.Error("Error reading experience file", "error", err) - return http.InternalError("could not read profile data") + return http.InternalError("could not read experience data") } resp := http.MakeResponseFrom(data.Version, w, r) @@ -36,7 +36,7 @@ func (h ExperienceHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request } if err := resp.RespondOk(data); err != nil { - slog.Error("Error marshaling JSON for response", "error", err) + slog.Error("Error marshaling JSON for experience response", "error", err) return nil } diff --git a/handler/profile.go b/handler/profile.go index 47b3305a..408c9397 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -22,7 +22,7 @@ func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) * data, err := pkg.ParseJsonFile[payload.ProfileResponse](h.filePah) if err != nil { - slog.Error("Error reading projects file", "error", err) + slog.Error("Error reading profile file", "error", err) return http.InternalError("could not read profile data") } @@ -36,7 +36,7 @@ func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) * } if err := resp.RespondOk(data); err != nil { - slog.Error("Error marshaling JSON for response", "error", err) + slog.Error("Error marshaling JSON for profile response", "error", err) return nil } diff --git a/handler/projects.go b/handler/projects.go index ccf826d6..fc0b0697 100644 --- a/handler/projects.go +++ b/handler/projects.go @@ -1,45 +1,45 @@ package handler import ( - "github.com/oullin/handler/payload" - "github.com/oullin/pkg" - "github.com/oullin/pkg/http" - "log/slog" - baseHttp "net/http" + "github.com/oullin/handler/payload" + "github.com/oullin/pkg" + "github.com/oullin/pkg/http" + "log/slog" + baseHttp "net/http" ) type ProjectsHandler struct { - filePah string + filePah string } func MakeProjectsHandler(filePah string) ProjectsHandler { - return ProjectsHandler{ - filePah: filePah, - } + return ProjectsHandler{ + filePah: filePah, + } } func (h ProjectsHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ProjectsResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.ProjectsResponse](h.filePah) - if err != nil { - slog.Error("Error reading projects file", "error", err) + if err != nil { + slog.Error("Error reading projects file", "error", err) - return http.InternalError("could not read profile data") - } + return http.InternalError("could not read projects data") + } - resp := http.MakeResponseFrom(data.Version, w, r) + resp := http.MakeResponseFrom(data.Version, w, r) - if resp.HasCache() { - resp.RespondWithNotModified() + if resp.HasCache() { + resp.RespondWithNotModified() - return nil - } + return nil + } - if err := resp.RespondOk(data); err != nil { - slog.Error("Error marshaling JSON for response", "error", err) + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for projects response", "error", err) - return nil - } + return nil + } - return nil // A nil return indicates success. + return nil // A nil return indicates success. } diff --git a/handler/social.go b/handler/social.go index dfe43b08..3a665983 100644 --- a/handler/social.go +++ b/handler/social.go @@ -22,9 +22,9 @@ func (h SocialHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *h data, err := pkg.ParseJsonFile[payload.SocialResponse](h.filePah) if err != nil { - slog.Error("Error reading projects file", "error", err) + slog.Error("Error reading social file", "error", err) - return http.InternalError("could not read profile data") + return http.InternalError("could not read social data") } resp := http.MakeResponseFrom(data.Version, w, r) @@ -36,7 +36,7 @@ func (h SocialHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *h } if err := resp.RespondOk(data); err != nil { - slog.Error("Error marshaling JSON for response", "error", err) + slog.Error("Error marshaling JSON for social response", "error", err) return nil } diff --git a/handler/talks.go b/handler/talks.go index 1ca36ec6..55e7c659 100644 --- a/handler/talks.go +++ b/handler/talks.go @@ -22,9 +22,9 @@ func (h TalksHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *ht data, err := pkg.ParseJsonFile[payload.TalksResponse](h.filePah) if err != nil { - slog.Error("Error reading projects file", "error", err) + slog.Error("Error reading talks file", "error", err) - return http.InternalError("could not read profile data") + return http.InternalError("could not read talks data") } resp := http.MakeResponseFrom(data.Version, w, r) @@ -36,7 +36,7 @@ func (h TalksHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *ht } if err := resp.RespondOk(data); err != nil { - slog.Error("Error marshaling JSON for response", "error", err) + slog.Error("Error marshaling JSON for talks response", "error", err) return nil } From 184c56b59114228531b475f4c4419e75cbf37023 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 12:07:24 +0800 Subject: [PATCH 15/17] format --- handler/projects.go | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/handler/projects.go b/handler/projects.go index fc0b0697..040b341a 100644 --- a/handler/projects.go +++ b/handler/projects.go @@ -1,45 +1,45 @@ package handler import ( - "github.com/oullin/handler/payload" - "github.com/oullin/pkg" - "github.com/oullin/pkg/http" - "log/slog" - baseHttp "net/http" + "github.com/oullin/handler/payload" + "github.com/oullin/pkg" + "github.com/oullin/pkg/http" + "log/slog" + baseHttp "net/http" ) type ProjectsHandler struct { - filePah string + filePah string } func MakeProjectsHandler(filePah string) ProjectsHandler { - return ProjectsHandler{ - filePah: filePah, - } + return ProjectsHandler{ + filePah: filePah, + } } func (h ProjectsHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ProjectsResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.ProjectsResponse](h.filePah) - if err != nil { - slog.Error("Error reading projects file", "error", err) + if err != nil { + slog.Error("Error reading projects file", "error", err) - return http.InternalError("could not read projects data") - } + return http.InternalError("could not read projects data") + } - resp := http.MakeResponseFrom(data.Version, w, r) + resp := http.MakeResponseFrom(data.Version, w, r) - if resp.HasCache() { - resp.RespondWithNotModified() + if resp.HasCache() { + resp.RespondWithNotModified() - return nil - } + return nil + } - if err := resp.RespondOk(data); err != nil { - slog.Error("Error marshaling JSON for projects response", "error", err) + if err := resp.RespondOk(data); err != nil { + slog.Error("Error marshaling JSON for projects response", "error", err) - return nil - } + return nil + } - return nil // A nil return indicates success. + return nil // A nil return indicates success. } From 521ba34f04374f8daa1bb777d9487cfd837f4f58 Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 12:17:44 +0800 Subject: [PATCH 16/17] fix file name typo --- handler/experience.go | 8 ++++---- handler/profile.go | 8 ++++---- handler/projects.go | 8 ++++---- handler/social.go | 8 ++++---- handler/talks.go | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/handler/experience.go b/handler/experience.go index 858c0233..8b9acc7b 100644 --- a/handler/experience.go +++ b/handler/experience.go @@ -9,17 +9,17 @@ import ( ) type ExperienceHandler struct { - filePah string + filePath string } -func MakeExperienceHandler(filePah string) ExperienceHandler { +func MakeExperienceHandler(filePath string) ExperienceHandler { return ExperienceHandler{ - filePah: filePah, + filePath: filePath, } } func (h ExperienceHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ExperienceResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.ExperienceResponse](h.filePath) if err != nil { slog.Error("Error reading experience file", "error", err) diff --git a/handler/profile.go b/handler/profile.go index 408c9397..bca0bbe9 100644 --- a/handler/profile.go +++ b/handler/profile.go @@ -9,17 +9,17 @@ import ( ) type ProfileHandler struct { - filePah string + filePath string } -func MakeProfileHandler(filePah string) ProfileHandler { +func MakeProfileHandler(filePath string) ProfileHandler { return ProfileHandler{ - filePah: filePah, + filePath: filePath, } } func (h ProfileHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ProfileResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.ProfileResponse](h.filePath) if err != nil { slog.Error("Error reading profile file", "error", err) diff --git a/handler/projects.go b/handler/projects.go index 040b341a..9ea65e5b 100644 --- a/handler/projects.go +++ b/handler/projects.go @@ -9,17 +9,17 @@ import ( ) type ProjectsHandler struct { - filePah string + filePath string } -func MakeProjectsHandler(filePah string) ProjectsHandler { +func MakeProjectsHandler(filePath string) ProjectsHandler { return ProjectsHandler{ - filePah: filePah, + filePath: filePath, } } func (h ProjectsHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.ProjectsResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.ProjectsResponse](h.filePath) if err != nil { slog.Error("Error reading projects file", "error", err) diff --git a/handler/social.go b/handler/social.go index 3a665983..4c9e6d96 100644 --- a/handler/social.go +++ b/handler/social.go @@ -9,17 +9,17 @@ import ( ) type SocialHandler struct { - filePah string + filePath string } -func MakeSocialHandler(filePah string) SocialHandler { +func MakeSocialHandler(filePath string) SocialHandler { return SocialHandler{ - filePah: filePah, + filePath: filePath, } } func (h SocialHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.SocialResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.SocialResponse](h.filePath) if err != nil { slog.Error("Error reading social file", "error", err) diff --git a/handler/talks.go b/handler/talks.go index 55e7c659..de8a3e91 100644 --- a/handler/talks.go +++ b/handler/talks.go @@ -9,17 +9,17 @@ import ( ) type TalksHandler struct { - filePah string + filePath string } -func MakeTalksHandler(filePah string) TalksHandler { +func MakeTalksHandler(filePath string) TalksHandler { return TalksHandler{ - filePah: filePah, + filePath: filePath, } } func (h TalksHandler) Handle(w baseHttp.ResponseWriter, r *baseHttp.Request) *http.ApiError { - data, err := pkg.ParseJsonFile[payload.TalksResponse](h.filePah) + data, err := pkg.ParseJsonFile[payload.TalksResponse](h.filePath) if err != nil { slog.Error("Error reading talks file", "error", err) From 9b7755c3a79be197a29bcf1a0525287d4f0e024a Mon Sep 17 00:00:00 2001 From: Gustavo Ocanto Date: Thu, 26 Jun 2025 12:18:58 +0800 Subject: [PATCH 17/17] remove quotes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a4a23824..a8d0ddd0 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ YELLOW := \033[1;33m # -------------------------------------------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------------------------------------------- # -ROOT_NETWORK := "oullin_net" +ROOT_NETWORK := oullin_net DATABASE := postgres SOURCE := go_bindata ROOT_PATH := $(shell pwd)