Skip to content
This repository has been archived by the owner on Sep 24, 2024. It is now read-only.

Add uuid to ulid converter #52

Merged
merged 1 commit into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion api/uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,27 @@ const (

func uuidAPI(app *fiber.App) {
app.Get(APIPrefix+UUIDPath, func(c fiber.Ctx) error {
uuidResponse := Response{Success: true, Data: map[string]interface{}{"uuid": internal.GenerateGUID()}}
uuidResponse := Response{Success: true, Data: map[string]interface{}{"uuid": internal.GenerateUUID()}}
return c.JSON(uuidResponse)
})

app.Get(APIPrefix+UUIDPath+"/convert", func(c fiber.Ctx) error {
uuid := c.Query("uuid", "")
if uuid == "" {
return c.JSON(Response{Success: false, Message: "uuid is required"})
}

convertTo := c.Query("to", "ulid")
var convertedString string
if convertTo == "ulid" {
ulid, err := internal.UUIDtoULID(uuid)
if err != nil {
return c.JSON(Response{Success: false, Message: err.Error()})
}
convertedString = ulid
} else {
return c.JSON(Response{Success: false, Message: "invalid conversion type"})
}
return c.JSON(Response{Success: true, Data: map[string]interface{}{"result": convertedString}})
})
}
87 changes: 87 additions & 0 deletions api/uuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package api

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -25,4 +28,88 @@ func TestUUIDAPI(t *testing.T) {
assert.True(t, ulidResponse.Success)
assert.NotNil(t, ulidResponse.Data.(map[string]interface{})["uuid"])
})

t.Run("Convert UUID to ULID", func(t *testing.T) {
uuid := "860d3040-1e23-416f-a1fd-f4ccc773833d"
expectedULID := "461MR407H385QT3ZFMSK3Q70SX"

req, _ := http.NewRequest("GET", fmt.Sprintf("/api/uuid/convert?uuid=%s&to=ulid", uuid), nil)
resp, _ := app.Test(req)

if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
}

body, _ := ioutil.ReadAll(resp.Body)
var result Response
json.Unmarshal(body, &result)

if !result.Success {
t.Errorf("Expected success to be true, but got false")
}

if _, ok := result.Data.(map[string]interface{})["result"]; !ok {
t.Errorf("Expected 'result' field in response data, but not found")
}

ulid, ok := result.Data.(map[string]interface{})["result"].(string)
if !ok {
t.Errorf("Expected 'result' field to be a string, but got %T", result.Data.(map[string]interface{})["result"])
}

if ulid != expectedULID {
t.Errorf("Expected ULID to be %s, but got %s", expectedULID, ulid)
}
})

t.Run("Invalid UUID", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/api/uuid/convert?uuid=860d3040-1e23-416f-a1fd-f4ccc773833", nil)
resp, _ := app.Test(req)

if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
}

body, _ := io.ReadAll(resp.Body)
var result Response
json.Unmarshal(body, &result)

if result.Success {
t.Errorf("Expected success to be false, but got true")
}

if result.Message != "invalid UUID length: 35" {
t.Errorf("Expected message to be 'invalid UUID length: 35', but got '%s'", result.Message)
}
})

t.Run("Missing UUID", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/api/uuid/convert?to=ulid", nil)
resp, _ := app.Test(req)
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
}

})

t.Run("Invalid conversion type", func(t *testing.T) {
req, _ := http.NewRequest("GET", "/api/uuid/convert?uuid=860d3040-1e23-416f-a1fd-f4ccc773833d&to=invalid", nil)
resp, _ := app.Test(req)

if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
}

body, _ := io.ReadAll(resp.Body)
var result Response
json.Unmarshal(body, &result)

if result.Success {
t.Errorf("Expected success to be false, but got true")
}

if result.Message != "invalid conversion type" {
t.Errorf("Expected message to be 'invalid conversion type', but got '%s'", result.Message)
}
})
}
31 changes: 31 additions & 0 deletions api/yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
Expand Down Expand Up @@ -179,4 +180,34 @@ func TestYamlAPI(t *testing.T) {

assert.Equal(t, false, response.Success)
})

t.Run("Test YAML to Properties conversion - empty yaml", func(t *testing.T) {
requestBody := `{"yaml": ""}`

// Create a new request
req := httptest.NewRequest(http.MethodPost, "/api/yaml?action=to_properties", strings.NewReader(requestBody))
req.Header.Set("Content-Type", "application/json")

// Perform the request
resp, err := app.Test(req)
if err != nil {
t.Fatalf("Failed to perform request: %v", err)
}
defer resp.Body.Close()

// Check the response status code
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status code %d, but got %d", http.StatusOK, resp.StatusCode)
}

// Parse the response body
var response Response
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
t.Fatalf("Failed to parse response body: %v", err)
}
fmt.Println(response)

assert.Equal(t, true, response.Success)
})
}
19 changes: 17 additions & 2 deletions cmd/uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,28 @@ var uuidCmd = &cobra.Command{
Short: "Generate a uuid string",
Long: `Generate a uuid string.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(internal.GenerateGUID())
if len(args) == 0 {
fmt.Println(internal.GenerateUUID())
} else {
to := cmd.Flag("to").Value.String()
switch to {
case "ulid":
ulid, err := internal.UUIDtoULID(args[0])
if err != nil {
fmt.Println(err)
} else {
fmt.Println(ulid)
}
default:
fmt.Println("Unknown conversion")
}
}
},
}

func init() {
rootCmd.AddCommand(uuidCmd)

uuidCmd.Flags().StringP("to", "t", "ulid", "Convert from uuid to (ulid)")
// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
Expand Down
7 changes: 0 additions & 7 deletions internal/guid.go

This file was deleted.

12 changes: 0 additions & 12 deletions internal/guid_test.go

This file was deleted.

5 changes: 1 addition & 4 deletions internal/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ func Properties2Yaml(propertiesData string) (string, error) {
}

// Convert map to YAML
yamlData, err := yaml.Marshal(dataMap)
if err != nil {
return "", err
}
yamlData, _ := yaml.Marshal(dataMap)

return string(yamlData), nil
}
Expand Down
67 changes: 67 additions & 0 deletions internal/uuid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package internal

import (
"bytes"
"encoding/hex"
"strings"

"github.com/google/uuid"
)

const B32_CHARACTERS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"

func GenerateUUID() string {
return uuid.NewString()
}

func UUIDtoULID(uuidStr string) (string, error) {
_, err := uuid.Parse(uuidStr)
if err != nil {
return "", err
}
uuidStr = strings.ReplaceAll(uuidStr, "-", "")
uuidBytes, _ := hex.DecodeString(uuidStr)

return crockfordEncode(uuidBytes), nil
}

func crockfordEncode(input []byte) string {
var output []int
bitsRead := 0
buffer := 0

// Work from the end of the buffer
reversedInput := reverseBuffer(input)

for _, byteVal := range reversedInput {
// Add current byte to start of buffer
buffer |= int(byteVal) << bitsRead
bitsRead += 8

for bitsRead >= 5 {
output = append([]int{buffer & 0x1f}, output...)
buffer >>= 5
bitsRead -= 5
}
}

if bitsRead > 0 {
output = append([]int{buffer & 0x1f}, output...)
}

var resultBuffer bytes.Buffer
for _, byteVal := range output {
resultBuffer.WriteString(string(B32_CHARACTERS[byteVal]))
}

return resultBuffer.String()
}

func reverseBuffer(input []byte) []byte {
length := len(input)
reversed := make([]byte, length)
for i := 0; i < length; i++ {
reversed[i] = input[length-i-1]
}
return reversed
}
33 changes: 33 additions & 0 deletions internal/uuid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package internal

import (
"testing"
)

func TestGenerateUUID(t *testing.T) {
guid := GenerateUUID()
if len(guid) != 36 {
t.Errorf("Expected GUID length to be 36, but got %d", len(guid))
}
}

func TestUUIDtoULID(t *testing.T) {
t.Run("Valid UUID", func(t *testing.T) {
uuid := "860d3040-1e23-416f-a1fd-f4ccc773833d"
expectedULID := "461MR407H385QT3ZFMSK3Q70SX"
ulid, err := UUIDtoULID(uuid)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if ulid != expectedULID {
t.Errorf("Expected ULID to be %s, but got %s", expectedULID, ulid)
}
})
t.Run("Invalid UUID", func(t *testing.T) {
uuid := "860d3040-1e23-416f-a1fd-f4ccc773833"
_, err := UUIDtoULID(uuid)
if err == nil {
t.Errorf("Expected error, but got nil")
}
})
}
3 changes: 3 additions & 0 deletions internal/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ func Yaml2Json(yamlString string) (string, error) {
}

func Yaml2Properties(yamlData string) (string, error) {
if yamlData == "" {
return "", nil
}
// Parse YAML into a Node
var yamlNode yaml.Node
err := yaml.Unmarshal([]byte(yamlData), &yamlNode)
Expand Down
Loading
Loading