Skip to content

Commit

Permalink
Split otils.go to multiple files
Browse files Browse the repository at this point in the history
Group by functions responsibility.
  • Loading branch information
Cuong Manh Le authored and cuonglm committed Jul 21, 2021
1 parent 7832baf commit 808f042
Show file tree
Hide file tree
Showing 7 changed files with 563 additions and 540 deletions.
76 changes: 76 additions & 0 deletions cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package otils

import (
"net/http"
)

type CORS struct {
Origins []string
Methods []string
Headers []string

// AllowCredentials when set signifies that the header
// "Access-Control-Allow-Credentials" which will allow
// the possibility of the frontend XHR's withCredentials=true
// to be set.
AllowCredentials bool

next http.Handler
}

func CORSMiddleware(c *CORS, next http.Handler) http.Handler {
if c == nil {
return next
}
copy := new(CORS)
*copy = *c
copy.next = next
return copy
}

var allInclusiveCORS = &CORS{
Origins: []string{"*"},
Methods: []string{"*"},
Headers: []string{"*"},

AllowCredentials: true,
}

// CORSMiddlewareAllInclusive is a convenience helper that uses the
// all inclusive CORS:
// Access-Control-Allow-Origin: *
// Access-Control-Allow-Methods: *
// Access-Control-Allow-Headers: *
// Access-Control-Allow-Credentials: *
// thus enabling all origins, all methods and all headers.
func CORSMiddlewareAllInclusive(next http.Handler) http.Handler {
return CORSMiddleware(allInclusiveCORS, next)
}

var _ http.Handler = (*CORS)(nil)

func (c *CORS) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
c.setCORSForResponseWriter(rw)
if c.next != nil {
c.next.ServeHTTP(rw, req)
}
}

func (c *CORS) setCORSForResponseWriter(rw http.ResponseWriter) {
for _, origin := range c.Origins {
rw.Header().Add("Access-Control-Allow-Origin", origin)
}
for _, mtd := range c.Methods {
rw.Header().Add("Access-Control-Allow-Methods", mtd)
}
for _, hdr := range c.Headers {
rw.Header().Add("Access-Control-Allow-Headers", hdr)
}
if c.AllowCredentials {
rw.Header().Add("Access-Control-Allow-Credentials", "true")
}
}

func unexportedField(name string) bool {
return len(name) > 0 && name[0] >= 'a' && name[0] <= 'z'
}
19 changes: 19 additions & 0 deletions env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package otils

import (
"os"
"strings"
)

func EnvOrAlternates(envVar string, alternates ...string) string {
if retr := strings.TrimSpace(os.Getenv(envVar)); retr != "" {
return retr
}
for _, alt := range alternates {
alt = strings.TrimSpace(alt)
if alt != "" {
return alt
}
}
return ""
}
58 changes: 58 additions & 0 deletions http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package otils

import (
"fmt"
"net/http"
)

// RedirectAllTrafficTo creates a handler that can be attached
// to an HTTP traffic multiplexer to perform a 301 Permanent Redirect
// to the specified host for any path, anytime that the handler
// receives a request.
// Sample usage is:
//
// httpsRedirectHandler := RedirectAllTrafficTo("https://orijtech.com")
// if err := http.ListenAndServe(":80", httpsRedirectHandler); err != nil {
// log.Fatal(err)
// }
//
// which is used in production at orijtech.com to redirect any non-https
// traffic from http://orijtech.com/* to https://orijtech.com/*
func RedirectAllTrafficTo(host string) http.Handler {
fn := func(rw http.ResponseWriter, req *http.Request) {
finalURL := fmt.Sprintf("%s%s", host, req.URL.Path)
rw.Header().Set("Location", finalURL)
rw.WriteHeader(301)
}

return http.HandlerFunc(fn)
}

// StatusOK returns true if a status code is a 2XX code
func StatusOK(code int) bool { return code >= 200 && code <= 299 }

type CodedError struct {
code int
msg string
}

func (cerr *CodedError) Error() string {
if cerr == nil {
return ""
}
return cerr.msg
}

func (cerr *CodedError) Code() int {
if cerr == nil {
return http.StatusOK
}
return cerr.code
}

func MakeCodedError(msg string, code int) *CodedError {
return &CodedError{
msg: msg,
code: code,
}
}
121 changes: 121 additions & 0 deletions json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package otils

import (
"encoding/json"
"strconv"
"strings"
"time"
)

// NullableString represents a string that is sent
// back by some APIs as null, in JSON unquoted which
// makes them un-unmarshalable in Go.
// NullableString interprets null as "".
type NullableString string

var _ json.Unmarshaler = (*NullableString)(nil)

func (ns *NullableString) UnmarshalJSON(b []byte) error {
str := string(b)
// Special case when we encounter `null`, modify it to the empty string
if str == "null" || str == "" {
str = ""
} else {
unquoted, err := strconv.Unquote(str)
if err != nil {
return err
}
*ns = NullableString(unquoted)
}

return nil
}

type NullableFloat64 float64

var _ json.Unmarshaler = (*NullableFloat64)(nil)

func (nf64 *NullableFloat64) UnmarshalJSON(b []byte) error {
str := string(b)
if strings.HasPrefix(str, "\"") {
unquoted, err := strconv.Unquote(str)
if err == nil {
str = unquoted
}
}

f64, err := strconv.ParseFloat(str, 64)
if err == nil {
*nf64 = NullableFloat64(f64)
return nil
}

// Otherwise trying checking if it was null
var ns NullableString
if err := json.Unmarshal(b, &ns); err != nil {
return err
}

if ns == "" {
*nf64 = 0.0
return nil
}

f64, err = strconv.ParseFloat(str, 64)
if err != nil {
return err
}

*nf64 = NullableFloat64(f64)
return nil
}

type NumericBool bool

func (nb *NumericBool) UnmarshalJSON(blob []byte) error {
if len(blob) < 1 {
*nb = false
return nil
}

s := string(blob)
// Try first parsing an integer.
pBool, err := strconv.ParseBool(s)
if err == nil {
*nb = NumericBool(pBool)
return nil
}

pInt, err := strconv.ParseInt(s, 10, 32)
if err != nil {
*nb = pInt != 0
return nil
}

return err
}

type NullableTime time.Time

var _ json.Unmarshaler = (*NullableTime)(nil)

func (nt *NullableTime) UnmarshalJSON(b []byte) error {
var ns NullableString
if err := json.Unmarshal(b, &ns); err != nil {
return err
}
if ns == "" {
nt = nil
return nil
}

// To parse the time, we need to quote the value
quotedStr := strconv.Quote(string(ns))
t := new(time.Time)
if err := json.Unmarshal([]byte(quotedStr), t); err != nil {
return err
}

*nt = NullableTime(*t)
return nil
}
Loading

0 comments on commit 808f042

Please sign in to comment.