Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"iterations" : 4,
"threads" : 1,
"forks" : 3,
"mean_ops" : 823635.7718335792
"mean_ops" : 824615.1373750888
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"iterations" : 4,
"threads" : 1,
"forks" : 3,
"mean_ops" : 655010.8023043653
"mean_ops" : 668869.350006137
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
@ToString
public class Id {
private String id;
private String prefix;
private String suffix;
private Date generatedDate;
private int node;
private int exponent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public Base36IdFormatter(IdFormatter idFormatter) {
this.idFormatter = idFormatter;
}

@Override
public IdParserType getType() {
throw new UnsupportedOperationException();
}

@Override
public String format(final DateTime dateTime,
final int nodeId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public class DefaultIdFormatter implements IdFormatter {
private static final Pattern PATTERN = Pattern.compile("(.*)([0-9]{15})([0-9]{4})([0-9]{3})");
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyMMddHHmmssSSS");

@Override
public IdParserType getType() {
return IdParserType.DEFAULT;
}

@Override
public String format(final DateTime dateTime,
final int nodeId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

public interface IdFormatter {

IdParserType getType();

String format(final DateTime dateTime,
final int nodeId,
final int randomNonce);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class IdFormatters {

private static final IdFormatter originalIdFormatter = new DefaultIdFormatter();
private static final IdFormatter base36IdFormatter = new Base36IdFormatter(originalIdFormatter);
private static final IdFormatter suffixIdFormatter = new SuffixIdFormatter();

public static IdFormatter original() {
return originalIdFormatter;
Expand All @@ -31,4 +32,8 @@ public static IdFormatter base36() {
return base36IdFormatter;
}

public static IdFormatter suffix() {
return suffixIdFormatter;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.appform.ranger.discovery.bundle.id.formatter;

import lombok.Getter;

@Getter
public enum IdParserType {
DEFAULT (0),
SUFFIX (11);
Comment on lines +7 to +8

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@r0goyal To maintain consistency, shouldnt it be DEFAULT(00), SUFFIX(01) ?


private final int value;

IdParserType(final int value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,21 @@
import lombok.extern.slf4j.Slf4j;
import lombok.val;

import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;


@Slf4j
@UtilityClass
public class IdParsers {
private static final int MINIMUM_ID_LENGTH = 22;
private static final Pattern PATTERN = Pattern.compile("(.*)([0-9]{22})");
private static final Pattern PATTERN = Pattern.compile("([A-Za-z]*)([0-9]{22})([0-9]{2})?(.*)");

private final Map<Integer, IdFormatter> parserRegistry = Map.of(
IdFormatters.original().getType().getValue(), IdFormatters.original(),
IdFormatters.suffix().getType().getValue(), IdFormatters.suffix()
);

/**
* Parse the given string to get ID
Expand All @@ -44,7 +51,18 @@ public Optional<Id> parse(final String idString) {
if (!matcher.find()) {
return Optional.empty();
}
return IdFormatters.original().parse(idString);

val parserType = matcher.group(3);
if (parserType == null) {
return IdFormatters.original().parse(idString);
}

val parser = parserRegistry.get(Integer.parseInt(matcher.group(3)));
if (parser == null) {
log.warn("Could not parse idString {}, Invalid formatter type {}", idString, parserType);
return Optional.empty();
}
return parser.parse(idString);
} catch (Exception e) {
log.warn("Could not parse idString {}", e.getMessage());
return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2024 Authors, Flipkart Internet Pvt. Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.appform.ranger.discovery.bundle.id.formatter;

import io.appform.ranger.discovery.bundle.id.Id;
import lombok.val;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.util.Optional;
import java.util.regex.Pattern;

public class SuffixIdFormatter implements IdFormatter {
private static final Pattern PATTERN = Pattern.compile("([A-Za-z]*)([0-9]{15})([0-9]{4})([0-9]{3})([0-9]{2})([0-9]*)");
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyMMddHHmmssSSS");

@Override
public IdParserType getType() {
return IdParserType.SUFFIX;
}

@Override
public String format(final DateTime dateTime,
final int nodeId,
final int randomNonce) {
return String.format("%s%04d%03d%02d", DATE_TIME_FORMATTER.print(dateTime), nodeId, randomNonce, getType().getValue());
}

@Override
public Optional<Id> parse(final String idString) {
val matcher = PATTERN.matcher(idString);
if (!matcher.find()) {
return Optional.empty();
}
return Optional.of(Id.builder()
.id(idString)
.prefix(matcher.group(1))
.suffix(matcher.group(6))
.node(Integer.parseInt(matcher.group(3)))
.exponent(Integer.parseInt(matcher.group(4)))
.generatedDate(DATE_TIME_FORMATTER.parseDateTime(matcher.group(2)).toDate())
.build());
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package io.appform.ranger.discovery.bundle.id.generator;

import io.appform.ranger.discovery.bundle.id.formatter.IdFormatter;
import io.appform.ranger.discovery.bundle.id.formatter.IdFormatters;
import io.appform.ranger.discovery.bundle.id.nonce.NonceGenerator;
import io.appform.ranger.discovery.bundle.id.nonce.RandomNonceGenerator;

public class DefaultIdGenerator extends IdGeneratorBase {

public DefaultIdGenerator() {
super(IdFormatters.original(), new RandomNonceGenerator());
}

public DefaultIdGenerator(final IdFormatter idFormatter) {
super(idFormatter, new RandomNonceGenerator());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,25 @@ public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespac
.build();
}

public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespace, final String suffix, final IdFormatter idFormatter) {
val dateTime = new DateTime(nonceInfo.getTime());
val id = String.format("%s%s%s", namespace, idFormatter.format(dateTime, getNodeId(), nonceInfo.getExponent()), suffix != null ? suffix : "");
return Id.builder()
.id(id)
.exponent(nonceInfo.getExponent())
.generatedDate(dateTime.toDate())
.node(getNodeId())
.build();
}

public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespace) {
return getIdFromIdInfo(nonceInfo, namespace, idFormatter);
}

public final Id getIdFromIdInfo(final NonceInfo nonceInfo, final String namespace, final String suffix) {
return getIdFromIdInfo(nonceInfo, namespace, suffix, idFormatter);
}

public final IdValidationState validateId(final List<IdValidationConstraint> inConstraints, final Id id, final boolean skipGlobal) {
// First evaluate global constraints
val failedGlobalConstraint
Expand Down Expand Up @@ -139,6 +154,10 @@ public final Id generate(final String namespace) {
return getIdFromIdInfo(idInfo, namespace);
}

public final Id generate(final String namespace, final String suffix) {
val idInfo = nonceGenerator.generate(namespace);
return getIdFromIdInfo(idInfo, namespace, suffix);
}

public final Id generate(final String namespace, final IdFormatter idFormatter) {
val idInfo = nonceGenerator.generate(namespace);
Expand Down Expand Up @@ -167,16 +186,29 @@ public final Optional<Id> generateWithConstraints(final String namespace, final
return generateWithConstraints(request);
}

public Optional<Id> generateWithConstraints(
String namespace,
String suffix,
final List<IdValidationConstraint> inConstraints) {
return generateWithConstraints(IdGenerationRequest.builder()
.prefix(namespace)
.constraints(inConstraints)
.skipGlobal(false)
.idFormatter(idFormatter)
.build());
}

public final Optional<Id> generateWithConstraints(final IdGenerationRequest request) {
val domain = request.getDomain() != null ? registeredDomains.getOrDefault(request.getDomain(), Domain.DEFAULT) : Domain.DEFAULT;
val idGenerationInput = IdGenerationInput.builder()
.prefix(request.getPrefix())
.suffix(request.getSuffix())
.domain(domain)
.build();
return Optional.ofNullable(retryer.get(
() -> {
val idInfoOptional = nonceGenerator.generateWithConstraints(idGenerationInput);
val id = getIdFromIdInfo(idInfoOptional, request.getPrefix(), request.getIdFormatter());
val id = getIdFromIdInfo(idInfoOptional, request.getPrefix(), request.getSuffix(), request.getIdFormatter());
return new GenerationResult(
idInfoOptional,
validateId(request.getConstraints(), id, request.isSkipGlobal()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@Builder
public class IdGenerationInput {
String prefix;
String suffix;
Domain domain;

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
public class IdGenerationRequest {

String prefix;
String suffix;
String domain;
boolean skipGlobal;
List<IdValidationConstraint> constraints;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ void testConstraintFailure() {

@Test
void testNodeId() {
val generatedId = IdGenerator.generate("TEST123");
val generatedId = IdGenerator.generate("TEST");
val parsedId = IdGenerator.parse(generatedId.getId()).orElse(null);
Assertions.assertNotNull(parsedId);
Assertions.assertEquals(parsedId.getNode(), nodeId);
Expand Down Expand Up @@ -225,17 +225,24 @@ void testParseSuccess() {
}

@Test
void testParseSuccessAfterGeneration() {
void testParseFailAfterGeneration() {
val generatedId = IdGenerator.generate("TEST123");
val parsedId = IdGenerator.parse(generatedId.getId()).orElse(null);
Assertions.assertNull(parsedId);
}

@Test
void testParseSuccessAfterGeneration() {
val prefix = "TEST";
val generatedId = IdGenerator.generate(prefix);
val parsedId = IdGenerator.parse(generatedId.getId()).orElse(null);
Assertions.assertNotNull(parsedId);
Assertions.assertEquals(parsedId.getId(), generatedId.getId());
Assertions.assertEquals(parsedId.getExponent(), generatedId.getExponent());
Assertions.assertEquals(parsedId.getNode(), generatedId.getNode());
Assertions.assertEquals(parsedId.getGeneratedDate(), generatedId.getGeneratedDate());
}


@SuppressWarnings("SameParameterValue")
private Date generateDate(int year, int month, int day, int hour, int min, int sec, int ms, ZoneId zoneId) {
return Date.from(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.appform.ranger.discovery.bundle.id;

import io.appform.ranger.discovery.bundle.id.formatter.IdFormatters;
import io.appform.ranger.discovery.bundle.id.formatter.IdParsers;
import io.appform.ranger.discovery.bundle.id.generator.DefaultIdGenerator;
import lombok.val;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class IdParsersTest {

@Test
void testDefaultId() throws ParseException {
val id = "T2407101232336168748798";
val parsedId = IdParsers.parse(id).orElse(null);
Assertions.assertNotNull(parsedId);
Assertions.assertEquals(id, parsedId.getId());
Assertions.assertEquals(798, parsedId.getExponent());
Assertions.assertEquals(8748, parsedId.getNode());
assertDate("240710123233616", parsedId.getGeneratedDate());
}

private void assertDate(final String dateString, final Date date) throws ParseException {
Assertions.assertEquals(new SimpleDateFormat("yyMMddHHmmssSSS").parse(dateString), date);
}

@Test
void testParseSuccessAfterGenerationWithSuffix() {
val idGenerator = new DefaultIdGenerator(IdFormatters.suffix());
val prefix = "TEST";
val suffix = "007";
val generatedId = idGenerator.generate(prefix, suffix);
val parsedId = IdGenerator.parse(generatedId.getId()).orElse(null);
Assertions.assertNotNull(parsedId);
Assertions.assertEquals(prefix, parsedId.getPrefix());
Assertions.assertEquals(suffix, parsedId.getSuffix());
Assertions.assertEquals(parsedId.getId(), generatedId.getId());
Assertions.assertEquals(parsedId.getExponent(), generatedId.getExponent());
Assertions.assertEquals(parsedId.getNode(), generatedId.getNode());
Assertions.assertEquals(parsedId.getGeneratedDate(), generatedId.getGeneratedDate());
}
}