Skip to content

Instantly share code, notes, and snippets.

@nfroidure
Created March 4, 2016 09:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nfroidure/f2ce4990c0b6d6e1eb62 to your computer and use it in GitHub Desktop.
Save nfroidure/f2ce4990c0b6d6e1eb62 to your computer and use it in GitHub Desktop.
Generating a MongoDB ObjectId in SalesForce Apex
public with sharing class MongoObjectId {
private static final Integer COUNTER_MASK = 16777215; // 0xFFFFFF
private static final Integer PID_MASK = 65535; // 0xFFFF
private static final String MACHINE_PART = '050135';
private static final String[] hexMap = '0123456789abcdef'.split('');
private static Integer counter = -1;
public static void seed() {
// Seed once or force reseed when testing
if(-1 == counter || Test.isRunningTest()) {
counter = Integer.valueOf(Math.mod(
Integer.valueOf(TestStubs.random() * COUNTER_MASK),
COUNTER_MASK
));
}
System.debug('counter');
System.debug(counter);
}
public static String decToHex(Long x, Integer length) {
String result = '';
while (x != 0) {
Integer modulus = Integer.valueOf(Math.mod(x, 16));
result = hexMap[modulus] + result;
x /= 16;
}
while(result.length() < length) {
result = '0' + result;
}
return result;
}
public static String userIdToPid(String userId) {
Integer pid = 0;
for(Integer x = userId.length() - 1; x >= 0; x--) {
pid = Math.mod(pid + userId.codePointAt(x), PID_MASK);
}
return decToHex(pid, 4);
}
public static String generate() {
// Ensure the counter was seeded
seed();
// Get current timestamp
Integer timestamp = Integer.valueOf(TestStubs.now().getTime() / 1000);
// Compute pid from userId
String pid = userIdToPid(TestStubs.getUserId());
// Increment the internal counter
counter = counter + 1;
counter = counter & COUNTER_MASK;
List<String> parts = new List<String> {
decToHex(timestamp, 8),
MACHINE_PART,
pid,
decToHex(counter, 6)
};
System.debug('counter');
System.debug(counter);
System.debug('Timestamp');
System.debug(timestamp);
return String.join(parts, '');
}
}
@isTest(seeAllData=false)
private class TestMongoObjectId {
static testMethod void testToHex() {
Test.startTest();
System.assertEquals('000ff', MongoObjectId.decToHex(
255,
5
));
System.assertEquals('000fe', MongoObjectId.decToHex(
254,
5
));
System.assertEquals('0012730c57800', MongoObjectId.decToHex(
1267833600000L,
13
));
Test.stopTest();
}
static testMethod void testUserIdToPid() {
Test.startTest();
System.assertEquals('01d7', MongoObjectId.userIdToPid(
'1664BABA'
));
Test.stopTest();
}
static testMethod void testGenerate() {
Test.startTest();
TestStubs.testRandom = 0.5;
TestStubs.testNow = 1267833600000L;
TestStubs.testUserId = '1664BABA';
String id = MongoObjectId.generate();
System.assertEquals(24, id.length());
System.assertEquals('4b919b0005013501d7800000', id);
Test.stopTest();
}
}
public with sharing class TestStubs {
@TestVisible
private static Long testNow {get; set;}
@TestVisible
private static Decimal testRandom {get; set;}
@TestVisible
private static String testUserId {get; set;}
public static DateTime now() {
if(Test.isRunningTest() && (null != testNow)) {
return DateTime.newInstance(testNow);
} else {
return System.now();
}
}
public static Decimal random() {
if(Test.isRunningTest() && (null != testRandom)) {
return testRandom;
} else {
return Math.random();
}
}
public static String getUserId() {
if(Test.isRunningTest() && (null != testUserId)) {
return testUserId;
} else {
return UserInfo.getUserId();
}
}
}
@isTest(seeAllData=false)
private class TestTestStubs {
static testMethod void testRandomStub() {
Test.startTest();
TestStubs.testRandom = 0.5;
System.assertEquals(TestStubs.testRandom, TestStubs.random());
Test.stopTest();
}
static testMethod void testNowStub() {
Test.startTest();
TestStubs.testNow = 1267833600000L;
System.assertEquals(TestStubs.testNow, TestStubs.now().getTime());
Test.stopTest();
}
static testMethod void testUserIdStub() {
Test.startTest();
TestStubs.testUserId = '1664BABA';
System.assertEquals((TestStubs.testUserId), TestStubs.getUserId());
Test.stopTest();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment