Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UUID provided function #253

Merged
merged 1 commit into from
Apr 13, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
Expand Down Expand Up @@ -103,6 +104,7 @@ public class BuiltinFunctions {
functions.put("to-json", new BuiltinFunctions.ToJson());
functions.put("replace", new BuiltinFunctions.Replace());
functions.put("trim", new BuiltinFunctions.Trim());
functions.put("uuid", new BuiltinFunctions.Uuid());

// BOOLEAN
functions.put("not", new BuiltinFunctions.Not());
Expand Down Expand Up @@ -974,6 +976,48 @@ public JsonNode call(JsonNode input, JsonNode[] arguments) {
}
}

// ===== UUID

public static class Uuid extends AbstractFunction {

public Uuid() {
super("uuid", 0, 2);
}

private long maskMSB(long number) {
final long version = 1 << 12;
long least12SignificantBit = (number & 0x000000000000FFFFL) >> 4;
return (number & 0xFFFFFFFFFFFF0000L) + version + least12SignificantBit;
}

private long maskLSB(long number) {
final long LSB_MASK = 0x3FFFFFFFFFFFFFFFL;
final long LSB_VARIANT3_BITFLAG = 0x8000000000000000L;
return (number & LSB_MASK) + LSB_VARIANT3_BITFLAG;
}

public JsonNode call(JsonNode input, JsonNode[] arguments) {
String uuid;
if (arguments.length == 0) {
uuid = UUID.randomUUID().toString();
} else if (arguments.length == 2) {
// NIL UUID is a special case defined in 4.1.7 of the RFC (https://www.ietf.org/rfc/rfc4122.txt)
if (arguments[0].isNull() && arguments[1].isNull()) {
uuid = "00000000-0000-0000-0000-000000000000";
} else {
long msb = NodeUtils.number(arguments[0], null).asLong();
long lsb = NodeUtils.number(arguments[1], null).asLong();
uuid = new UUID(maskMSB(msb), maskLSB(lsb)).toString();
}
} else {
throw new JsltException("Build-in UUID function must be called with either none or two parameters.");
}

return new TextNode(uuid);
}
}


// ===== JOIN

public static class Join extends AbstractFunction {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.schibsted.spt.data.jslt.buildinfunctions;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.schibsted.spt.data.jslt.Expression;
import com.schibsted.spt.data.jslt.JsltException;
import com.schibsted.spt.data.jslt.Parser;
import com.schibsted.spt.data.jslt.TestBase;
import org.junit.Test;

import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;

public class UuidTest extends TestBase {
private static ObjectMapper mapper = new ObjectMapper();

@Test
public void testUuidWithoutParameterMatchesRegex() throws JsonProcessingException {
Expression given = Parser.compileString("uuid()");
String actual = mapper.writeValueAsString(given.apply(null));

String uuidRegex = "^\"[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\"$";
assertTrue(actual.matches(uuidRegex));
}

@Test
public void testUuidWithoutParameterGeneratesRandomValues() throws JsonProcessingException {
Expression given = Parser.compileString("{ \"uuid1\" : uuid(), \"uuid2\" : uuid() }");
JsonNode result = given.apply(null);
String actual1 = mapper.writeValueAsString(result.findValue("uuid1"));
String actual2 = mapper.writeValueAsString(result.findValue("uuid2"));

assertNotEquals(actual1, actual2);
}

@Test(expected = JsltException.class)
public void testUuidWithOneParameterRaisesJsltException() {
Expression given = Parser.compileString("uuid(123)");
given.apply(null);
}
}
35 changes: 35 additions & 0 deletions core/src/test/resources/function-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,41 @@
"input" : "\"a\"",
"output": "\"a\""
},
{
"query": "uuid(9223372036854775807, 9223372036854775807)",
"input" : "null",
"output": "\"7fffffff-ffff-1fff-bfff-ffffffffffff\""
},
{
"query": "uuid(-9223372036854775808, -9223372036854775808)",
"input" : "null",
"output": "\"80000000-0000-1000-8000-000000000000\""
},
{
"query": "uuid(0, 0)",
"input" : "null",
"output": "\"00000000-0000-1000-8000-000000000000\""
},
{
"query": "uuid(null, 9223372036854775807)",
"input" : "null",
"output": "\"00000000-0000-1000-bfff-ffffffffffff\""
},
{
"query": "uuid(9223372036854775807, null)",
"input" : "null",
"output": "\"7fffffff-ffff-1fff-8000-000000000000\""
},
{
"query": "uuid(1234567890, 1234567890)",
"input" : "null",
"output": "\"00000000-4996-102d-8000-0000499602d2\""
},
{
"query": "uuid(null, null)",
"input" : "null",
"output": "\"00000000-0000-0000-0000-000000000000\""
},
larsga marked this conversation as resolved.
Show resolved Hide resolved
{
"query": "min(1, 2)",
"input" : "null",
Expand Down
16 changes: 16 additions & 0 deletions functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,22 @@ trim(false) => "false"
trim(null) => null
```

### _uuid(long, long) -> string_

Generates a formatted UUID string with dashes.
If no parameter are passed, a random UUID version 4 will be generated.
If two parameter are passed, a UUID version 1 is generated based on two long values where the first parameter represents the MSB (most significant bytes) and the second parameter the LSB (least significant bytes) of the UUID.
If both parameter are null a NIL UUID (00000000-0000-0000-0000-000000000000 is generated)

Examples:

```
uuid() => "b02c39c0-6f8f-4250-97cd-78500af36e27"
uuid(1234567890, 1234567890) => "00000000-4996-102d-8000-0000499602d2"
uuid(0, 0) => "00000000-0000-1000-8000-000000000000"
uuid(null, null) => "00000000-0000-0000-0000-000000000000"
```

<!-- BOOLEAN ================================================================-->

## Boolean functions
Expand Down