Skip to content

Commit

Permalink
Add UUID transforms supports v3, v4, v5 (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nearhan authored and chilland committed Jul 31, 2017
1 parent fd5d465 commit 02fc2bf
Show file tree
Hide file tree
Showing 7 changed files with 659 additions and 1 deletion.
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Kazaam currently supports the following transforms:
- coalesce
- extract
- timestamp
- uuid
- default
- pass

Expand Down Expand Up @@ -240,6 +241,98 @@ would result in
}
```

### UUID
A `uuid` transform generates a UUID based on the spec. Currently supports UUIDv3, UUIDv4, UUIDv5.

For version 4 is a very simple spec

```javascript
{
"operation": "uuid",
"spec": {
"doc.uuid": {
"version": 4, //required
}
}
}
```

executed on a json message with format
```javascript
{
"doc": {
"author_id": 11122112,
"document_id": 223323,
"meta": {
"id": 23
}
}
}
```

would result in
```javascript
{
"doc": {
"author_id": 11122112,
"document_id": 223323,
"meta": {
"id": 23
}
"uuid": "f03bacc1-f4e0-4371-a5c5-e8160d3d6c0c"
}
}
```

For UUIDv3 & UUIDV5 are a bit more complex. These require a Name Space which is a valid UUID already, and a set of paths, which generate UUID's based on the value of that path. If that path doesn't exist in the incoming document, a default field will be used instead. **Note** both of these fields must be strings.
**Additionally** you can use the 4 predefined namespaces such as `DNS`, `URL`, `OID`, & `X500` in the name space field otherwise pass your own UUID.

```javascript
{
"operation":"uuid",
"spec":{
"doc.uuid":{
"version":5,
"nameSpace":"DNS",
"names":[
{"path":"doc.author_name", "default":"some string"},
{"path":"doc.type", "default":"another string"},
]
}
}
}
```

executed on a json message with format
```javascript
{
"doc": {
"author_name": "jason",
"type": "secret-document"
"document_id": 223323,
"meta": {
"id": 23
}
}
}
```

would result in
```javascript
{
"doc": {
"author_name": "jason",
"type": "secret-document",
"document_id": 223323,
"meta": {
"id": 23
},
"uuid": "f03bacc1-f4e0-4371-a7c5-e8160d3d6c0c"
}
}
```


### Default
A default transform provides the ability to set a key's value explicitly. For example
```javascript
Expand Down
2 changes: 2 additions & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ package: github.com/qntfy/kazaam
import:
- package: github.com/buger/jsonparser
version: bb14bb6c38f6cf1706ef55278891d184b6a51b0e
- package: github.com/satori/go.uuid
version: v1.1.0
1 change: 1 addition & 0 deletions kazaam.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func init() {
"concat": transform.Concat,
"coalesce": transform.Coalesce,
"timestamp": transform.Timestamp,
"uuid": transform.UUID,
}
}

Expand Down
Binary file added kazaam/kazaam
Binary file not shown.
2 changes: 1 addition & 1 deletion kazaam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestReregisterKazaamTransform(t *testing.T) {
}

func TestDefaultTransformsSetCardinarily(t *testing.T) {
if len(validSpecTypes) != 7 {
if len(validSpecTypes) != 8 {
t.Error("Unexpected number of default transforms. Missing tests?")
}
}
Expand Down
125 changes: 125 additions & 0 deletions transform/uuid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package transform

import (
"strings"

"github.com/buger/jsonparser"
uuid "github.com/satori/go.uuid"
)

var (
versionError = SpecError("Please set version 3 || 4 || 5")
)

// UUID tries to generate a UUID based on spec components
func UUID(spec *Config, data []byte) ([]byte, error) {

// iterate through the spec
for k, v := range *spec.Spec {
outPath := strings.Split(k, ".")

// convert to corrct type
uuidSpec, ok := v.(map[string]interface{})
if !ok {
return nil, SpecError("Invalid Spec for UUID")
}

//grab version
version, ok := uuidSpec["version"]

if !ok {
return nil, versionError
}

var u uuid.UUID
var err error

switch version {
case 4.0:
u = uuid.NewV4()

case 3.0, 5.0:

names, ok := uuidSpec["names"]
if !ok {
return nil, SpecError("Must provide names field")
}

nameSpace, ok := uuidSpec["nameSpace"].(string)
if !ok {
return nil, SpecError("Must provide namesapce, Must be a string")
}

var nameSpaceUUID uuid.UUID

// swtich on the namespace
switch nameSpace {
case "DNS":
nameSpaceUUID = uuid.NamespaceDNS
case "URL":
nameSpaceUUID = uuid.NamespaceURL
case "OID":
nameSpaceUUID = uuid.NamespaceOID
case "X500":
nameSpaceUUID = uuid.NamespaceX500
default:
nameSpaceUUID, err = uuid.FromString(nameSpace)
if err != nil {
return nil, SpecError("nameSpace is not a valid UUID or is not DNS, URL, OID, X500")
}
}

nameFields, ok := names.([]interface{})
if !ok {
return nil, SpecError("Spec is invalid")
}

// loop over the names field
for _, field := range nameFields {
p, _ := field.(map[string]interface{})["path"].(string)

name, err := getJSONRaw(data, p, false)
if err == jsonparser.KeyPathNotFoundError {

d, ok := field.(map[string]interface{})["default"].(string)
if !ok {
return nil, SpecError("Spec is invalid")

}
name = []byte(d)

}

// check if there is an empty uuid & version 3
if u.String() == "00000000-0000-0000-0000-000000000000" && version == 3.0 {

u = uuid.NewV3(nameSpaceUUID, string(name))

// same as above except version 5
} else if u.String() == "00000000-0000-0000-0000-000000000000" && version == 5.0 {

u = uuid.NewV5(nameSpaceUUID, string(name))

} else if version == 3.0 {

u = uuid.NewV3(u, string(name))

} else if version == 5.0 {

u = uuid.NewV3(u, string(name))
}
}

default:
return nil, versionError

}
d, err := jsonparser.Set(data, bookend([]byte(u.String()), '"', '"'), outPath...)
if err != nil {
return nil, err
}

return d, nil
}
return nil, SpecError("Spec invalid for UUID")
}
Loading

0 comments on commit 02fc2bf

Please sign in to comment.