We Had To Use Dark Magic To Make This Work

This commit is contained in:
2023-11-01 10:56:22 -04:00
commit 24a142c6a6
11 changed files with 416 additions and 0 deletions

2
.gitignore vendored Executable file
View File

@@ -0,0 +1,2 @@
.idea/
.DS_Store

21
README.md Executable file
View File

@@ -0,0 +1,21 @@
# Email api golang skd
```shell
go get git.siteworxpro.com/golang/packages/email-api
```
````go
import "git.siteworxpro.com/golang/packages/email-api/client"
config := client.ConfigFromEnv()
apiClient := client.NewClient(config)
emails, err := apiClient.GetEmails(1, 100)
if err != nil {
log.Fatal(err)
}
for _, email := range emails.Emails {
println(email.Uuid)
}
````

39
client/client.go Executable file
View File

@@ -0,0 +1,39 @@
package client
type Client struct {
config *Configuration
token *Token
}
var c = struct {
client *Client
initialized bool
}{}
type accessTokenRequest struct {
GrantType string `json:"grant_type"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
}
func NewClient(config *Configuration) *Client {
if c.initialized {
return c.client
}
client := &Client{
config: config,
}
token, err := getToken(client.config)
if err != nil {
return nil
}
client.token = token
c.client = client
return c.client
}

36
client/configuration.go Executable file
View File

@@ -0,0 +1,36 @@
package client
import (
"git.siteworxpro.com/golang/packages/utilities/Env"
"log"
)
const (
apiEndpoint Env.EnvironmentVariable = "API_ENDPOINT"
clientId Env.EnvironmentVariable = "CLIENT_ID"
clientSecret Env.EnvironmentVariable = "CLIENT_SECRET"
)
type Configuration struct {
Endpoint string
ClientId string
ClientSecret string
}
func ConfigFromEnv() *Configuration {
config := Configuration{
Endpoint: apiEndpoint.GetEnvString("https://email.siteworxpro.com"),
ClientId: clientId.GetEnvString(""),
ClientSecret: clientSecret.GetEnvString(""),
}
if config.ClientId == "" {
log.Fatal("Client ID not provided in ENV. use CLIENT_ID")
}
if config.ClientSecret == "" {
log.Fatal("Client Secret not provided in ENV. use ClientSecret")
}
return &config
}

64
client/create.go Executable file
View File

@@ -0,0 +1,64 @@
package client
import (
"bytes"
"encoding/json"
"net/http"
"time"
)
type EmailRequest struct {
Source string `json:"Source"`
Destination struct {
ToAddresses []string `json:"ToAddresses"`
CcAddresses []string `json:"CcAddresses"`
BccAddresses []string `json:"BccAddresses"`
} `json:"Destination"`
Message struct {
Body struct {
Html struct {
Data string `json:"Data"`
} `json:"Html,omitempty"`
Text struct {
Data string `json:"Data"`
} `json:"Text,omitempty"`
} `json:"Body"`
Subject struct {
Data string `json:"Data"`
} `json:"Subject"`
} `json:"Message"`
ScheduledTime time.Time `json:"ScheduledTime,omitempty"`
Catch bool `json:"Catch,omitempty"`
}
type EmailCreateResponse struct {
Id string `json:"id"`
CreatedAt time.Time `json:"created_at"`
}
func (client *Client) SendEmail(request *EmailRequest) (*EmailCreateResponse, error) {
jsonData, _ := json.Marshal(request)
httpClient := &http.Client{}
req, _ := http.NewRequest(
"POST",
client.config.Endpoint+"/api/email",
bytes.NewReader(jsonData),
)
req.Header.Set("Authorization", client.token.TokenType+" "+client.token.AccessToken)
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
response := Response[EmailCreateResponse]{}
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
return nil, err
}
return &response.Payload, nil
}

70
client/get.go Executable file
View File

@@ -0,0 +1,70 @@
package client
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
)
func (client *Client) GetEmails(page uint16, limit uint16) (*EmailResponse, error) {
response := Response[EmailResponse]{}
httpClient := &http.Client{}
req, _ := http.NewRequest(
"GET",
client.config.Endpoint+"/api/email?page="+strconv.Itoa(int(page))+"&limit="+strconv.Itoa(int(limit)),
nil,
)
req.Header.Set("Authorization", client.token.TokenType+" "+client.token.AccessToken)
get, err := httpClient.Do(req)
if err != nil {
return nil, err
}
if get.StatusCode >= 400 {
return nil, fmt.Errorf("")
}
body := make([]byte, get.ContentLength)
_, err = get.Body.Read(body)
if err != nil {
return nil, err
}
err = json.Unmarshal(body, &response)
if err != nil {
return nil, err
}
return &response.Payload, nil
}
func (client *Client) GetEmail(uuid string) (*Email, error) {
httpClient := &http.Client{}
req, _ := http.NewRequest(
"GET",
client.config.Endpoint+"/api/email/"+uuid,
nil,
)
req.Header.Set("Authorization", client.token.TokenType+" "+client.token.AccessToken)
get, err := httpClient.Do(req)
defer get.Body.Close()
if err != nil {
return nil, err
}
if get.StatusCode >= 400 {
return nil, fmt.Errorf("")
}
response := Response[Email]{}
err = json.NewDecoder(get.Body).Decode(&response)
if err != nil {
return nil, err
}
return &response.Payload, nil
}

99
client/token.go Executable file
View File

@@ -0,0 +1,99 @@
package client
import (
"bytes"
"context"
"encoding/json"
"git.siteworxpro.com/golang/packages/email-api/redis"
"net/http"
"time"
)
type Token struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn uint16 `json:"expires_in"`
}
type AccessTokenRequest struct {
GrantType string `json:"grant_type"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
}
func NewToken(config *Configuration) *Token {
return &Token{}
}
func getToken(configuration *Configuration) (*Token, error) {
token := FromCache()
if token.AccessToken != "" {
return token, nil
}
request := accessTokenRequest{
GrantType: "client_credentials",
ClientId: configuration.ClientId,
ClientSecret: configuration.ClientSecret,
}
body, _ := json.Marshal(request)
resp, err := http.Post(configuration.Endpoint+"/access_token", "application/json", bytes.NewBuffer(body))
if err != nil {
return nil, err
}
responseBody := make([]byte, resp.ContentLength)
_, err = resp.Body.Read(responseBody)
if err != nil {
return nil, err
}
err = json.Unmarshal(responseBody, token)
if err != nil {
return nil, err
}
saveCache(token)
return token, nil
}
func FromCache() *Token {
rdb := redis.NewRedis()
token := Token{}
result, err := rdb.Get(context.Background(), "api.access_token").Result()
if err != nil {
return &token
}
err = json.Unmarshal([]byte(result), &token)
if err != nil {
return &token
}
return &token
}
func saveCache(token *Token) {
rdb := redis.NewRedis()
tokenJson, _ := json.Marshal(token)
expiresIn := time.Duration(int64(token.ExpiresIn) * 100000000)
cmd := rdb.Set(context.Background(), "api.access_token", tokenJson, expiresIn)
result, err := cmd.Result()
if err != nil {
return
}
println(result)
}

29
client/types.go Executable file
View File

@@ -0,0 +1,29 @@
package client
import "time"
type Email struct {
Uuid string `json:"uuid"`
IsSent uint8 `json:"is_sent"`
CreatedAt time.Time `json:"created_at"`
SentTime time.Time `json:"sent_time"`
To string `json:"to"`
From string `json:"from"`
Subject string `json:"subject"`
Body string `json:"body,omitempty"`
}
type EmailResponse struct {
Emails []Email `json:"emails"`
Page uint16 `json:"page"`
AvailablePages uint16 `json:"availablePages"`
Total uint16 `json:"total"`
}
type Response[T any] struct {
Payload T `json:"payload"`
Time uint32 `json:"time"`
Version string `json:"version"`
Status string `json:"status"`
Md5 string `json:"md5"`
}

10
go.mod Executable file
View File

@@ -0,0 +1,10 @@
module git.siteworxpro.com/golang/packages/email-api
go 1.19
require (
git.siteworxpro.com/golang/packages/utilities v1.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/redis/go-redis/v9 v9.0.2 // indirect
)

6
go.sum Executable file
View File

@@ -0,0 +1,6 @@
git.siteworxpro.com/golang/packages/utilities v1.0.1/go.mod h1:5ZZDqKb5Y7B552oJjhaMVAj6r9xvqw/wkp1EADmT5wg=
git.siteworxpro.com/golang/packages/utilities v1.2.0 h1:UDsaEl0NOqPcHudMKcOGwtlShBoqCW3NXYZH9zh+Ju8=
git.siteworxpro.com/golang/packages/utilities v1.2.0/go.mod h1:5ZZDqKb5Y7B552oJjhaMVAj6r9xvqw/wkp1EADmT5wg=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=

40
redis/redis.go Executable file
View File

@@ -0,0 +1,40 @@
package redis
import (
"git.siteworxpro.com/golang/packages/utilities/Env"
"github.com/redis/go-redis/v9"
"strconv"
)
const (
redisHost Env.EnvironmentVariable = "REDIS_HOST"
redisHPassword Env.EnvironmentVariable = "REDIS_PASSWORD"
redisDb Env.EnvironmentVariable = "REDIS_DB"
)
var r = struct {
client *redis.Client
initialized bool
}{}
func NewRedis() *redis.Client {
if r.initialized {
return r.client
}
db, err := strconv.ParseInt(redisDb.GetEnvString("0"), 10, 64)
if err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: redisHost.GetEnvString("localhost:6379"),
Password: redisHPassword.GetEnvString(""),
DB: int(db),
})
r.client = rdb
return r.client
}