feat: add vehicle key derivation
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
package com.lingniu.ingest.facts;
|
||||||
|
|
||||||
|
import com.lingniu.ingest.api.ProtocolId;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
|
||||||
|
public final class VehicleKey {
|
||||||
|
|
||||||
|
private VehicleKey() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String derive(ProtocolId protocol, String vin, String phone, String rawIdentitySeed) {
|
||||||
|
if (protocol == null) {
|
||||||
|
throw new IllegalArgumentException("protocol must not be null");
|
||||||
|
}
|
||||||
|
String normalizedVin = clean(vin);
|
||||||
|
if (!normalizedVin.isBlank()) {
|
||||||
|
return normalizedVin;
|
||||||
|
}
|
||||||
|
String normalizedPhone = clean(phone);
|
||||||
|
if (protocol == ProtocolId.JT808 && !normalizedPhone.isBlank()) {
|
||||||
|
return "jt808:" + normalizedPhone;
|
||||||
|
}
|
||||||
|
String seed = clean(rawIdentitySeed);
|
||||||
|
if (seed.isBlank()) {
|
||||||
|
seed = protocol.name();
|
||||||
|
}
|
||||||
|
return "unknown:" + protocol.name() + ":" + sha256(seed).substring(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String clean(String value) {
|
||||||
|
return value == null ? "" : value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sha256(String value) {
|
||||||
|
try {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
return HexFormat.of().formatHex(digest.digest(value.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("SHA-256 is unavailable", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.lingniu.ingest.facts;
|
||||||
|
|
||||||
|
import com.lingniu.ingest.api.ProtocolId;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
class VehicleKeyTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void usesVinForGb32960() {
|
||||||
|
assertThat(VehicleKey.derive(ProtocolId.GB32960, " LB9A32A20P0LS1257 ", "", "abc"))
|
||||||
|
.isEqualTo("LB9A32A20P0LS1257");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void usesResolvedVinForJt808WhenPresent() {
|
||||||
|
assertThat(VehicleKey.derive(ProtocolId.JT808, "VIN001", "013912345678", "abc"))
|
||||||
|
.isEqualTo("VIN001");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void usesPhoneForJt808WhenVinIsMissing() {
|
||||||
|
assertThat(VehicleKey.derive(ProtocolId.JT808, "", "013912345678", "abc"))
|
||||||
|
.isEqualTo("jt808:013912345678");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void usesStableUnknownHashWhenVehicleIdentifiersAreMissing() {
|
||||||
|
assertThat(VehicleKey.derive(ProtocolId.JT808, "", "", "abc"))
|
||||||
|
.isEqualTo(VehicleKey.derive(ProtocolId.JT808, null, null, "abc"))
|
||||||
|
.startsWith("unknown:JT808:");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsMissingProtocol() {
|
||||||
|
assertThatThrownBy(() -> VehicleKey.derive(null, "VIN001", "", "abc"))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
|
.hasMessageContaining("protocol");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user